ByeByeVPN

всем привет. запилил тулзу - ByeByeVPN, показывает как ТСПУ / GFW увидит твой VPN / proxy / Reality сервер. кидаешь IP или хостнейм, на выходе получаешь:

  • score 0-100 + лейбл (CLEAN / NOISY / SUSPICIOUS / OBVIOUSLY VPN)

  • 3-tier TSPU-вердикт: BLOCK / THROTTLE / ALLOW + разбор какие правила сработали

  • hardening suggestions что чинить

8-фазный pipeline: DNS → GeoIP (9 источников) → full TCP scan → UDP probes (12 протоколов включая AmneziaWG / Hysteria2 / TUIC / L2TP) → TLS + cert fingerprint → J3 active probing (TSPU-style) → SNITCH RTT/GeoIP consistency → verdict. Реализованы все оси выявления из OCR-методики РКН §5-10.

стек: C++20 + OpenSSL, static exe 1.1 МБ, без DLL, без прав админа, без инсталла, без телеметрии. Запускать двойным кликом (интерактивное меню) либо byebyevpn.exe <host>.

репа: https://github.com/pwnnex/ByeByeVPN
последний релиз: https://github.com/pwnnex/ByeByeVPN/releases/latest

про прозрачность (на всякий случай): сурс открытый, сборка воспроизводимая (см. BUILD.md), в README есть секция On-the-wire posture - таблица что уходит на каждом уровне + команда проверить что ни один исходящий байт не идентифицирует тулку. В SECURITY.md публичная таблица known-but-open threats (JA3, burst pattern и т.д. - что ещё не закрыл).

проверить что в сеть не сливается маркер тулки - 30 секунд:

git clone https://github.com/pwnnex/ByeByeVPN && cd ByeByeVPN

grep -nE 'ByeByeVPN|BBVPN|pwnnex' src/byebyevpn.cpp

→ должно быть 3 совпадения, все нетворк-нейтральные (banner комментарий, коммент про сам фикс, текст --help).

багрепорты / PR / находки приветствуются. особенно интересны кейсы где verdict engine ошибается, fingerprint’ы которые я пропустил, или порт uTLS-Chrome для настоящей JA3-мимикрии.

отдельная благодарность людям которые проводят аудит, проверяют на уязвимости.
благодаря вам тулза живет, и в последнее время релизы будут выходить реже (очень мало времени в ИРЛ)
все issus/pr, буду принимать и активно фиксить.
спасибо!

Нейросеть заменило шило на мыло в последних изменениях. Fingerprint пользователей тулзы остаётся.

Все ваши IP адреса туннелей также легко идентифицировать шпионскими модулями.

Заявлено, что заголовки якобы идентичны последней версии Chrome 131. Сейчас уже 147. И они не полностью идентичны тому что реально шлёт браузер и образуют статичную уникальную комбинацию.

Для большинства IP чекеров вообще никакие заголовки не нужны:

curl -sS "https://api.ipapi.is/?q=8.8.8.8"
curl -sS "https://iplocate.io/api/lookup/8.8.8.8"
curl -sS "http://ip-api.com/json/8.8.8.8?fields=status,country,countryCode,city,isp,org,as,asname,hosting,proxy,mobile,query"
curl -sS "https://ipwho.is/8.8.8.8"
curl -sS "https://ipinfo.io/8.8.8.8/json"
curl -sS "http://api.sypexgeo.net/json/8.8.8.8"
curl -sS "http://api.2ip.me/geo.json?ip=8.8.8.8"
curl -sS -L https://freeipapi.com/api/json/8.8.8.8

Пихать туда что-то значит намеренно вставлять fingerprint. Вы передаете шпионам секрет – отличаться нельзя.

Не понимаю зачем используется https://2ip.io/geoip/<ip>/, если оно обычно выдает anti-bot, с которым программа не справляется вообще никак.


Релизные бинарники лучше собирать с помощью GitHub Actions если есть такая возможность, иначе что реально скрыто в опубликованных мы не знаем.

То что собирается через инструкцию в BUILD.md не совсем согласуется с тем, что реально публикуется.

BUILD.md

Зачем-то используется именно старый OpenSSL 3.6.1-3 который везде по ветке версии 3.6.x в отличие от 3.6.2-2 (новый) – по умолчанию не распространяется, лишь в архивах.

В репозитории заявлен источник libssl: “msys2 package mingw-w64-ucrt-x86_64-openssl-3.6.1-3” с хэшем 069b8c7c92872a5a4336aeea492a58d3be137afc69bd49d4cc4f153d347a65be. Не совпадает ни с одним из обнаруженных зеркал:

А именно:

  • libssl.a
    · Фактически: 1,713,862 bytes
    · SHA256: 8ca56f23e80460fbd06e36f17373ae1fc0195a4829ea595f0c3383b9d0f40e01
    · В BUILD.md: 475,768 bytes, 069b8c7c92872a5a4336aeea492a58d3be137afc69bd49d4cc4f153d347a65be

  • libcrypto.a
    · Фактически: 10,014,240 bytes
    · SHA256: f238d9f18bc5a47489b0997bad8ae1ebc4a0a4ca46ef687e37335cc74d055c33
    · В BUILD.md: 4,607,000 bytes, 0aa09e4841847b6aee0d77449c576944b0fe08e9baa457cebb0d2560d7abe371

Значит использовались не публичные или модифицированные версии?

Если собрать с теми что распространяются публично, используя те же инструменты, размер итогового файла меньше того что опубликован на 200,587 байт (0.2006 MB). Что добавили в опубликованном – неясно.


UPD: Второй тейк частично оправдывается последним релизом. Вчера меня тоже смутило огромное расхождение в размере файлов.

UPD 2: Открыл issues 1 и 2 по поводу проблем.

спасибо, по делу, всё закрыл в v2.5.5: Release ByeByeVPN v2.5.5 · pwnnex/ByeByeVPN · GitHub

