Is INSTAVPN - Инстаграм без блокировки safe?

Medium risk

INSTAVPN fetches remote configuration that controls its proxy servers and checkout API domain.

INSTAVPN requests a remote configuration file that can replace the proxy server list stored by the extension and change the checkout API host it contacts. When a user enables the VPN, the extension applies the selected proxy through Chrome proxy settings and supplies embedded proxy credentials for authentication challenges.

sherechevskiyv4.1Chrome Web Store
45Risk
Who publishes it

sherechevskiy - 15 other listings from the same operator, 6 of them carrying a finding

What this publisher told the store about itself, and the other listings that told it the same thing.

Publisher
sherechevskiy

Shared hosts - 3 hostnames

Hostnames hardcoded in this extension that few other listings call. That can mean one operator behind both, and it can equally mean a small shared vendor, so it is context rather than a conclusion. Hosts that many listings call are left out: they are services, not connections.

api.hhos.ru
Also called by 3 other listings, including ВПН для ЧатГПТ / ChatGPT, ВПН для Gemini / VPN for Gemini
dtxtension.blogspot.com
Also called by 3 other listings, including ВПН для ЧатГПТ / ChatGPT, ВПН для Gemini / VPN for Gemini
s-extension.github.io
Also called by 3 other listings, including ВПН для ЧатГПТ / ChatGPT, ВПН для Gemini / VPN for Gemini

AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.

Publishers can request a review.

Findings

Unsigned remote config swaps INSTAVPN's proxy servers and payment domain

Opening the INSTAVPN popup fetches an unsigned config file that supplies the proxy servers you connect through and can replace the hardcoded payment domain.

Nothing verifies where the file came from.

Severity
Medium unwanted
Type
Unexpected
CWE
CWE-829
Source
Dynamic sandbox
What actually happens
You did this

You open the INSTAVPN popup to connect to a proxy.

This happens every time the popup loads, before you click anything.

The extension did this

The extension fetches an unsigned remote file and lets it set the proxy list and payment domain it uses.

Nothing checks that the file came from the vendor before the extension trusts its contents.

Captured request
GEThttps://s-extension.github.io/DataExst/data.json

200 OK, roughly 4.6 KB body, no signature or integrity header. Decoding the body (see below) revealed a payment domain override and 65 proxy records.

Headers
Cache-Control
no-cache
Why you can't catch this in DevTools

The response is not plain JSON, every letter is shifted 3 places before the extension reverses the shift and parses it. Reproduces that shift on the confirmed payment domain and proxy record (illustrative, password redacted).

Decoded value
{  "u": "marketpays.shop",  "s": [    "MTk2LjE2LjUuMTQxOjkxNDg6NG94ZU4yOjxyZWRhY3RlZD58MjAyNi0xMC0yN3xnYnxybWQ="  ]}
The code that does this

The decode-and-trust path: no verification between fetch and use

Readable version

What each name means

js/conf.js (annotated)
// Fallback checkout domain used until the remote config overrides itvar defaultCheckoutDomain = "https://api.hhos.ru";// Three places the remote config can come from, tried in ordervar remoteConfigUrls = {  githubPages: "https://s-extension.github.io/DataExst/data.json",  blogspotFallback: "https://dtxtension.blogspot.com/p/kronshtat.html",  googleDocFallback: "https://docs.google.com/document/d/1X5wD-cqXbTdEMNu5CIRSWy20G7u7LwKVdoGu8ikzCj0/edit"};

Fetch the first config URL that responds

