Is Rutor обход блокировки safe?

Medium risk

Rutor обход блокировки routes browser traffic through a remotely configured proxy and injects clickunder ads on every mouse click.

The extension fetches its proxy configuration—including host, port, and credentials—from an operator-controlled server at dark-proxy.ru on each startup, then applies those settings to redirect user traffic through the specified proxy for configured domains. When no paid promo token is active, it opens an advertisement page at dark-proxy.ru on every mouse click inside the popup. User-entered promo tokens are transmitted to a separate third-party service, token.ruchatgpt.site, for validation on every popup open.

open.rutracker.orgv7.2.7Chrome Web Store
51Risk
Who publishes it

open.rutracker.org - 1 other listing from the same operator, none carrying a finding

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

Publisher
open.rutracker.org

Same store account

1 other listing published from this account, 1k+ users between them, none of them carrying a finding.

Shared hosts - 2 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.

cat.dark-proxy.ru
Also called by 1 other listing: Rutor
rutor.info
Also called by 1 other listing: rutor_torrent_search

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

Publishers can request a review.

Findings

Remote Proxy Config Controls Browser Routing

HighunwantedCWE-829Dynamic sandbox

Enabling the proxy feature requests config from a remote endpoint.

What actually happens

You did this

You turn on the proxy feature in the popup.

The popup stores the enabled state and sends an enable message to the background worker.

The extension did this

The extension requests proxy rules from a remote host and applies them to matching browsing traffic.

If the response contains a proxy server and domains, the background worker installs a PAC script and proxy authentication handler.

Captured request

GEThttps://pphakoegbpaabllnkbjadgpjckkbgdkp.dark-proxy.ru/pphakoegbpaabllnkbjadgpjckkbgdkp.php

Status 0 / net::ERR_FAILED observed during dynamic analysis; no JSON config body was captured.

Remote config fields accepted by the code

  • Proxy server
    cfg.host plus cfg.port; no live value captured because the endpoint returned net::ERR_FAILED

    Decides which server receives browser traffic for sites covered by the rules.

  • Proxy credentials
    cfg.login plus cfg.pass, or user:pass embedded in cfg.servers

    Lets the extension answer proxy login prompts for the supplied server.

  • Routed domains
    cfg.domains entries matched as exact domains, subdomains, or substrings

    Controls which sites are sent through the supplied proxy instead of going directly from your browser.

  • Visited site through proxy
    https://rutor.info/favicon.ico

    For covered domains, the proxy server can see the requested site and timing of visits.

The code that does this

The popup trigger, remote config fetch, PAC builder, and proxy authentication path

Readable version

Popup toggle sends the enableProxy message

