Is VPN Наружу для YouTube. Обход блокировки YouTube бесплатно safe?
The extension fetches its proxy configuration and PAC script from remote servers with no integrity verification.
On every connection, the extension retrieves a configuration JSON from storage.googleapis.com (with a Yandex Cloud fallback) to obtain its proxy server address and the URL of a PAC script. The PAC script itself is downloaded, decompressed client-side, and applied directly to browser proxy settings without any signature or integrity check, meaning a compromised or substituted config file could redirect all browser traffic. The extension also enumerates all installed browser extensions every 30 minutes and writes a user ID cookie to a domain specified by the same remote config.
AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.
Publishers can request a review.
Findings
VPN proxy routing rules are fetched from a remote server with no integrity check
This VPN/proxy extension routes traffic through proxy servers.
On startup it fetches, with no integrity check, naruzhu.json, a PAC registry, then the PAC script, applied via chrome.proxy.settings.set.
Host/port also come from a remote API.
You install the extension and the background service worker starts.
No interaction beyond loading the extension is required; the configuration fetch fires automatically on service-worker startup.
The extension fetches its proxy routing rules from a remote server and applies them with no integrity check.
A three-stage fetch chain retrieves a PAC (Proxy Auto-Config) script whose URL is supplied by a remote registry, then applies it via chrome.proxy.settings.set to route browser traffic.
- storage.googleapis.com
Hosts naruzhu.json (meta config) and the PAC-script registry uboost-neo/pca-scripts-registry-google.json. Google Cloud Storage bucket controlled by the extension operator.
- storage.yandexcloud.net
Fallback config host (vpnn-web-configs/...). Yandex Cloud Storage bucket used when the Google bucket path is selected as fallback.
- staticfiles.cukubst.top
Hosts the actual PAC script payload (pacs/free-pac-script.json) fetched in our session. Operator-controlled domain.
- extension.nrz.homes
apiBaseUrl returned by naruzhu.json; serves /api/v2/get-proxy, which returns the proxy host and port used to route traffic.
The PAC-script URL comes from a remote registry; the decompressed script becomes the proxy config.
const path = devMeta ? 'naruzhu/naruzhu-test.json?version=3' : 'naruzhu/naruzhu.json';
const metaUrl = await resolveConfigBaseUrl(path); // -> storage.googleapis.com or yandexcloud fallback
if (!metaUrl) throw new Error('No meta API URL provided');
const meta = await http.get(metaUrl.toString()).json(); // { apiBaseUrl, cookieDomain, ... } -- NO integrity checkconst regName = devMeta ? 'uboost-neo/pca-scripts-registry-google-test.json'
: 'uboost-neo/pca-scripts-registry-google.json';
const regUrl = (configBase === 'https://storage.yandexcloud.net/vpnn-web-configs/')
? new URL(devMeta ? 'uboost-neo/pca-scripts-registry-yandex-test.json'
: 'uboost-neo/pca-scripts-registry-yandex.json', configBase)
: new URL(regName, 'https://storage.googleapis.com/');
return await http.get(regUrl.toString()).json(); // registry -> per-tier PAC-script URLsconst pac = buildPac({ host, port, pacString, shouldDecompress, vpnHost, vpnPort });
const proxyConfig = { mode: 'pac_script', pacScript: { data: pac } };
await chrome.proxy.settings.set({ value: proxyConfig, scope: 'regular' });
// No signature check, no Subresource Integrity, no pinned hash on any fetched file.Proxy host and port also come from the remote apiBaseUrl
async function getProxy({ apiBaseUrl, deviceId, deviceIp, onFail }) {
const { featuresList } = await features.getSnapshotAsync();
// apiBaseUrl itself came from the remote naruzhu.json config
return await http.post(`https://${apiBaseUrl}/api/v2/get-proxy`, {
json: { product: 'naruzhu', device_id: deviceId, device_ip: deviceIp ?? 'unknown', on_fail: onFail, with_auth: featuresList[Feature.ProxyAuth] }
}).json(); // returns the proxy host/port that traffic is routed through
}We observed the full remote configuration fetch chain (naruzhu.json -> PAC registry -> PAC script, status 200) fire on service-worker startup before any login. The final `chrome.proxy.settings.set` application step is gated behind an email one-time-password login wall that we did not pass in this session, so we did not observe traffic being actively re-routed. The remote-controlled, integrity-unchecked source of the routing rules — the substance of this finding — was fully demonstrated.
Remote config picks which domain the extension writes account cookies to
When userId or deviceId changes in local storage, the extension writes them as cookies to a domain read from remote config naruzhu.json.
Our test returned cuknrz.top; the extension set userId (login email) and deviceId cookies there.
Your userId or deviceId changes in the extension's local storage.
A chrome.storage.onChanged listener watches for these identifier values changing.
The extension writes the identifier as a cookie to a domain it reads from a remote config file.
The destination is the cookieDomain field returned by storage.googleapis.com/naruzhu/naruzhu.json, not a hardcoded value in the extension.
| Field | Value | Why it matters | |
|---|---|---|---|
Account identifier (userId) | userId = <redacted>@example.com | The userId cookie carried the account identifier; in our session this was the email address entered at the login screen. | |
Device identifier (deviceId) | deviceId = 5f3c2a91-8b04-4e77-9c1a-2d6e0f4ab123 | A per-install device identifier, read and written to the same remote-config-supplied domain. | |
Destination domain (cookieDomain) | cuknrz.top | The domain cookies are written to, read from the remote naruzhu.json config, so a server change redirects where these identifiers are set. |
Cookie write/read target their URL at the remote-config cookieDomain
// cookieDomain is destructured from the remote meta config (naruzhu.json)
async function setIdentifierCookies(entries) {
const { cookieDomain } = await getMetaConfig(); // <- remote-supplied
for (const [name, value] of Object.entries(entries)) {
await chrome.cookies.set({ url: `https://${cookieDomain}`, name, value, ...opts });
}
}
async function getIdentifierCookie(name) {
const { cookieDomain } = await getMetaConfig(); // <- remote-supplied
const c = await chrome.cookies.get({ url: `https://${cookieDomain}`, name });
return c ? { [name]: c.value } : { [name]: null };
}storage.onChanged listener triggers the cookie write when the identifier changes
chrome.storage.onChanged.addListener(changes => run(async () => {
// identifier changes in storage.local are mirrored out to the cookieDomain
// by setIdentifierCookies({ userId } / { deviceId }) on change.
...
}));