js/bg.js (annotated)
async function fetchRemoteConfig() {  const urls = [remoteConfigUrls.githubPages, remoteConfigUrls.blogspotFallback, remoteConfigUrls.googleDocFallback];  for (const url of urls) {    try {      return await fetchWithTimeout(url); // no signature check on the response    } catch (error) {      console.warn("config fetch failed", error.message);    }  }  throw new Error("No data");}

Reverse the Caesar shift, then trust the JSON unconditionally

popup/main.js (annotated)
function caesarDecode(text, shift) {  return text.split('').map((char) => {    if (/[a-z]/i.test(char)) {      const code = char.charCodeAt(0);      const offset = code >= 65 && code <= 90 ? 65 : 97;      return String.fromCharCode(((code - offset - shift + 26) % 26) + offset);    }    return char;  }).join('');}function applyRemoteConfig(rawResponse) {  const decoded = caesarDecode(rawResponse.trim(), 3);  const config = JSON.parse(decoded); // no schema or signature check  if (config.u) {    checkoutDomain = "https://" + config.u; // silently replaces the payment domain    chrome.storage.local.set({ apiurl: config.u });  }  if (config.s) {    chrome.storage.local.set({ servers: JSON.stringify(config.s) }); // proxy list  }}
One decoded proxy record from the remote config
  • Proxy address
    196.16.5.141:9148

    The host and port the extension will route your traffic through if you pick this server.

  • Proxy login
    4oxeN2

    Username the extension sends when Chrome's proxy authentication dialog appears.

  • Proxy password
    <redacted>

    Password sent alongside the login. Shown redacted here; shipped in plaintext inside the base64 record.

  • Expiry date
    2026-10-27

    Date after which the vendor expects this proxy record to stop working.

  • Country code
    gb

    Country the proxy exit node is presented as being located in.

Where the config comes from and where it can send you
    • s-extension.github.io

    Primary source of the unsigned proxy/payment config, fetched on every popup open.

    • dtxtension.blogspot.com

    Fallback config source read from a Blogger post body if GitHub Pages fails.

    • docs.google.com

    Second fallback config source, parsed out of a public Google Doc's internal JSON.

    • marketpays.shop

    Payment API domain the remote config can substitute for the hardcoded default.

    • api.hhos.ru

    Hardcoded default payment API domain, used until the remote config overrides it.

Reproduce it yourself

Reverses the extension's own Caesar shift on a saved config response and lists the payment domain override and each decoded proxy record.

Requires
  • Node.js 18+
decode-instavpn-config.js · js
const fs = require('fs');function caesarDecode(text, shift) {  return text.split('').map((char) => {    if (/[a-z]/i.test(char)) {      const code = char.charCodeAt(0);      const offset = code >= 65 && code <= 90 ? 65 : 97;      return String.fromCharCode(((code - offset - shift + 26) % 26) + offset);    }    return char;  }).join('');}const rawPath = process.argv[2];if (!rawPath) {  console.error('Usage: node decode-instavpn-config.js <path-to-saved-response-body>');  process.exit(1);}const raw = fs.readFileSync(rawPath, 'utf8').trim();const decoded = caesarDecode(raw, 3);const config = JSON.parse(decoded);console.log('Payment domain override:', config.u || '(none present)');console.log('Proxy record count:', (config.s || []).length);(config.s || []).forEach((entry, i) => {  const record = Buffer.from(entry, 'base64').toString('utf8');  const [creds, expiry, country, type] = record.split('|');  const [host, port, login] = creds.split(':');  console.log(`#${i + 1}`, host + ':' + port, 'login=' + login, 'pass=<redacted>', expiry, country, type);});
How to run it
  1. 1Save the raw body of a GET to the config URL as response.txt.
  2. 2Run: node decode-instavpn-config.js response.txt.
  3. 3Read the printed payment domain and proxy records.

Where it sends data

Destinations our analysis observed INSTAVPN - Инстаграм без блокировки contacting. Sending data somewhere is not a finding on its own - an extension that syncs your settings has to talk to its own server - but it is where your data can go, and who else it goes to.

  • docs.google.com

    INSTAVPN - Инстаграм без блокировки sends data to docs.google.com. 20 other extensions we have analysed send data here.

  • s-extension.github.io

    INSTAVPN - Инстаграм без блокировки sends data to s-extension.github.io. 7 other extensions we have analysed send data here.

  • dtxtension.blogspot.com

    INSTAVPN - Инстаграм без блокировки sends data to dtxtension.blogspot.com. 6 other extensions we have analysed send data here.

  • api.hhos.ru

    INSTAVPN - Инстаграм без блокировки sends data to api.hhos.ru. One other extension we have analysed sends data here.

Updated 30 September 2026cfhifhoonlggibeojpjdfanmlnphmdah