Недавно я наткнулся на довольно интересный случай. На первый взгляд — обычное VPN-расширение для браузера: список стран, кнопка подключения, бесплатный сервер, платные серверы и подписка. Но мне стало интересно, что именно происходит после нажатия кнопки Connect. Я решил не ограничиваться интерфейсом и полез непосредственно в код расширения. И вот там уже стало интереснее.

Речь идёт о расширении TOP VPN.

Сразу скажу: я не буду утверждать, что это вредоносное расширение только на основании найденного worker.js. Но с точки зрения заявленной функции VPN здесь есть моменты, которые заставляют как минимум задуматься о том, что именно пользователь устанавливает себе в браузер.

Начинаем с worker.js

Основная логика находится в Service Worker:

/* Top VPN — Service Worker */

let relayActive = false;
let chosenNode = null;

Дальше разработчики просто оставили список серверов непосредственно в JavaScript:

const SERVERS = [
  {
    "id": "us-1",
    "name": "США 1",
    "countryCode": "US",
    "host": "103.35.189.225",
    "port": 1082,
    "premium": false
  },
  {
    "id": "us-2",
    "name": "США 2",
    "countryCode": "US",
    "host": "103.35.191.173",
    "port": 1082,
    "premium": true
  }
];

И таких записей там достаточно много.

Например:

103.35.189.225:1082
103.35.191.173:1082
178.130.47.129:1082

80.92.204.33:1082
80.92.204.47:1082

5.180.30.122:1082
5.180.30.15:1082

86.104.74.110:1082
94.131.118.237:1082

Кроме IP-адресов встречаются и домены:

jp.neoncloak.space:1082
sg.neoncloak.space:1082
ca.neoncloak.space:1082
au.neoncloak.space:1082
tr.neoncloak.space:1082

То есть уже на этом этапе можно понять, что расширение работает с заранее определённой инфраструктурой. Но самое интересное находится чуть ниже.

Что происходит при подключении

Я дошёл до функции:

