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.2Chrome Web Store
45Risk

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

Remote Proxy Config Controls Browser Routing

Enabling the proxy feature requests config from a remote endpoint.

The code accepts a host, port, credentials, and domain list, then installs matching rules.

The toggle GET was observed, but the endpoint was unreachable; no config captured.

01EvidenceCAUSE EFFECT
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.

02EvidenceNETWORK CAPTURE
Captured request
GEThttps://pphakoegbpaabllnkbjadgpjckkbgdkp.dark-proxy.ru/pphakoegbpaabllnkbjadgpjckkbgdkp.php
Status 0 / net::ERR_FAILED observed during dynamic analysis; no JSON config body was captured.
03EvidenceFIELD TABLE
Remote config fields accepted by the code
FieldValueWhy it matters
Proxy server
cfg.host plus cfg.port; no live value captured because the endpoint returned net::ERR_FAILEDDecides which server receives browser traffic for sites covered by the rules.
Proxy credentials
cfg.login plus cfg.pass, or user:pass embedded in cfg.serversLets the extension answer proxy login prompts for the supplied server.
Routed domains
cfg.domains entries matched as exact domains, subdomains, or substringsControls which sites are sent through the supplied proxy instead of going directly from your browser.
Visited site through proxy
https://rutor.info/favicon.icoFor covered domains, the proxy server can see the requested site and timing of visits.
04EvidenceCODE COMPARE
The code that does this

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

What it actually does
Popup toggle sends the enableProxy messagepopup.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 domainsbackground.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 proxybackground.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 credentialsbackground.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");
  }
}
05EvidenceTHIRD PARTY LIST
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.

SeverityMEDIUM
ClassUNWANTED
TypeUnexpected
CWECWE-506
SourceAI SANDBOX

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

This proxy tool reaches rutor.info from Russia.

Without a paid token, clicking the popup opens dark-proxy.ru advertising paid tokens, confirmed by testing.

It fires at most once every few minutes, and never while a valid token is active.

01EvidenceCAUSE EFFECT
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.

02EvidenceCODE COMPARE
The code that does this

The click handler that opens the ad window

What it actually does
// 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();
    }
  };
}
03EvidenceTEMPORAL PATTERN
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.

04EvidenceNETWORK CAPTURE
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
Refererhttps://dark-proxy.ru/new/google/rutor/cl.html
SeverityMEDIUM
ClassUNWANTED
TypeUnexpected
CWECWE-200
SourceAI SANDBOX

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

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

Re-sent each popup open as a blacklist check.

A planted marker value appeared verbatim in the captured POST.

01EvidenceCAUSE EFFECT
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.

02EvidenceNETWORK CAPTURE
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-Typeapplication/json
Body
{
  "code": "<redacted>",
  "checkBlacklist": false
}
03EvidenceFIELD TABLE
What the POST body carries
FieldValueWhy it matters
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":falseTrue when the token is re-sent on popup open to check whether it has been revoked; false on the initial activation.
04EvidenceCODE COMPARE
The code that does this

The token validation request

What it actually does
// 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 open
function 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);
}
05EvidenceTHIRD PARTY LIST
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.

Data recipients

pphakoegbpaabllnkbjadgpjckkbgdkp.dark-proxy.rudark-proxy.rutoken.ruchatgpt.site
Updated 17 September 2026pphakoegbpaabllnkbjadgpjckkbgdkp