popup.js
proxyToggle.addEventListener('change', () => {  chrome.storage.local.get(['promoValidUntil'], (data) => {    const now = Date.now();    const promoValid = data.promoValidUntil && data.promoValidUntil > now;    if (proxyToggle.checked) {      // --- ВИЗУАЛЬНЫЙ ПИНОК (ОТОБРАЖАЕМ FAVICON) ---      const container = document.getElementById('pokeContainer');      if (container) {        container.innerHTML = `Пытаюсь загрузить: <br> <img src="https://rutor.info/favicon.ico?t=${Date.now()}" style="width:16px; height:16px; vertical-align:middle;">`;      }      // --------------------------------------------      chrome.storage.local.set({ proxyEnabled: true }, () => {        chrome.runtime.sendMessage({ action: 'enableProxy', skipTimer: promoValid });        status.textContent = '';        updateTimerVisibility(promoValid);        setupClickunder(promoValid);        if (!promoValid) {          const disableAt = Date.now() + 3 * 60 * 1000;          chrome.storage.local.set({ proxyDisableTime: disableAt });          startTimer(180);        } else {          stopTimer();        }        syncUI();      });    } else {      // Очистка контейнера при выключении      const container = document.getElementById('pokeContainer');      if (container) container.innerHTML = "Авторизация: выключено";      chrome.storage.local.set({ proxyEnabled: false }, () => {        chrome.runtime.sendMessage({ action: 'disableProxy' });        status.textContent = '';        stopTimer();        updateTimerVisibility(promoValid);        setupClickunder(promoValid);        syncUI();      });    }  });});

Remote config fetch accepts host, port, credentials, servers, and domains

background.js
async function loadConfig(force = false) {  if (proxyManuallyDisabled && !force) return;  const { activatedPromoCode } = await chrome.storage.local.get("activatedPromoCode");  const token = activatedPromoCode ? activatedPromoCode.trim() : "";  const url = token    ? `${PROXY_BASE_URL}pphakoegbpaabllnkbjadgpjckkbgdkp2.php?token=${encodeURIComponent(token)}`    : `${PROXY_BASE_URL}pphakoegbpaabllnkbjadgpjckkbgdkp.php`;  try {    const r = await fetch(url, { cache: "no-store" });    const cfg = await r.json();    let parsed = null;    if (cfg.host && cfg.port) {      parsed = { host: String(cfg.host), port: String(cfg.port), login: cfg.login || null, pass: cfg.pass || null };    } else if (cfg.servers && typeof cfg.servers === "object") {      const key = Object.keys(cfg.servers)[0];      parsed = parseProxyString(cfg.servers[key]);    }    if (!parsed) throw new Error("Cannot parse proxy");    const newHostPort = `${parsed.host}:${parsed.port}`;    if (!force && lastAppliedHost === newHostPort) return;    proxyData = { ...parsed, rawPacString: parsed.rawPacString || null };    domains = Array.isArray(cfg.domains) ? cfg.domains.slice() : [];    lastAppliedHost = newHostPort;    applyPacAndAuth();    enableXProxyAccessHeader();  } catch (e) {    log("[Config] Error: " + e.message);  }}

PAC builder sends matching domains through the supplied proxy

background.js
function buildPacFromDomains(proxyHostPort) {  if (!proxyHostPort || domains.length === 0) return "DIRECT";  let rules = "";  for (const d of domains) {    const dom = (d || "").trim();    if (!dom) continue;    if (dom.includes(".")) {      rules += `if (host === "${dom}" || host.endsWith(".${dom}")) return "PROXY ${proxyHostPort}";\n`;    } else {      rules += `if (host.includes("${dom}")) return "PROXY ${proxyHostPort}";\n`;    }  }  return `function FindProxyForURL(url, host) {\n${rules}return "DIRECT";\n}`;}

PAC installation and proxy credentials

background.js
function applyPacAndAuth() {  if (proxyManuallyDisabled) {    log("[Proxy] applyPacAndAuth blocked — manually disabled");    chrome.proxy.settings.clear({ scope: "regular" });    return;  }  if (!proxyData.host || !proxyData.port) {    log("[Proxy] No proxy data → nothing to apply");    return;  }  const hostPort = `${proxyData.host}:${proxyData.port}`;  const pac = buildPacFromDomains(hostPort);  chrome.proxy.settings.set(    { value: { mode: "pac_script", pacScript: { data: pac } }, scope: "regular" },    () => {       if (chrome.runtime.lastError) {         log("[Proxy] Error: " + chrome.runtime.lastError.message);       } else {         log("[Proxy] PAC applied → " + hostPort);         forceAuthorize(); // ВЫЗОВ ПРОГРЕВА       }    }  );  if (currentAuthHandler) {    try { chrome.webRequest.onAuthRequired.removeListener(currentAuthHandler); } catch(e) {}    currentAuthHandler = null;  }  if (proxyData.login && proxyData.pass) {    currentAuthHandler = (details) => {      if (details.isProxy) {        return {           authCredentials: {             username: proxyData.login,             password: proxyData.pass           }         };      }      return {};    };    chrome.webRequest.onAuthRequired.addListener(currentAuthHandler, { urls: ["<all_urls>"] }, ["blocking"]);    log("[Proxy] Auth handler added");  }}

Hosts involved in the proxy-control flow

    • pphakoegbpaabllnkbjadgpjckkbgdkp.dark-proxy.ru

    Receives the proxy configuration GET request; the code can also request a tokenized config endpoint on this host.

    • cat.dark-proxy.ru

    The background worker adds an X-Proxy-Access header rule for main-frame requests to this host after proxy data is loaded.

    • rutor.info

    The background worker fetches the favicon after applying PAC settings to warm proxy authorization.

Clicking inside the popup opens a dark-proxy.ru ad window

MediumunwantedCWE-506Dynamic sandbox

This proxy tool reaches rutor.info from Russia.

What actually happens

You did this

You click anywhere inside the extension popup while no paid token is active.

The popup installs a document.onmouseup handler whenever a valid token is not present.

The extension did this

The popup opens a new browser window to a dark-proxy.ru page advertising paid access tokens.

window.open() targets dark-proxy.ru/new/google/rutor/cl.html with full browser chrome.

The code that does this

The click handler that opens the ad window

Readable version
// Arms a click-under on the popup whenever no paid token is active.// First qualifying click only sets a 1-minute 'clickunder-delay' cookie.// Next click after the delay (and while no 'clickunder' cookie exists)// opens a new browser window to the dark-proxy.ru buy-token page and// sets a 'clickunder' cookie that suppresses repeats for 2 minutes.function setupClickunder(promoValid) {  document.onmouseup = null;  if (promoValid) return;                 // suppressed while a paid token is active  document.onmouseup = function () {    if (!getCookie('clickunder-delay')) {  // first click: arm 1-min delay      setCookie('clickunder-delay', '1', plus1Minute, '/');      return;    }    if (!getCookie('clickunder')) {        // later click: open ad window      setCookie('clickunder', '1', plus2Minutes, '/');      window.open('https://dark-proxy.ru/new/google/rutor/cl.html', 'dark-proxy',                  'menubar=yes,location=yes,resizable=yes,scrollbars=yes,status=yes');      window.focus();    }  };}

When this fires

When a batch threshold is hit

The window does not open on the first click. The first qualifying click only sets a 1-minute 'clickunder-delay' cookie; a later click (after that delay, while no 'clickunder' cookie is set) opens the window and sets a 'clickunder' cookie that suppresses repeats for 2 minutes. Net effect: the ad window opens roughly once every couple of minutes of active clicking, not on every click.

Captured request

GEThttps://counter.yadro.ru/hit

Analytics beacon fired by the newly-opened dark-proxy.ru ad window during dynamic analysis. The window itself loaded a page titled 'Рутор Лена' headed 'Получи токен для безлимитного доступа' with buy-token call-to-action buttons, and also requested dark-proxy.ru/favicon.ico. This request was observed during dynamic analysis after a single click inside the popup with an invalid token.

Headers
Referer
https://dark-proxy.ru/new/google/rutor/cl.html

Promo token sent to token.ruchatgpt.site, unrelated to the vendor

MediumunwantedCWE-200Dynamic sandbox

Pasting an access token and clicking Activate POSTs it to token.ruchatgpt.site/propromo.php, distinct from vendor dark-proxy.ru.

What actually happens

You did this

You enter an access token in the popup and click Activate.

The token also re-sends automatically each time the popup opens while a token is stored.

The extension did this

The popup POSTs the token to token.ruchatgpt.site, a domain unrelated to the stated vendor.

The request is a JSON POST to token.ruchatgpt.site/propromo.php carrying the token in a 'code' field.

Captured request

POSThttps://token.ruchatgpt.site/propromo.php

Captured during dynamic analysis after typing a planted marker value into the token field and clicking Активировать. The marker appeared verbatim in the request body (shown redacted here), confirming the entered token is transmitted off the device. The destination token.ruchatgpt.site is a separate domain from the extension vendor dark-proxy.ru.

Headers
Content-Type
application/json
Body
{  "code": "<redacted>",  "checkBlacklist": false}

What the POST body carries

  • Your access token
    {"code":"<redacted>"}

    The token you typed into the popup to unlock the proxy. Sent in full to a domain that is not the extension's stated vendor.

  • Blacklist-check flag
    "checkBlacklist":false

    True when the token is re-sent on popup open to check whether it has been revoked; false on the initial activation.

The code that does this

The token validation request

Readable version
// POSTs the user-entered token to token.ruchatgpt.site.// Called two ways://  - promoButton click -> validateToken(code, false, ...)  (initial activation)//  - checkBlacklist()   -> validateToken(storedCode, true, ...) on every popup openfunction validateToken(code, checkBlacklist, callback) {  fetch('https://token.ruchatgpt.site/propromo.php', {    method: 'POST',    headers: { 'Content-Type': 'application/json' },    body: JSON.stringify({ code, checkBlacklist })  })    .then(res => res.json())    .then(data => callback(null, data))    .catch(callback);}

Where the token is sent

    • token.ruchatgpt.site

    Receives the user's access token via POST /propromo.php for validation and blacklist checks. A separate domain from the stated vendor dark-proxy.ru.

What it can do

Permissions this extension asks for, as declared in version 7.2.2. Asking for a permission is not a finding on its own - it is what the extension can do if it chooses to. The current listed version is 7.2.7, which we have not unpacked yet.

  • Read and change your data on rutor.info

    *://rutor.info/*

  • Read and change your data on cat.dark-proxy.ru

    *://cat.dark-proxy.ru/*

  • Route all of your browsing through a server of its choosing

    proxy

  • Store data in your browser

    storage

  • Block and redirect the requests your browser makes

    declarativeNetRequest

  • Show you desktop notifications

    notifications

  • Watch every request your browser makes

    webRequest

webRequestAuthProvider

Where it sends data

Destinations our analysis observed Rutor обход блокировки 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.

  • pphakoegbpaabllnkbjadgpjckkbgdkp.dark-proxy.ru

    Rutor обход блокировки sends data to pphakoegbpaabllnkbjadgpjckkbgdkp.dark-proxy.ru. One other extension we have analyzed sends data here.

  • dark-proxy.ru

    Rutor обход блокировки sends data to dark-proxy.ru. No other extension we have analyzed sends data here.

  • token.ruchatgpt.site

    Rutor обход блокировки sends data to token.ruchatgpt.site. No other extension we have analyzed sends data here.

Updated 30 September 2026pphakoegbpaabllnkbjadgpjckkbgdkp