про хедеры (#5): убрал chrome-131 блок. http_get теперь голый GET - ни ua, ни accept, ни accept-encoding, ни sec-fetch, ни upgrade-insecure. winhttp открываю с пустым agent плюс force-override через WINHTTP_OPTION_USER_AGENT = “” потому что на некоторых билдах винхттп пихает дефолтный ua даже при пустом session name. SendRequest с WINHTTP_NO_ADDITIONAL_HEADERS. auto-gzip через DECOMPRESSION оставил на случай если сервер сам сожмёт, но accept-encoding не шлём. https_probe и fp_http_plain тоже минимум (Host, Accept: */*, Connection: close). прогнал смоук на 8.8.8.8 и 1.1.1.1, все 9 провайдеров отвечают на bare GET.

про 2ip.io (#5): выкинул. geo_2ip_ru теперь сразу на api.2ip.me/geo.json, тот же url что у тебя в curl’е.

про build.md (#4): попутно апнул openssl с 3.6.1-3 на 3.6.2-2 (current upstream). build-win/*.a теперь реально в git через exception в .gitignore, это настоящие static archives из публичного pkg. sha в BUILD.md совпадают с pacman:

pkg 3.6.2-2: 4b919cc9ed46b55c465a39204bf5034f2d8be931840c6a62ae71b1554bbea9a5

libssl.a (1664942): bf7a501fbcd50db87187ff263c35a264be48dd5ce18278bdf350aa6ad2a6a038

libcrypto.a (9683326): 23c62ffdef07d5ee2a4667eba45d713b56b5f57cbd0ae4b15dfd761500bd0046

source urls в BUILD.md, проверяется pacman -S mingw-w64-ucrt-x86_64-openssl или curl https://repo.msys2.org/mingw/ucrt64/mingw-w64-ucrt-x86_64-openssl-3.6.2-2-any.pkg.tar.zst.

ci: добавил .github/workflows/release.yml, на push тега пересобирает в pinned msys2 ucrt64, сверяет build-win/*.a байт-в-байт с pacman pkg, objdump дёргает чтоб убедиться что в бинаре нет mingw/openssl dll-деп, грепает сорс на tool-fingerprint строки (фейлит билд на любом лишнем матче), пишет sha256 exe и zip в release body. начиная с v2.5.5 все релизы выходят только из ci.

про +200кб в v2.5.4: претензия справедливая. тот бинарь собирал локально у себя, откуда лишние байты честно не знаю (скорее всего pe timestamp + build-id от другого окружения). постфактум не переделать, тот zip уже выложен как есть. v2.5.5+ идёт из ci с sha256 в release notes, любой сверит.

что остаётся открытым: tls ja3 не чистый chrome, нужен utls port в c++ (большой рерайт, записано в SECURITY.md как known threat). single-source-ip repeat-scan correlation на уровне тулы не починить, просто факт.

попутно нашёл аудитом ещё несколько багов не связанных с твоей критикой: fp_socks5 читал reply[1] когда сервер возвращал ровно 1 байт (UB), 9 cli-error путей пропускали WSACleanup, atoi на --threads/–tcp-to/–udp-to без clamp негатива (wrap’ало SO_TIMEO в 49 дней), open.size() в scan_tcp читался вне мьютекса. всё в changelog.

docs: readme переписал без маркетингового воды, добавил китайский и персидский переводы. SECURITY.md подрезал, там теперь секция recently closed с конкретикой что именно починил.

issues #4 и #5 закрыл как fixed. спасибо за разбор, реально полезно

программа огонь, проверял старой версией было всё CLEAN, проверил последней версией и получил canned response (типа палево кароче), гемини накатала конфиг нжинкса и всё стало збс 100 из 100 конфиг засунул под спойлер может кому пригодится

Summary
# --- DUMMY DEFAULT SERVER (The Decoy) ---
server {
    listen 80 default_server;
    listen 443 ssl default_server;
    server_name _;

    ssl_certificate /etc/letsencrypt/live/yourdomainhere/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/yourdomainhere/privkey.pem;

    return 404;
}

# --- REAL PROXY SERVER ---
server {
    listen 80;
    server_name yourdomainhere;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    server_name yourdomainhere;

    ssl_certificate /etc/letsencrypt/live/yourdomainhere/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/yourdomainhere/privkey.pem;

    ssl_protocols TLSv1.3;
    ssl_prefer_server_ciphers off;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 1d;
    ssl_session_tickets off;
    ssl_stapling on;
    ssl_stapling_verify on;

    add_header Strict-Transport-Security "max-age=63072000" always;
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;

    root /var/www/html;
    index index.html;

    error_page 400 /400.html;
    error_page 403 /403.html;
    error_page 404 /404.html;
    error_page 500 502 503 504 /50x.html;

    location /api/stream {
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_buffering off;
        proxy_request_buffering off;
        proxy_read_timeout 1h;
        proxy_send_timeout 1h;
        proxy_pass http://127.0.0.1:8080;
        proxy_hide_header Server;
        proxy_hide_header X-Powered-By;
        add_header Server "nginx" always;
        access_log off;
        error_log off;
    }

    location / {
        try_files $uri $uri/ =404;
    }
}

а вообще взято отсюдова изначально GitHub - filip-lebiecki/xray-xhttp · GitHub

спасибо за обратный отзыв, рад что тулза помогла вам)

Без комментариев. Версия 2.5.8

Summary

| __ ) _ _ | __ ) _ _ __\ \ / / _ | \ | |
| _ | | | |/ _ \ _ | | | |/ _ \ \ / /| |
) | | |
| |) | || | / |) | || | /\ V / | /| |\ |
|___/ _, |_
|___/ _, |_| _/ || || _|
|/ |__/
Full TSPU/DPI/VPN detectability scanner v2.5.8

[1] Full scan — end-to-end scan of an IP/hostname
[2] TCP port scan — TCP port-scan only
[3] UDP probes — OpenVPN / WireGuard / IKE / QUIC / DNS
[4] TLS + SNI consistency — TLS audit on a single port (Reality discriminator)
[5] J3 active probing — TSPU/GFW-style probes on one port
[6] GeoIP lookup — country / ASN / VPN-flag aggregation
[7] Local analysis — this machine: VPN adapters, split-tunnel, processes
[8] SNITCH latency check — RTT + GeoIP consistency (methodika §10.1)
[9] Traceroute — ICMP hop count analysis (ttl sweep)
[0] Exit

1
target (IP or hostname): 4pda.to

[1/8] DNS resolve
4pda.to → 104.20.39.144 172.66.159.63 [v4, 348ms]
using primary IP 104.20.39.144 for all probes

[2/8] GeoIP (9 providers in parallel: 3 EU / 3 RU / 3 global)
ipapi.is IP 104.20.39.144 US (San Francisco) AS 13335 Cloudflare, Inc.
flags: HOSTING
iplocate.io IP 104.20.39.144 US (New York City) AS AS13335 Cloudflare, Inc.
flags: HOSTING
freeipapi.com err: http 0 io 12002
2ip.me (RU) err: http 0 io 12002
IP-API.com - Geolocation API (RU) IP 104.20.39.144 CA (Торонто) AS AS13335 Cloudflare, Inc. Cloudflare, Inc.
flags: HOSTING
sypexgeo.net (RU) IP 104.20.39.144 US-CA (San Francisco) AS
ip-api.com IP 104.20.39.144 CA (Toronto) AS AS13335 Cloudflare, Inc. Cloudflare, Inc.
flags: HOSTING
ipwho.is IP 104.20.39.144 US (San Francisco) AS 13335 Cloudflare, Inc.
ipinfo.io IP 104.20.39.144 US (San Francisco) AS AS13335 Cloudflare, Inc.

[3/8] TCP port scan mode=FULL 1-65535 (65535 ports, 500 threads, 800ms timeout)
(press ‘q’ to skip this phase)
scan done (65535/65535, open=10)
:80 106ms HTTP
:443 125ms HTTPS / XTLS / Reality
:2052 153ms -
:2053 141ms -
:2082 150ms -
:2083 172ms -
:2086 160ms -
:2087 185ms -
:2095 167ms -
:2096 184ms -

[4/8] UDP probes
UDP:53 DNS query no answer (no-reply / filtered)
UDP:500 IKEv2 SA_INIT no answer (no-reply / filtered)
UDP:4500 IKEv2 NAT-T no answer (no-reply / filtered)
UDP:1194 OpenVPN HARD_RESET no answer (no-reply / filtered)
UDP:443 QUIC v1 Initial no answer (no-reply / filtered)
UDP:51820 WireGuard handshake no answer (no-reply / filtered)
UDP:41641 Tailscale handshake no answer (no-reply / filtered)
UDP:1701 L2TP SCCRQ no answer (no-reply / filtered)
UDP:36712 Hysteria2 QUIC no answer (no-reply / filtered)
UDP:8443 TUIC v5 no answer (no-reply / filtered)
UDP:55555 AmneziaWG Sx=8 no answer (no-reply / filtered)
UDP:51820 AmneziaWG Sx=8 no answer (no-reply / filtered)

[5/8] Service fingerprints per open port
:80 HTTP HTTP/1.1 403 Forbidden | Server: cloudflare %[cloudflare]
:80 HTTP-PROXY HTTP/1.1 400 Bad Request [vpn-like]
:443 TLS TLSv1.3 / TLS_AES_256_GCM_SHA384 / ALPN=h2 / / 342ms
cert CN=4pda.to issuer=WE1 age=56d left=33d SAN=2 wildcard [free-CA]
SNI behaviour: cert varies per SNI (normal multi-tenant TLS, not Reality)
cert-sha256: 7eb1e05a131366d4… issuer: /C=US/O=Google Trust Services/CN=WE1
CT-log (crt.sh): cert IS in public CT logs (0 entries) — normal legit cert
HTTP-over-TLS: HTTP/1.1 403 Forbidden Server: cloudflare
[cdn] Cloudflare (CF-Ray=9f8484f0fe79a0f1-HKG)
:2052 unknown open but silent on connect (ambiguous: firewalled service / Shadowsocks / Trojan / Reality wrapper — inconclusive without protocol match)
:2053 unknown open but silent on connect (ambiguous: firewalled service / Shadowsocks / Trojan / Reality wrapper — inconclusive without protocol match)
:2082 unknown open but silent on connect (ambiguous: firewalled service / Shadowsocks / Trojan / Reality wrapper — inconclusive without protocol match)
:2083 TLS TLSv1.3 / TLS_AES_256_GCM_SHA384 / ALPN=h2 / / 325ms
cert CN=4pda.to issuer=WE1 age=56d left=33d SAN=2 wildcard [free-CA]
SNI behaviour: cert varies per SNI (normal multi-tenant TLS, not Reality)
cert-sha256: 7eb1e05a131366d4… issuer: /C=US/O=Google Trust Services/CN=WE1
CT-log (crt.sh): query failed — http 0
HTTP-over-TLS: HTTP/1.1 403 Forbidden Server: cloudflare
[cdn] Cloudflare (CF-Ray=9f8485465f930eb8-HKG)
:2086 unknown open but silent on connect (ambiguous: firewalled service / Shadowsocks / Trojan / Reality wrapper — inconclusive without protocol match)
:2087 TLS TLSv1.3 / TLS_AES_256_GCM_SHA384 / ALPN=h2 / / 311ms
cert CN=4pda.to issuer=WE1 age=56d left=33d SAN=2 wildcard [free-CA]
SNI behaviour: cert varies per SNI (normal multi-tenant TLS, not Reality)
cert-sha256: 7eb1e05a131366d4… issuer: /C=US/O=Google Trust Services/CN=WE1
CT-log (crt.sh): cert IS in public CT logs (0 entries) — normal legit cert
HTTP-over-TLS: HTTP/1.1 403 Forbidden Server: cloudflare
[cdn] Cloudflare (CF-Ray=9f848590594b03b7-HKG)
:2095 unknown open but silent on connect (ambiguous: firewalled service / Shadowsocks / Trojan / Reality wrapper — inconclusive without protocol match)
:2096 TLS TLSv1.3 / TLS_AES_256_GCM_SHA384 / ALPN=h2 / / 316ms
cert CN=4pda.to issuer=WE1 age=56d left=33d SAN=2 wildcard [free-CA]
SNI behaviour: cert varies per SNI (normal multi-tenant TLS, not Reality)
cert-sha256: 7eb1e05a131366d4… issuer: /C=US/O=Google Trust Services/CN=WE1
CT-log (crt.sh): cert IS in public CT logs (0 entries) — normal legit cert
HTTP-over-TLS: HTTP/1.1 403 Forbidden Server: cloudflare
[cdn] Cloudflare (CF-Ray=9f8485d1ccc920fb-HKG)

[6/8] J3 / TSPU active probing
→ port :80
SILENT empty/close (dropped)
RESP HTTP GET / 412B HTTP/1.1 403 Forbidden [48 54 54 50 2f 31 2e 31 20 34 30 33 20 46 6f 72]
RESP HTTP CONNECT 316B HTTP/1.1 400 Bad Request [48 54 54 50 2f 31 2e 31 20 34 30 30 20 42 61 64]
RESP SSH banner 316B HTTP/1.1 400 Bad Request [48 54 54 50 2f 31 2e 31 20 34 30 30 20 42 61 64]
RESP random 512B 316B HTTP/1.1 400 Bad Request [48 54 54 50 2f 31 2e 31 20 34 30 30 20 42 61 64]
SILENT TLS CH invalid-SNI (dropped)
RESP HTTP abs-URI (proxy-style) 843B HTTP/1.1 200 OK [48 54 54 50 2f 31 2e 31 20 32 30 30 20 4f 4b 0d]
RESP 0xFF x128 316B HTTP/1.1 400 Bad Request [48 54 54 50 2f 31 2e 31 20 34 30 30 20 42 61 64]
→ responds to arbitrary bytes (plaintext HTTP-style origin) (silent=2 / resp=6)
→ port :443
SILENT empty/close (dropped)
RESP HTTP GET / 414B HTTP/1.1 400 Bad Request [48 54 54 50 2f 31 2e 31 20 34 30 30 20 42 61 64]
RESP HTTP CONNECT 316B HTTP/1.1 400 Bad Request [48 54 54 50 2f 31 2e 31 20 34 30 30 20 42 61 64]
RESP SSH banner 316B HTTP/1.1 400 Bad Request [48 54 54 50 2f 31 2e 31 20 34 30 30 20 42 61 64]
RESP random 512B 316B HTTP/1.1 400 Bad Request [48 54 54 50 2f 31 2e 31 20 34 30 30 20 42 61 64]
RESP TLS CH invalid-SNI 7B …2 [15 03 01 00 02 02 32]
RESP HTTP abs-URI (proxy-style) 414B HTTP/1.1 400 Bad Request [48 54 54 50 2f 31 2e 31 20 34 30 30 20 42 61 64]
RESP 0xFF x128 7B …F [15 03 01 00 02 02 46]
→ responds to arbitrary bytes (plaintext HTTP-style origin) (silent=1 / resp=7)
uniform reply: the SAME first-line (414B ‘HTTP/1.1 400 Bad Request’) for 2 raw-TCP probes, but the HTTP-over-TLS probe is clean — that’s normal nginx/CDN behaviour on a TLS port (not a fallback)
→ port :2083
SILENT empty/close (dropped)
RESP HTTP GET / 414B HTTP/1.1 400 Bad Request [48 54 54 50 2f 31 2e 31 20 34 30 30 20 42 61 64]
RESP HTTP CONNECT 316B HTTP/1.1 400 Bad Request [48 54 54 50 2f 31 2e 31 20 34 30 30 20 42 61 64]
RESP SSH banner 316B HTTP/1.1 400 Bad Request [48 54 54 50 2f 31 2e 31 20 34 30 30 20 42 61 64]
RESP random 512B 316B HTTP/1.1 400 Bad Request [48 54 54 50 2f 31 2e 31 20 34 30 30 20 42 61 64]
RESP TLS CH invalid-SNI 7B …2 [15 03 01 00 02 02 32]
RESP HTTP abs-URI (proxy-style) 414B HTTP/1.1 400 Bad Request [48 54 54 50 2f 31 2e 31 20 34 30 30 20 42 61 64]
RESP 0xFF x128 7B …F [15 03 01 00 02 02 46]
→ responds to arbitrary bytes (plaintext HTTP-style origin) (silent=1 / resp=7)
uniform reply: the SAME first-line (414B ‘HTTP/1.1 400 Bad Request’) for 2 raw-TCP probes, but the HTTP-over-TLS probe is clean — that’s normal nginx/CDN behaviour on a TLS port (not a fallback)
→ port :2087
SILENT empty/close (dropped)
RESP HTTP GET / 414B HTTP/1.1 400 Bad Request [48 54 54 50 2f 31 2e 31 20 34 30 30 20 42 61 64]
RESP HTTP CONNECT 316B HTTP/1.1 400 Bad Request [48 54 54 50 2f 31 2e 31 20 34 30 30 20 42 61 64]
RESP SSH banner 316B HTTP/1.1 400 Bad Request [48 54 54 50 2f 31 2e 31 20 34 30 30 20 42 61 64]
RESP random 512B 316B HTTP/1.1 400 Bad Request [48 54 54 50 2f 31 2e 31 20 34 30 30 20 42 61 64]
RESP TLS CH invalid-SNI 7B …2 [15 03 01 00 02 02 32]
RESP HTTP abs-URI (proxy-style) 414B HTTP/1.1 400 Bad Request [48 54 54 50 2f 31 2e 31 20 34 30 30 20 42 61 64]
RESP 0xFF x128 7B …F [15 03 01 00 02 02 46]
→ responds to arbitrary bytes (plaintext HTTP-style origin) (silent=1 / resp=7)
uniform reply: the SAME first-line (414B ‘HTTP/1.1 400 Bad Request’) for 2 raw-TCP probes, but the HTTP-over-TLS probe is clean — that’s normal nginx/CDN behaviour on a TLS port (not a fallback)
→ port :2096
SILENT empty/close (dropped)
RESP HTTP GET / 414B HTTP/1.1 400 Bad Request [48 54 54 50 2f 31 2e 31 20 34 30 30 20 42 61 64]
RESP HTTP CONNECT 316B HTTP/1.1 400 Bad Request [48 54 54 50 2f 31 2e 31 20 34 30 30 20 42 61 64]
RESP SSH banner 316B HTTP/1.1 400 Bad Request [48 54 54 50 2f 31 2e 31 20 34 30 30 20 42 61 64]
RESP random 512B 7B …F [15 03 01 00 02 02 46]
RESP TLS CH invalid-SNI 7B …2 [15 03 01 00 02 02 32]
RESP HTTP abs-URI (proxy-style) 414B HTTP/1.1 400 Bad Request [48 54 54 50 2f 31 2e 31 20 34 30 30 20 42 61 64]
RESP 0xFF x128 7B …F [15 03 01 00 02 02 46]
→ responds to arbitrary bytes (plaintext HTTP-style origin) (silent=1 / resp=7)
uniform reply: the SAME first-line (414B ‘HTTP/1.1 400 Bad Request’) for 2 raw-TCP probes, but the HTTP-over-TLS probe is clean — that’s normal nginx/CDN behaviour on a TLS port (not a fallback)

[7/8] SNITCH latency + traceroute + SSTP
SNITCH RTT: median=84.2ms min=82.3ms max=101.2ms stddev=7.0ms (6 samples)
Anchors: Cloudflare=116ms Google=130ms Yandex=121ms
Expected: country=US physical_min=100ms (from US observer)
=> target RTT doesn’t match closest anchor ratio — location doesn’t add up
Traceroute: 8 hops, reached=yes, max_rtt_jump=78ms, long_hops(>150ms)=0
1 192.168.1.1 0ms
2 10.251.14.121 48ms
3 10.251.14.122 4ms
4 87.226.133.55 82ms
5 63.217.237.29 80ms
6 63.218.174.130 79ms
7 *
8 103.22.203.23 80ms
9 104.20.39.144 80ms
SSTP/443: SSTP? TLS handshake failed (not HTTPS)
Our ClientHello JA3: 0cce74b0d9b7f8528fb2181588d23793 (OpenSSL 3.x default — real browsers use uTLS-Chrome)

[8/8] Verdict

Stack identified: 3x-ui/x-ui/Marzban panel install (multiple preset TLS ports open) — VLESS/Trojan/Shadowsocks multiplex likely

Per-port classification:
:443 generic HTTPS / CDN origin (junk probes get HTTP 4xx as expected) — TLSv1.3 / ALPN=h2 / CN=4pda.to / issuer=WE1 / age=56d / validity=90d / SAN=2
:2083 generic HTTPS / CDN origin (junk probes get HTTP 4xx as expected) — TLSv1.3 / ALPN=h2 / CN=4pda.to / issuer=WE1 / age=56d / validity=90d / SAN=2
:2087 generic HTTPS / CDN origin (junk probes get HTTP 4xx as expected) — TLSv1.3 / ALPN=h2 / CN=4pda.to / issuer=WE1 / age=56d / validity=90d / SAN=2
:2096 generic HTTPS / CDN origin (junk probes get HTTP 4xx as expected) — TLSv1.3 / ALPN=h2 / CN=4pda.to / issuer=WE1 / age=56d / validity=90d / SAN=2
:80 OPEN HTTP PROXY (accepts CONNECT) — HTTP/1.1 400 Bad Request

DPI exposure matrix:
Port-based (default VPN ports) LOW no default VPN ports among open set
Protocol handshake signature LOW TLS handshake looks normal
Cert-steering (Reality discriminator) NONE cert varies per SNI (multi-tenant TLS, not Reality)
ASN classifier (VPS/hosting) LOW 4 sources classify the ASN as hosting/datacenter — normal for any public server
Threat-intel tags (VPN/Proxy/Tor) NONE no VPN/Proxy/Tor tag from any source
Cert freshness (new-LE watch) LOW no suspiciously fresh certs
Active junk probing (J3) LOW 34 responses — looks like a permissive web-origin
Open-port profile (sparsity) HIGH 10 ports open, dominated by the 3x-ui/x-ui/Marzban preset TLS cluster 4 hits (2053/2083/2087/2096/8443/…) — installer fingerprint
TLS hygiene (1.3 + h2 + trusted-CA) LOW TLS posture is clean (1.3 + h2 + trusted-CA)
Cert impersonation (Reality-static tell) NONE no cert claims a major-brand domain the ASN doesn’t own
Active HTTP-over-TLS probe LOW 4 port(s) returned a well-formed HTTP reply with a Server: header — looks like a real web origin
Panel-port cluster (3x-ui/x-ui/Marzban) HIGH 4 of the preset panel TLS ports are open (2053/2083/2087/2096/8443/8880/6443/7443/9443)
J3 canned/anomaly aggregate LOW no canned / bad-version / raw-non-HTTP replies

Strong signals (2) [! = real evidence of VPN/proxy]
[!] 4 of the classical 3x-ui/x-ui/Marzban panel TLS ports are open ({2053,2083,2087,2096}) — installer fingerprint; regular webhosts rarely open this exact set
[!] open HTTP proxy (accepts CONNECT) on :80

Soft signals (0) [- = suggestive pattern, not proof]
(none)

Informational (3) [i = observation only, no penalty — normal sites can have these]
[i] asn-hosting 4 of 9 sources classify the ASN as hosting/datacenter (normal for any public server — not a red flag on its own)
[i] snitch-anchor SNITCH: target RTT doesn’t match the closest anchor ratio — geolocation may be off
[i] trace-ok traceroute: 8 hops, max RTT step 78ms — path looks clean

Final score: 66/100 verdict: SUSPICIOUS

Hardening suggestions:
[xui-panel]
The open-port profile matches the 3x-ui / x-ui / Marzban panel installer set
(2053/2083/2087/2096/8443/8880/6443/7443/9443). That exact cluster is the
single strongest fingerprint a TSPU-class DPI engine looks for. Fix: close
the unused panel ports (keep ONE listener on :443 on the real Reality inbound),
firewall the panel UI to admin source IPs only, and avoid the defaults.
[asn-hosting]
Being on a hosting ASN is the norm for every public server — this alone is
NOT a VPN signal. TSPU does use ASN as a gate for deeper checks, but
what it then verifies is the TLS/HTTP behaviour, not the ASN itself.
If you want to escape the ‘hosting ASN’ category entirely, the only
clean move is a residential-ASN proxy in front (rare) or a CDN.

ТСПУ / TSPU classification (emulated Russian DPI verdict):
Verdict: THROTTLE / QoS — 1 B-tier anomaly — TSPU would tag this host for further monitoring / rate-limiting but not instant block
TSPU-tier hits: A=0 (protocol block) / B=1 (soft anomaly)
Triggered rules:
[B] 3x-ui/x-ui/Marzban panel Panel-installer preset TLS-port cluster open
What the operator sees:
The destination is flagged but not blocked. Flows are logged, RTT +
handshake patterns are sampled over time. If the anomaly persists or
converges with other hosts in the same /24, the block threshold trips.

Threat-model note:
TSPU/GFW classify a destination by what the IP actually does on the wire —
TLS handshake bytes, cert-steering, active HTTP-over-TLS reply shape,
reactions to junk, default-port replies. IP ‘reputation’ (hosting ASN /
GeoIP VPN tag) is only a coarse pre-filter, so this tool treats it as
informational and focuses the score on the actual protocol signatures at
the endpoint. v2.4 strong signals are: cert impersonation (brand CN on
non-owning ASN), short-validity certs (<14d), canned-fallback pages,
HTTP-version anomalies, 3x-ui/x-ui/Marzban panel-port clusters, CT-log
absence on fresh certs, proxy-chain header leakage (Via/Forwarded/XFF),
SNITCH geo-latency inconsistency (§10.1), modern tunnels (AmneziaWG /
Hysteria2 / TUIC / L2TP / SSTP) — these are expensive-to-fake tells that
map directly to Xray / Reality / Trojan / modern obfuscated VPN stacks.
If every strong signal is ‘none’ and soft signals are quiet, the host is
essentially invisible to passive DPI regardless of what the ASN looks like.
Reference methodology: Russian OCR методика выявления VPN/Proxy (§5-10).

[Enter] to continue…


Продетектил обычный nginx http 80 как canned.
Но там ничего нет


    SILENT   empty/close                   (dropped)
    RESP     HTTP GET /                    616B  HTTP/1.1 200 OK  [48 54 54 50 2f 31 2e 31 20 32 30 30 20 4f 4b 0d]
    RESP     HTTP CONNECT                  295B  HTTP/1.1 400 Bad Request  [48 54 54 50 2f 31 2e 31 20 34 30 30 20 42 61 64]
    RESP     SSH banner                    295B  HTTP/1.1 400 Bad Request  [48 54 54 50 2f 31 2e 31 20 34 30 30 20 42 61 64]
    RESP     random 512B                   295B  HTTP/1.1 400 Bad Request  [48 54 54 50 2f 31 2e 31 20 34 30 30 20 42 61 64]
    RESP     TLS CH invalid-SNI            295B  HTTP/1.1 400 Bad Request  [48 54 54 50 2f 31 2e 31 20 34 30 30 20 42 61 64]
    RESP     HTTP abs-URI (proxy-style)    616B  HTTP/1.1 200 OK  [48 54 54 50 2f 31 2e 31 20 32 30 30 20 4f 4b 0d]
    RESP     0xFF x128                     295B  HTTP/1.1 400 Bad Request  [48 54 54 50 2f 31 2e 31 20 34 30 30 20 42 61 64]
    -> responds to arbitrary bytes (plaintext HTTP-style origin)  (silent=1 / resp=7)
    !! canned response: the SAME first-line (616B 'HTTP/1.1 200 OK') came back for 2 different probes — not  real web server, that's a static fallback page (cassic Xray `fallback+redirect`, Trojan, or Caddy placeholder)
server {
        listen 80 default_server;
        listen [::]:80 default_server;

        root /home/www/myserver;

        index index.html index.htm;

        server_name myserver.com www.myserver.com;

        location /pub {
                autoindex on;
                autoindex_exact_size on;
                autoindex_localtime on;
        }
        location / {
                # First attempt to serve request as file, then
                # as directory, then fall back to displaying a 404.
                try_files $uri $uri/ =404;
        }

}

оо, болван привет)
рад что ты заметил тулзу

скинь полный вывод, она же ведь не только nginx ищет)

зачем ты туда сунул 4pda.to …

Отчет прочитай, что там нашла твоя прога.

пока есть свободное время, изучил твой тест:

обе hard-сигналы которые накопали 2.5.8 это false positive на Cloudflare-фронте, ща поясню:

  1. “panel-cluster {2053,2083,2087,2096}” - это стандартные cloudflare-origin порты, CF открывает 2052/2053/2082/2083/2086/2087/2095/2096 на каждом edge, ровно как 80/443. они полностью перекрываются с пресетом 3x-ui/x-ui/Marzban (потому что панели именно эти CF-friendly порты и рекомендуют). на любом сайте за CF этот сигнал поднимется автоматом.

фикс: в pre-чеке для panel-cluster я добавлю отказ когда GeoIP/ASN-org == Cloudflare. тогда “те же 4 порта на cloudflare AS13335” не будет считаться panel-installer fingerprint’ом. сделаю в v2.5.10.

  1. “OPEN HTTP PROXY (accepts CONNECT) на :80” - баг в fp_http_connect: я считаю любой HTTP/1.x ответ на CONNECT как пруф что прокся, но реальный признак прокси это HTTP/1.1 200 Connection established, а 400/403 это как раз отказ. CF отдает 400 на CONNECT и я ошибочно его классифицирую как открытую проксю.

фикс: гейтить is_vpn_like + label “HTTP-PROXY” по status == 200 + body содержит "established", иначе пометить как “endpoint rejects CONNECT” без пенальти. тоже в v2.5.10.

по факту реальный score 4pda.to должен быть 90+, оба этих хита уйдут после фиксов. спасибо что прогнал, идеальный test-case для CF-frontend ложноположительности.

(пиши в следующий раз в issue)

короче, как в ирл закончу дела - перейду к фиксам, буду благодарен другим находкам багов.

Просканировал ру-сервер, который не жалко и получил не понятную мне запись в вердикте. Что она пытается сказать? В сетях к сожалению не разбираюсь. Использовал v2.5.9

Спойлер
  Soft signals (1)  [- = suggestive pattern, not proof]
    [-] traceroute goes through 1 hop(s) matching the tspu management-subnet layout (10.X.Y.[131-235]/[241-245]/254) - indicates a tspu site is between you and the target

  ТСПУ / TSPU classification (emulated Russian DPI verdict):
    Verdict: THROTTLE / QoS  —  1 B-tier anomaly — TSPU would tag this host for further monitoring / rate-limiting but not instant block
    TSPU-tier hits: A=0 (protocol block) / B=1 (soft anomaly)
    Triggered rules:
      [B] TSPU mgmt-subnet in traceroute        hop(s) in 10.X.Y.[131-235]/[241-245]/254 range - tspu site on path
    What the operator sees:
      The destination is flagged but not blocked. Flows are logged, RTT +
      handshake patterns are sampled over time. If the anomaly persists or
      converges with other hosts in the same /24, the block threshold trips.

А потом я решил просканировать netflix.com. Результат шокировал

Спойлер
[8/8] Verdict

  Stack identified:  Xray-core VLESS+Reality (static dest — TLS cert cloned from a major brand)

  Per-port classification:
    :443    Reality hidden-mode (silent-on-junk — strong DPI signature)
    :80     OPEN HTTP PROXY (accepts CONNECT) — HTTP/1.1 400 Bad Request

  DPI exposure matrix:
    Port-based (default VPN ports)       LOW     no default VPN ports among open set
    Protocol handshake signature         LOW     TLS 1.3 handshake looks normal (Reality identified by cert-steering, not handshake bytes)
    Cert-steering (Reality discriminator) HIGH    Reality steering pattern positively identified
    ASN classifier (VPS/hosting)         LOW     4 sources classify the ASN as hosting/datacenter — normal for any public server
    Threat-intel tags (VPN/Proxy/Tor)    NONE    no VPN/Proxy/Tor tag from any source
    Cert freshness (new-LE watch)        LOW     no suspiciously fresh certs
    Active junk probing (J3)             MEDIUM  12 silent / 4 resp — strict TLS-only posture (fingerprintable by TSPU)
    Open-port profile (sparsity)         LOW     sparse (<=3 ports) on hosting ASN — ambiguous (minimal corp server / proxy VPS)
    TLS hygiene (1.3 + h2 + trusted-CA)  LOW     TLS posture is clean (1.3 + h2 + trusted-CA)
    Cert impersonation (Reality-static tell) HIGH    1 cert port(s) claim brand 'netflix.com' on an ASN that does NOT own it — Reality `dest=` cloning signature
    Active HTTP-over-TLS probe           MEDIUM  1 port(s) responded without a Server: header — nginx/Apache/Caddy always set one
    Panel-port cluster (3x-ui/x-ui/Marzban) NONE    no panel-preset TLS ports among open set
    J3 canned/anomaly aggregate          HIGH    1 canned / 0 bad-version / 0 raw-non-HTTP port(s) — static fallback signature

  Strong signals (5)  [! = real evidence of VPN/proxy]
    [!] cert on :443 vouches for brand 'netflix.com' but the ASN is not owned by that brand — Reality-static / cert-cloning signature (Xray `dest=netflix.com` profile)
    [!] Reality in passthrough mode on :443 (base cert is for 'netflix.com' — stream tunnelled to the real brand, SNI-based vhost routing then returns different certs per SNI; cert + ASN disagree)
    [!] HTTP-over-TLS on :443 leaks proxy-chain headers (Via="1.1 i-02ddebf43acc80770 (eu-west" ) — methodika §10.2 diagnostic: the origin IS behind (or IS) a middle proxy
    [!] port :80 returns a canned fallback page (same first-line 'HTTP/1.1 403 Forbidden' with identical byte count 52B for 2 different probes) — real web servers vary their replies; this is the Xray/Trojan `fallback+redirect` signature
    [!] open HTTP proxy (accepts CONNECT) on :80

  Soft signals (2)  [- = suggestive pattern, not proof]
    [-] HTTP-over-TLS on :443 responded without a Server: header — real nginx/Apache/Caddy/CDN set one; absence is a middleware tell
    [-] utls dual-probe on :443 saw different ServerHello (JA4S) per client flavor (chrome=t130200_1301_a56c5b993250, openssl=t130200_1302_a56c5b993250). server adapts TLS parameters to client JA3, suggests utls-aware multi-stack frontend (CDN / smart router / reality-permissive setup).

  Informational (4)  [i = observation only, no penalty — normal sites can have these]
    [i] asn-hosting  4 of 9 sources classify the ASN as hosting/datacenter (normal for any public server — not a red flag on its own)
    [i] sparse-ports  2 TCP ports open on a hosting ASN with :443 — sparse profile; common for both minimal corporate servers and single-purpose proxy VPSes
    [i] trace-ok  traceroute: 4 hops, max RTT step 43ms — path looks clean
    [i] tcp-firewall-drop  closed-port probe got no RST (drop policy). consistent with operator-grade firewall (TSPU ACL drop) or strict cloud SG. not a VPN signal on its own, but it tells us the network is filtered at L3 not just at the host stack.

  Final score: 0/100  verdict: OBVIOUSLY-VPN

  Hardening suggestions:
    [reality-hidden]
      Reality hidden-mode: TLS handshake ok, but non-TLS bytes are silently dropped.
      That pattern is DPI-detectable (TSPU/GFW fingerprint it).
      Fix: set `dest=` to a real HTTPS site you don't control, and configure
      `fallback` so the server returns its own 400/502 page on unrecognised bytes.
    [cert-impersonation]
      Reality `dest=` points at 'netflix.com', so the endpoint serves a cert (and/or
      `Server:` banner) for that brand on an ASN that doesn't own it. This is the
      cheapest tell in the book — DPI engines cross-reference cert subject + HTTP
      Server-header + ASN ownership. Pick a `dest=` on the SAME ASN/CDN as your VPS
      (e.g. a small regional site on the same hosting provider's netblock), or —
      safer — move to a real domain you own with its own full LE chain. Never pick
      amazon/apple/microsoft/google/cloudflare on a random VPS.
    [canned-fallback]
      At least one port returns a canned fallback (same byte-exact first line for
      different probes) or a malformed HTTP version — classic Xray `fallback` /
      Trojan default handler. Real nginx/Apache/Caddy vary their replies per
      request (different URIs -> different statuses, different bodies). Fix:
      put a real nginx in front with a proper error-page map, and make the Xray
      `fallbacks` point at that nginx so non-handshake bytes get REAL HTTP.

если не разбираешься в сетях - копипастни в нейронку и она тебе разжует, тебе будет проще.

я вообще не понимаю зачем второй человек за день сканирует популярные домены, как минимум утилита не рассчитана для скана обычных легитимных серверов. упор в основном идет на детект протоколов/панелей и т.д - на ваших серверах.
напомню, что вы делаете полное сканирование портов (чужих серверов) со своего айпи - последствия думаю рассказывать не нужно.. даже если ты делаешь это ради интереса.

Я пробовал, ии несут чушь об этом. Поведайте как разработчик что вы пытались сказать конкретно этим Soft signals?

Я считаю, что если утилита не способна отличить настоящий сайт от vpn сервера, то нельзя полагаться на её результат

меняй ИИ

Thought for 33s

Итог: по этому фрагменту сервер не считается “моментально блокируемым” и не выглядит как явно распознанный VPN-протокол. Инструмент нашёл только один слабый косвенный признак: в traceroute есть один промежуточный hop из диапазона 10.x.x.x, который ByeByeVPN считает похожим на management-сеть ТСПУ.

Что означает заключение:

Строка Расшифровка
Soft signals (1) Найден 1 мягкий/косвенный сигнал. Это не доказательство.
traceroute goes through 1 hop(s) ... 10.X.Y... По пути от машины, где запускался сканер, до сервера встретился внутренний IP-адрес, похожий на шаблон, который программа связывает с ТСПУ.
Verdict: THROTTLE / QoS По модели ByeByeVPN это не блокировка, а “наблюдать / возможно ограничивать скорость”.
A=0 Не найдено жёсткой сигнатуры протокола, которая дала бы немедленный блок.
B=1 Есть одна soft-аномалия. По шкале программы B=1 = monitoring/rate-limit, B≥2 = block, A≥1 = immediate block.
Triggered rules: TSPU mgmt-subnet in traceroute Единственная причина вердикта — именно маршрут, а не поведение самого сервера.

Важный момент: 10.x.x.x — это приватный RFC1918-диапазон, не публично маршрутизируемый в интернете; такие адреса часто видны внутри сетей провайдеров, CGNAT/MPLS/магистральной инфраструктуры. Поэтому сам факт 10.x.x.x в трассировке не доказывает, что сервер “попал под ТСПУ”; это эвристика инструмента.

Практический вывод: сервер сейчас выглядит не как “заблокировать немедленно”, а как “есть один подозрительный сетевой признак на маршруте”. Риск по этому отчёту низкий/умеренный: тревожный флаг есть, но он один и связан с маршрутом, а не с явной сигнатурой VPN-протокола.

так мне точно такую же чушь нейронка и выдала, чего плохого в том, что на маршруте есть 10.x.x.x? Даже если это реально management-сеть ТСПУ, что это меняет?:slightly_smiling_face:

я думал тебе ничего не выдала, ок

вроде тут неплохо описано в целом

Soft signals (1) [- = suggestive pattern, not proof]
буквально перед глазами текст что это значит, это не говорит о том что у тебя жесткий детект а больше информационный - но желательно исправить на бекенде.
ну, нейронка ничего не поймет если ты ей не дашь тулзу на изучение кода/описания.
а просто копипастнуть в чат логи.. без комментариев.

во первых: раз утилите “грош цена” - пиши свою. (и не нужно редактировать сообщения, я все вижу ;D)
во вторых: она и не должна различать настоящий сайт от vpn-сервера который намеренно притворяется например dest:netflix.com потому что технически в сети они выглядят очень похоже. и именно такие конфиги (reality + популярный SNI) сейчас популярны. если бы тулза их не детектила - она бы не делала свою работу.

прежде чем говорить что “тулза не может различить обычный сервер - от впн сервера, значит хуйня” - перечитывай выше еще раз )