Is Total VPN - один ВПН для всего safe?

High risk

Total VPN - один ВПН для всего is high risk. On popup open, the worker fetches config from GitHub Pages, Blogspot, or Google Docs, ROT-3 decoded. A 'u' value overwrites the payment API domain; 's' is a base64 proxy list. The operator can edit these pages anytime; confirmed live.

katia.noomovav2.0Chrome Web Store
75Risk

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

Publishers can request a review.

Findings

SeverityHIGH
ClassUNWANTED
TypeUnexpected
CWECWE-829
SourceAI SANDBOX

Payment domain and proxy list swapped via dead-drop config pages

On popup open, the worker fetches config from GitHub Pages, Blogspot, or Google Docs, ROT-3 decoded.

A 'u' value overwrites the payment API domain; 's' is a base64 proxy list.

The operator can edit these pages anytime; confirmed live.

01EvidenceCAUSE EFFECT
What actually happens
You did this

You open the VPN extension's popup.

The extension did this

The extension fetches a configuration file from a public page it controls and uses it to set which domain receives your payment and account requests.

The same response also supplies the list of proxy servers your browsing traffic is routed through.

02EvidenceNETWORK CAPTURE
Captured request
GEThttps://katnmv.github.io/exs/list.json
Returned a ROT-3-obfuscated body (about 1.9 KB) observed during dynamic analysis on popup open. If this host fails, the extension retries https://twexst.blogspot.com/p/data2.html and then https://docs.google.com/document/d/1SFsBZ6Nsz1WH86YIKZyuT_KSzE_Ns36MS_78WOblpU0/edit.
Headers
Cache-Controlno-cache
03EvidenceOPAQUE REVEAL
Why you can't catch this in DevTools

The fetched body is encoded with a 3-position Caesar letter shift so it is not readable as JSON on the wire. Decoding it reveals the replacement payment domain ('u') and a base64-encoded server list ('s').

What's actually being sent
{
  "u": "mainapi.store",
  "s": [
    "196.16.2.115:9037:RD4xCy:9XFUs|2026-07-14|gb|rmd"
  ]
}
04EvidenceCODE COMPARE
The code that does this

Remote config replaces the payment API domain and extends the proxy server list

What it actually does
popup/main.js
var c=showx(data.trim(),3);          // ROT-3 decode
var j=JSON.parse(c);
if(typeof(j['u'])!='undefined'){
  if(j['u'].length>0){
    ucheckout="https://"+j['u'];        // replace payment/account API domain
    chrome.storage.local.set({"apiurl":j['u']})
  }
}
if(typeof(j['s'])!='undefined'){
  chrome.storage.local.set({"servers":JSON.stringify(j['s'])});
  j['s'].forEach(function(e,i){
    let k=atob(e).split("|");           // host:port:user:pass | expiry | country | label
    a1.push(k[0]);
  });
  serverConfigs.unshift(...a1);         // prepend remote servers
}
js/checkout.js / popup/main.js
// The replaced domain becomes the base for every payment/account call:
$.post(ucheckout+"/api/v"+acheckout+"/check_ssid",{id:id,...});
$.post(ucheckout+"/api/v"+acheckout+"/getvpn",{id:id,...});
$.post(ucheckout+"/api/v"+acheckout+"/setcode",{id:id,t:tcheckout,c:c});
05EvidenceTHIRD PARTY LIST
Public pages used to deliver the configuration
  • katnmv.github.io

    Primary dead-drop: serves exs/list.json, the ROT-3-encoded config carrying the payment domain and server list. GitHub Pages, editable by whoever controls the repository.

  • twexst.blogspot.com

    First fallback dead-drop: the page /p/data2.html carries the same config. Blogspot, editable by the post owner.

  • docs.google.com

    Second fallback dead-drop: a Google Docs document (id 1SFsBZ6Nsz1WH86YIKZyuT_KSzE_Ns36MS_78WOblpU0) carries the same config. Editable by the document owner.

  • mainapi.store

    Default payment/account API domain shipped in conf.js; replaceable at runtime by the 'u' field from any of the dead-drop pages.

06EvidenceARTIFACT
Reproduce it yourself

Fetches the GitHub Pages config, applies the ROT-3 decode the extension uses, parses the JSON, and prints the replacement payment domain plus each decoded proxy server entry.

RequiresNode.js 18+
decode-config.js · js
// Reproduces the extension's config decode. Node 18+ (global fetch).
const URLS = [
  'https://katnmv.github.io/exs/list.json',
  'https://twexst.blogspot.com/p/data2.html',
  'https://docs.google.com/document/d/1SFsBZ6Nsz1WH86YIKZyuT_KSzE_Ns36MS_78WOblpU0/edit'
];

function rot(text, shift) {            // matches showx(text,3)
  return text.split('').map((c) => {
    if (/[a-z]/i.test(c)) {
      const code = c.charCodeAt(0);
      const base = code >= 65 && code <= 90 ? 65 : 97;
      return String.fromCharCode(((code - base - shift + 26) % 26) + base);
    }
    return c;
  }).join('');
}

(async () => {
  let body;
  for (const u of URLS) {
    try { const r = await fetch(u, { headers: { 'Cache-Control': 'no-cache' } });
          if (r.ok) { body = await r.text(); break; } } catch {}
  }
  if (!body) { console.error('no config fetched'); return; }
  const j = JSON.parse(rot(body.trim(), 3));
  if (j.u) console.log('payment API domain ->', j.u);
  (j.s || []).forEach((e) => {
    const [hostPort, expiry, country, label] = Buffer.from(e, 'base64').toString().split('|');
    console.log('proxy server ->', hostPort, '| expiry', expiry, '| country', country, '| label', label);
  });
})();
How to run it
  1. 1
    Save as decode-config.js.
  2. 2
    Run `node decode-config.js`.
  3. 3
    Read the printed payment API domain and the decoded proxy server entries.
Updated 17 September 2026nkhfdlilombfkfbnbdfephaaaifaollp