Is VPN для ChatGPT safe?
VPN для ChatGPT is high risk. Opening the popup fetches a config file from GitHub Pages, then Blogspot, then Google Docs as fallbacks, ROT-3 shifted and parsed as JSON. A 'u' value replaces the API host; an 's' value replaces the proxy list.
Who publishes itsherechevskiy - 17 other listings from the same operator, 10 of them carrying a finding
sherechevskiy - 17 other listings from the same operator, 10 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
17 other listings published from this account, 265k+ users between them. 10 of them carry a finding.
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.
AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.
Publishers can request a review.
Findings
Backend API and proxy list pulled from GitHub Pages and overwritten at runtime
Opening the popup fetches a config file from GitHub Pages, then Blogspot, then Google Docs as fallbacks, ROT-3 shifted and parsed as JSON.
A 'u' value replaces the API host; an 's' value replaces the proxy list.
What actually happens
You open the extension popup.
The extension fetches a config file from an external GitHub Pages page (Blogspot/Google Docs fallbacks), decodes it, and overwrites its backend API host and proxy list.
The new backend host is written to local storage, so it persists across sessions until changed again by a later fetch.
Captured request
Observed during dynamic analysis when the popup opened: a ~4.6 KB response body, ROT-3 encoded. After decoding, it contained a backend host override and roughly 65 proxy server entries that were not present in the extension's shipped js/conf.js.
Why you can't catch this in DevTools
The fetched config is shifted with a ROT-3 letter cipher before it is published, so the raw response is not directly readable. Shifting each letter back by three positions reveals normal JSON naming a backend host and proxy entries.
{"u":"obratx.shop", "s":[ /* ~65 base64-encoded proxy entries; each decodes to host:port:user:pass|expiry|country|tag, e.g. 168.81.75.126:9091:tFC47N:BDpmJp|2026-07-14|in|rmd */ ]}The code that does this
The fetch-decode-override path, from the shipped source.
What the decode-and-override logic does
// On popup open: fetch the remote config, decode it, apply overrides.function applyRemoteConfig(rawBody) { const json = JSON.parse(rot3Decode(rawBody.trim())); // showx(body, 3) if (json.u && json.u.length > 0) { backendApi = 'https://' + json.u; // overwrite api.hhos.ru chrome.storage.local.set({ apiurl: json.u }); // persist across sessions } if (json.s) { chrome.storage.local.set({ servers: JSON.stringify(json.s) }); // replace proxy list rebuildServerList(json.s); }}Hosts involved in remote config delivery and the resulting backend
- s-extension.github.io
Primary config source. Serves DataExst/data.json (ROT-3 encoded) used to override the backend host and proxy list. Hosted on GitHub Pages.
- dtxtension.blogspot.com
Fallback config source. Config read from a .post-body paragraph on the page. Hosted on Google Blogspot.
- docs.google.com
Fallback config source. Config read from a DOCS_modelChunk script block in a Google Docs document.
- api.hhos.ru
Default bundled backend host, used until a fetched config supplies a 'u' value that replaces it.
Reproduce it yourself
Fetches the extension's remote config from GitHub Pages, applies the ROT-3 decode the extension uses, and prints the backend host override ('u') and proxy entries ('s'). Lets you confirm what the external file currently instructs the extension to do.
- Node.js 18+ (global fetch)
// decode-remote-config.js// Usage: node decode-remote-config.jsconst URL = 'https://s-extension.github.io/DataExst/data.json';function rot3(text) { return text.split('').map(ch => { if (/[a-z]/i.test(ch)) { const code = ch.charCodeAt(0); const base = code >= 65 && code <= 90 ? 65 : 97; return String.fromCharCode(((code - base - 3 + 26) % 26) + base); } return ch; }).join('');}(async () => { const raw = await (await fetch(URL)).text(); const json = JSON.parse(rot3(raw.trim())); if (json.u) console.log('Backend host override (u):', 'https://' + json.u); if (Array.isArray(json.s)) { console.log(`Proxy entries (s): ${json.s.length}`); for (const e of json.s.slice(0, 5)) { console.log(' ', Buffer.from(e, 'base64').toString('utf8')); } }})();- 1Save this file.
- 2Run: node decode-remote-config.js.
- 3Read the printed backend host override and the first few decoded proxy entries. Re-run later to see whether the externally-hosted config has changed.