function engageTunnel(nodeId) {
    const node = SERVERS.find(function(s) {
        return s.id === nodeId;
    });

    if (!node) return false;

Она получает идентификатор выбранной страны, находит соответствующий сервер и формирует конфигурацию proxy. Вот ключевой участок:

const config = {
    mode: "fixed_servers",
    rules: {
        singleProxy: {
            scheme: "socks5",
            host: node.host,
            port: node.port
        },
        bypassList: BYPASS_LIST
    }
};

Здесь для меня и начинается самое интересное. Расширение не устанавливает какой-то собственный VPN-протокол. Оно говорит браузеру:

используй SOCKS5
хост = указанный сервер
порт = 1082

После чего применяет эту конфигурацию:

chrome.proxy.settings.set(
    { value: config, scope: "regular" },
    function() {
        ...
    }
);

Получается примерно такая схема:

┌──────────────┐
│    Chrome    │
└──────┬───────┘
       │
       │ SOCKS5
       ▼
┌─────────────────────┐
│ 103.35.189.225:1082 │
└──────────┬──────────┘
           │
           ▼
        Internet

И вот тут возникает вполне логичный вопрос:

а где здесь шифрование VPN?

SOCKS5 — это ещё не VPN

Я специально остановился на этом моменте, потому что здесь очень легко запутаться. Наличие socks5 в конфигурации не означает, что между пользователем и сервером автоматически создаётся защищённый VPN-туннель. SOCKS5 — это протокол прокси. Его задача — принять соединение клиента и передать его дальше. То есть:

Клиент
   ↓
SOCKS5 proxy
   ↓
Сайт

Это не то же самое, что:

Клиент
   ↓
зашифрованный VPN-туннель
   ↓
VPN-сервер
   ↓
Сайт

Именно поэтому я бы не стал называть найденную реализацию полноценным VPN только потому, что расширение называется TOP VPN.

Но HTTPS ведь всё равно шифруется?

Да. И здесь важно не перегнуть палку. Если я открываю:

https://example.com

то содержимое HTTPS-соединения защищается TLS между браузером и сервером сайта. SOCKS5 сам по себе не расшифровывает HTTPS. Поэтому утверждение:

«SOCKS5 полностью снимает шифрование с HTTPS»

было бы неправильным. Но это не означает, что оператор proxy ничего не видит. Proxy находится посередине сетевого соединения и видит сетевую метаинформацию, необходимую для работы соединения. А если приложение использует незашифрованный протокол, ситуация уже совсем другая.

Например:

HTTP
↓
SOCKS5
↓
Internet

В таком случае данные могут проходить через proxy без защиты на уровне самого приложения. Поэтому здесь важно разделять шифрование HTTPS и защищённый VPN-туннель. Это совершенно разные вещи.

А кому тогда мы доверяем?

Вот это, пожалуй, главный вопрос, который я бы задавал при анализе любого неизвестного VPN.

Когда я использую обычное интернет-соединение:

Я → провайдер → Internet

а затем включаю такой proxy:

Я → SOCKS5-сервер → Internet

точка доверия меняется. Теперь часть сетевой информации проходит через сервер, который указан непосредственно в расширении. А эти адреса разработчик заранее положил сюда:

singleProxy: {
    scheme: "socks5",
    host: node.host,
    port: node.port
}

То есть пользователь фактически доверяет владельцу этой proxy-инфраструктуры. И здесь я бы уже не стал слепо верить надписи VPN в интерфейсе.

Интересная деталь с neoncloak.space

Есть ещё одна вещь, которая привлекла моё внимание. После установки расширение открывает:

chrome.tabs.create({
    url: "https://neoncloak.space/",
    active: true
});

При этом часть серверов тоже использует домен:

jp.neoncloak.space
sg.neoncloak.space
ca.neoncloak.space
au.neoncloak.space
tr.neoncloak.space

То есть в одном месте у меня получается:

TOP VPN
    │
    ├── worker.js
    │
    ├── SOCKS5
    │
    ├── neoncloak.space
    │
    └── *.neoncloak.space

Само по себе это, конечно, не является доказательством чего-либо плохого. Но для меня это хороший повод продолжить исследование инфраструктуры.

Бесплатный сервер и Premium

Ещё одна интересная деталь находится прямо в конфигурации:

"premium": false

Например:

{
    "id": "us-1",
    "name": "США 1",
    "countryCode": "US",
    "host": "103.35.189.225",
    "port": 1082,
    "premium": false
}

А другие серверы имеют:

"premium": true

Получается достаточно простая модель:

US-1
↓
бесплатный

US-2
US-3
DE-1
DE-2
...
↓
Premium

При этом само расширение называется TOP VPN, а для платной подписки пользователю предлагают Myxa VPN. Вот это сочетание мне уже показалось достаточно странным. Получается несколько разных названий:

TOP VPN
Myxa VPN
neoncloak.space

И я бы точно не стал делать вывод, что это разные независимые продукты, пока не посмотрел их инфраструктуру и код.

Что происходит при отключении

Здесь всё достаточно просто. При отключении расширение возвращает браузеру системную proxy-конфигурацию:

function releaseTunnel() {
    chrome.proxy.settings.set({
        value: {
            mode: "system"
        },
        scope: "regular"
    }, function() {
        relayActive = false;
        chosenNode = null;
        chrome.action.setBadgeText({ text: "" });
    });
}

То есть:

VPN ON

Chrome
 ↓
SOCKS5
 ↓
server

а после выключения:

VPN OFF

Chrome
 ↓
System Proxy
 ↓
Internet

Никакой сложной логики здесь нет.

Что в итоге получилось

Когда я впервые увидел расширение, я ожидал найти обычную VPN-реализацию.

Вместо этого в worker.js обнаружился достаточно простой механизм:

выбрали страну
      ↓
нашли IP/домен
      ↓
выбрали порт 1082
      ↓
SOCKS5
      ↓
chrome.proxy.settings.set()

И всё. Поэтому я бы не писал, что «это точно вредоносный VPN» — для такого вывода одного worker.js недостаточно. Но назвать его полноценным VPN с собственной защищённой туннельной реализацией по этому коду я тоже не могу. Фактически перед нами браузерное расширение, которое перенаправляет трафик через заданные SOCKS5-серверы. И для меня это хороший пример того, почему при выборе VPN нельзя ориентироваться только на интерфейс и красивую кнопку ON. Я бы всегда сначала посмотрел, что находится под капотом.

Потому что между:

«Я включил VPN»

и:

«Я установил SOCKS5 proxy, принадлежащий неизвестному оператору»

есть довольно большая разница. И именно эту разницу обычный пользователь, скорее всего, вообще не увидит.