0x4D31

Activity

Stars and mentions · Last 30 days, Oct 6 – Nov 4

GitHub stars1
Mentions31
036912Oct 6Oct 20Nov 4
UTC days · repositories 0x4D31 owns1 repository

Top repositories

0x4D31’s repositories ranked by pull request authors, as on trending.

Loading

Contributes to

Repositories outside 0x4D31’s account where 0x4D31 opened or reviewed pull requests in the past 365 days.

Who noticed 0x4D31

Everyone who starred or forked 0x4D31’s repositories since the archive began in 2011.

Notable accounts

Ordered by the stars their own repositories have received.

Biggest fans

Accounts that starred the most of these repositories.

Breakout weeks

Each repository’s best week of new stars, with the release published around it.

Mentions

31posts
Nov 50 posts
  1. @MalwareTechBlog > the attacker used the compromised computer plus an internal AI tool to gain control of the wallet developer's system nice :)) good one to add to EAA 0x4D31/endpoint-ai-agent-abuse

    17 likes · 3 reposts · 288 views
  2. PHP2Shellの悪用確認を、DebianとDockerの公式イメージで測る PHPの重大な脆弱性2件(CVE-2026-109618、CVE-2026-109619。通称PHP2Shell)が、10月26日(米国時間)にCISAのKEVカタログに追加されました。どちらもCVSS 9.2で、認証なしでリモートコード実行に至ります。連邦機関の対応期限は10月29日で、カタログの「フォレンジックトリアージ」の欄は「Yes」です。CISAの警告、PHP Foundationの告知、BleepingComputerの記事を読みました。 修正前のPHPが入っている機器を、診断レポートが自分から知らせてくれるのか。Debian 12と公式のDockerイメージで試しました。診断はRoamSwitchのLinux版にある項目で、ネットワークにつながないコンテナ(--network none)の中で動かしています。 修正版からKEV入りまで 日時 出来事 10月17日ごろ 攻撃の試みが始まる。BleepingComputerが伝えたAmazonの報告で、公開の4日前。成功は確認されていない 10月21日 0時ごろ(日本時間) CVE番号が出回る 10月21日 16時ごろ(日本時間) 修正を含むビルド済みのイメージが、先に公開される 10月21日 23時(日本時間。14:00 UTC) 修正版 8.5.12、8.4.27、8.3.36、8.2.35 10月24日 ハニーポットで、Apache mod_phpのPHP 8.4.26に対する遠隔コード実行が成功。証拠データの日付 10月26日 KEVカタログに追加 10月29日 連邦機関の対応期限 修正版が出てからKEV入りまでは5日間でした。この間に、人が調べて回らなくても、手元の機器が該当するかどうかが分かるのかを試しました。 診断の条件 診断レポートの項目「悪用される恐れの高い脆弱性 (入っているソフトウェア)」は、入っているOSパッケージを、手元にある既知CVEのデータと突き合わせます。次のどちらかに当たる脆弱性が未修正のまま残っていると、不合格になります。 CISAが悪用を確認した脆弱性(KEV) 悪用される確率の推定値(EPSS)が0.9以上 通信はしません。管理者がリスクの受け入れを記録した脆弱性は、数えません。 データはソースパッケージ名で書かれている Debianのセキュリティデータは、バイナリパッケージではなく、元になったソースパッケージの名前で書かれています。修正前のphp8.2-cliを入れた機器で見てみます。 $ apt-cache policy php8.2-cli php8.2-cli: Candidate: 8.2.35-1~deb12u1 Version table: 8.2.35-1~deb12u1 500 500 deb.debian.org bookworm-security/main 8.2.32-1~deb12u1 500 500 deb.debian.org bookworm/main 出力はアーキテクチャの表記を省いています。修正版の8.2.35はセキュリティ更新のリポジトリに、修正前の8.2.32は通常のリポジトリにあります。8.2.32は、php8.2-common、php8.2-opcache、php8.2-readlineも同じ版にそろえて入れました。 $ dpkg-query -W -f='${binary:Package} ${source:Package} ${Version}\n' php8.2-cli php8.2-cli php8.2 8.2.32-1~deb12u1 バイナリ名はphp8.2-cliで、ソース名はphp8.2です。データ側のCVE-2026-109618は、こう書かれています。 {"cveId":"CVE-2026-109618","package":"php8.2","cvssScore":9.8, "ranges":[{"introduced":"0","fixed":"8.2.35-1~deb12u1"}],"epss":0.99981} packageはphp8.2なので、バイナリ名のphp8.2-cliのままでは突き合わせられません。診断は、dpkgが持っているソース名とソースの版で照合します。データにはdeb12u1、deb13u1のように、リリースごとの修正版が並んでいます。Debian 12の機器では、deb12uの修正版だけを見ます。 修正前は不合格、修正版は合格 コンテナにはデーモンがないので、診断レポートはMCP(get_security_report)から取りました。修正前の8.2.32の結果です。 悪用の恐れが高い脆弱性が2件あります (php8.2 CVE-2026-109619, php8.2 CVE-2026-109618) scan-packagesにはCVE-2026-17543(CVSS 9.8、EPSS 0.3%)も出ますが、KEVでもEPSS 0.9以上でもないので、項目の件数には入りません。 修正版の8.2.35を入れて同じように診断すると、項目は合格になり、scan-packagesにもPHPは出ません。 該当なし (データ 2026-10-26 版、KEVの一覧は2026-10-27版) こちらは0.34秒でした。 データのKEV印は遅れる 手元のデータは10月26日版ですが、中のKEV印は10月23日時点のカタログから付けたものです(kevDateが2026-10-23)。CISAが2件を加えたのは26日なので、データだけで照合すると、scan-packagesの2件にKEVの表示は付きません。 CVE-2026-109618 [Debian] php8.2 8.2.32-1~deb12u1 (CVSS 9.8) EPSS 100% (2026-10-25) (悪用が確認された数字ではなく、悪用される見込みの目安です) 修正版: 8.2.35-1~deb12u1 それでも項目が不合格になるのは、EPSSが0.9998あるからです。EPSSを条件に入れているのは、KEV印が遅れる間を埋めるためです。 KEVの一覧だけは、データ本体とは別に毎日配っています。CVE番号だけを集めた署名つきのファイルで、CISAのカタログから作ると1,760件、約29KBでした。10月27日版を置いてscan-packagesをもう一度実行すると、2件にKEVの印が付きました。 CVE-2026-109618 [Debian] php8.2 8.2.32-1~deb12u1 (CVSS 9.8) KEV 米国CISAが、実際に悪用されていることを確認した脆弱性です。 EPSS 100% (2026-10-25) (悪用が確認された数字ではなく、悪用される見込みの目安です) パッケージ管理の外のPHP BleepingComputerが伝えた環境は、公式のDockerイメージと同じ、パッケージ管理の外に入れたPHPです。 $ docker run --rm php:8.4.26-apache php -v PHP 8.4.26 (cli) (built: Oct 21 2026 00:32:44) (NTS) dpkgは、このPHPを知りません。診断は、PATHや/usr/local/binなどにあるPHPの実行ファイルから、版を直接読みます。Apacheのモジュール(libphp.so)は、ファイルに埋め込まれたX-Powered-By: PHP/8.4.26という文字列から版を読みます。脆弱性のデータが何もない状態でも、結果はこうなりました。 悪用の恐れが高い脆弱性が1件あります (php 8.4.26 (mod_php)) 修正版のphp:8.4.27-apacheは、データを置くと「該当なし」になりました。データがないときは「確認不能 (パッケージの脆弱性データがまだありません)」と出ます。確認できなかったものを、問題なしとは言いません。 結果 入力 データ 診断の結果 Debian 12、php8.2 8.2.32 脆弱性データのみ 不合格。php8.2の2件。EPSSの条件で検出 Debian 12、php8.2 8.2.32 脆弱性データと毎日のKEV一覧 不合格。同じ2件に、scan-packagesでKEVの印 Debian 12、php8.2 8.2.35 脆弱性データと毎日のKEV一覧 該当なし php:8.4.26-apache なし 不合格。php 8.4.26 (mod_php) php:8.4.27-apache 脆弱性データと毎日のKEV一覧 該当なし php:8.4.27-apache なし 確認不能(合格扱い。問題なしとは言わない) この項目が見るのは、データに載っている脆弱性と、版を直接読めるPHPです。データに載っていない脆弱性と、パッケージ管理の外に入れたPHP以外のソフトウェアは、見えません。

  3. Security companies have observed millions of attempts to exploit the php2shell vulnerability. Now there are first reports of successful attacks that started shells and executed commands on honeypot hosts. github.com/0x4D31/honeypot-data/blob/main/reports…

  4. TLS наизнанку: от рукопожатия TLS 1.2/1.3 до Fingerprinting (JA3/JA4/http2) и маскировки с uTLS Привет, Habr! Задумывались ли вы когда‑нибудь, почему ваш написанный парсер, который досконально копирует заголовки браузера, вдруг наглухо блокируется защитой сайта уже на втором запросе? Вроде бы и User‑Agent правильный, и прокси качественные, а сервер всё равно отвечает: Ты бот и сверху еще накидывает капчу. Каждый раз, когда браузер открывает страницу, приложение обращается к API или очередной парсер собирает данные, почти всегда происходит один и тот же сценарий — устанавливается защищённое соединение по TLS. Мы привыкли воспринимать его как нечто само собой разумеющееся: браузер проверил сертификат, договорился с сервером о шифровании, и дальше можно спокойно передавать данные. Но есть один нюанс: шифрование содержимого трафика вовсе не означает, что клиент остается невидимым для сервера. Ещё до того, как приложение отправит первый HTTP‑запрос, клиент успевает рассказать о себе достаточно много. Набор поддерживаемых шифров, порядок TLS‑расширений, параметры криптографических групп и даже особенности последующего общения по HTTP/2 формируют своеобразный цифровой почерк. По нему современные WAF и антифрод‑системы способны отличать обычный браузер от скрипта, консольного curl'а или самописного клиента на Go. В этой статье разберемся, как устроено TLS‑рукопожатие, чем отличаются версии TLS 1.0 и 1.3 и почему даже переход на более современную криптографию не избавил клиентов от сетевого снифинга. Посмотрим, как работают алгоритмы JA3 и JA4, почему одного TLS‑отпечатка недостаточно и какую дополнительную информацию можно получить из HTTP/2. А затем перейдем к практике: разберемся, как управлять TLS‑отпечатком с помощью uTLS в Go и как использовать Finch для анализа и фильтрации входящего трафика. Историческая справка: от амбиций Netscape до устройства современных шифров TLS В середине 90-х интернет перестал быть игрушкой для академиков и начал стремительно превращаться в глобальный маркетплейс. Главной проблемой зарождающейся электронной коммерции была абсолютная прозрачность сети: данные кредитных карт и пароли летели по HTTP в открытом виде, доступные любому промежуточному маршрутизатору. Понимая, что без приватности бизнес не взлетит, инженеры компании Netscape под руководством Тахера Эльгамала в 1994 году сели проектировать решение. Их первая попытка, протокол SSL 1.0, содержала столько криптографических детских болезней (вроде уязвимостей к атакам повтора), что её даже не решились выпустить в паблик. Дебютировавший в 1995 году SSL 2.0 добрался до релиза в браузере Netscape Navigator 1.1, но тоже оказался дырявым как решето. Только к версии SSL 3.0 (1996 год) архитектуру удалось довести до ума. Позже консорциум IETF взял SSL 3.0 за основу, доработал хеш‑функции для генерации ключей и в 1999 году представил миру независимый открытый стандарт TLS 1.0 (технологически это был SSL 3.1). Так началась эпоха транспортной безопасности: именно эта эволюция заложила фундамент для криптографического рукопожатия, которое сегодня защищает триллионы терабайт мирового трафика. Как эллиптические кривые спасли процессоры В эпоху раннего TLS всем заправляла классическая асимметричная криптография RSA — именно она отвечала за проверку подлинности сервера и генерацию общих ключей. Чтобы защитить сессию, устройствам приходилось ворочать огромными простыми числами. Проблема обнаружилась в конце 2000-х с приходом эпохи смартфонов: мобильный интернет рос, а слабенькие процессоры первых гаджетов буквально плавились от необходимости высчитывать огромные 2048-битные ключи. В этот момент как раз и вышли эллиптические кривые (ECC). Их математика заключалась в том, что кривая вида обеспечивала ту же стойкость, но при экстремально короткой длине ключа. Например, скромный 256-битный ключ популярной кривой X25519 давал защиту уровня RSA-3072. Переход на ECC в связке с алгоритмами класса ECDHE не только снизил нагрузку на сервера и мобильные чипы, но и подарил интернету прямую секретность. Поскольку интернет состоит из миллионов абсолютно разных устройств — от древних банкоматов до ультрасовременных серверов — TLS проектировался, как гибкий конструктор. Чтобы клиент и сервер поняли друг друга, инженеры придумали наборы шифров. Это длинные строки‑инструкции, собиравшиеся как комбо‑обеды в KFC. Например, в шифре TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 прописывалось: за безопасное согласование первичного секрета отвечает ECDHE протокол на эллиптических кривых, подлинность самого сервера всё ещё заверяется старым добрым RSA, а трафик шифруется и проверяется на целостность с помощью AES‑GCM. А SHA256 работает как контролер, он генерирует общие ключи шифрования и защищает сам процесс подключения от вмешательства извне. Эволюция рукопожатий: от TLS 1.0 к TLS 1.3 Чтобы понять, как современные антифрод‑системы вычисляют пользователей по первому сетевому пакету, нужно разобрать логику взаимодействия браузера и сервера на транспортном уровне L4 OSI. Каждое поколение протоколов пыталось решить две задачи: сделать соединение безопасным и снизить задержки. В эпоху TLS 1.0–1.2 процесс рукопожатия требовал двух полных кругов обмена данными (2 RTT). Браузер отправлял пакет Client Hello со списком поддерживаемых версий, шифронаборов и расширений. Сервер выбирал комбинацию в Server Hello, прикладывал свой цифровой сертификат и отправлял параметры для генерации ключей. Только после второго круга согласования включалось шифрование. Server Hello ответ с параметрами при использовании TLS 1.2 Список поддерживаемых сервером криптографических шифров У этой схемы было две проблемы. Первая — высокие задержки из‑за лишних сетевых запросов. Вторая — избыточная открытость: сертификат сервера, расширения браузера и методы сжатия передавались в чистом виде. Любой пассивный сниффер мог изучить параметры сессии до включения криптозащиты. Кроме того, отсутствие Perfect Forward Secrecy позволяло в будущем расшифровать исторический трафик при компрометации приватного ключа. Стандарт TLS 1.3 (2018 год) сократил процедуру знакомства до одного круга (1 RTT) и убрал десятки устаревших шифров, оставив пять надежных вариантов на базе AEAD. Теперь браузер сразу отправляет в Client Hello заготовки ключей для популярных криптографических групп, а сервер принимает одну из них в ответном пакете. При повторном визите механизм сессионных тикетов (0-RTT) позволяет отправлять зашифрованный HTTP‑запрос сразу в первом пакете. В качестве примера посмотрим на Client Hello при обращении к GitHub через Wireshark: Весь байтовый пакет Client Hello TLS 1.3 с содержимым полей и расширений Список поддерживаемых клиентом криптографических шифров Расширение SNI используется для маршрутизации запросов к веб‑серверу В TLS 1.3 все сообщения сервера, включая сертификат сайта, шифруются сразу после получения заготовки ключа от клиента. Для внешнего наблюдателя открытым остается только первое приветствие Client Hello. Поскольку Cloudflare, WAF и антифрод‑платформы лишились возможности анализировать сертификаты и последующие этапы хэндшейка, они сфокусировались на единственной видимой детали. Структура, порядок шифров и расширения в незашифрованном пакете Client Hello превратились в уникальный цифровой почерк, по которому сегодня и вычисляют ботов. Цифровой почерк: анатомия TLS‑фингерпринтинга (JA3 и JA4) Поскольку в TLS 1.3 все последующие этапы рукопожатия скрыты за криптографией, пакет Client Hello превратился в главный маркер идентификации. Идея TLS‑фингерпринтинга проста: каждый сетевой стек — будь то движок в Google Chrome, библиотека curl, Python‑пакет requests или кастомный троян на Go — формирует это первое сообщение по‑своему. Порядок шифронаборов, состав расширений и поддерживаемые форматы эллиптических кривых жестко зашиты в коде клиента. Для защитных систем это идеальный маркер. Зачем анализировать поведение бота, если его можно срезать на дальних подступах просто потому, что его сетевой стек заявляет себя как Chrome в HTTP‑заголовках, но здоровается в TLS как старый скрипт на Python? Долгое время абсолютным стандартом в этой области оставался алгоритм JA3, разработанный инженерами Salesforce в 2017 году. Механика JA3 простая до гениальности: из сырого пакета Client Hello извлекаются строго пять параметров в определенном порядке — версия TLS, список поддерживаемых шифронаборов (Cipher Suites), список расширений (Extensions), поддерживаемые эллиптические кривые (Supported Groups) и форматы эллиптических кривых (Elliptic Curve). Значения внутри каждой категории записываются через запятую, а сами категории разделяются дефисом. Полученная текстовая строка прогоняется через хеш‑функцию MD5. Этот финальный 32-символьный хеш и есть отпечаток JA3. Если WAF видит запрос к эндпоинту авторизации с хешем, который гуглится как стандартный фингерпринт утилиты sqlmap, сессия мгновенно дропается. [JA3 Fullstring: 771,4865-4866-4867-49195-49199-49196-49200-52393-52392-49171-49172-156-157-47-53,51764-16-65281-35-23-45-0-65037-13-5-11-43-51-27-10-17613-18-41,4588-29-23-24,0] [JA3: 2a91540461ce04bac4c1c785ce9fa811]sourcecraft.dev Однако у JA3 со временем вскрылись серьезные архитектурные проблемы, которые для TLS 1.3 стали критическими. Первая беда — хрупкость: поскольку JA3 учитывает абсолютно все расширения и шифры в том порядке, в котором их выплюнул клиент, любое минимальное изменение (например, если браузер обновился и добавил одно новое расширение в конец списка) полностью меняет MD5-хеш. Вторая и главная проблема — инициатива Google по рандомизации TLS (GREASE). Generate Random Extensions And Sustain Extensibility стандарт RFC 8701 Чтобы вендоры сетевого оборудования не затачивали свои парсеры под конкретные байты, современные браузеры при каждом новом рукопожатии намеренно подмешивают в Client Hello случайные мусорные шифры и расширения из специального диапазона. Для JA3 это стало катастрофой: один и тот же легитимный Chrome при пяти перезагрузках страницы генерировал пять абсолютно разных MD5-отпечатков, что сделало классические черные списки малоэффективными против умных клиентов. Если открыть Wireshark и запустить захват трафика, вы своими глазами увидите, как отпечатки JA3 скачут от сессии к сессии на одном и том же устройстве. [JA3 Fullstring: 771,4865-4866-4867-49195-49199-49196-49200-52393-52392-49171-49172-156-157-47-53,51-13-10-65037-23-43-45-27-0-16-65281-35-5-11-51764-17613-18-41,4588-29-23-24,0] [JA3: 7420fb81d80ea49cb26bf1b355881c06]sourcecraft.dev Чтобы окончательно понять масштаб катастрофы, достаточно взглянуть на реальные отпечатки Client Hello одного и того же браузера, собранные с разницей в несколько секунд. В первой сессии сырая строка параметров выглядит как 771,4865-4866-4867-49195-49199-49196-49200-52393-52392-49171-49172-156-157-47-53,51-13-10-65037-23-43-45-27-0-16-65281-35-5-11-51764-17613-18-41,4588-29-23-24,0, что после хеширования дает строгий MD5-отпечаток 7420fb81d80ea49cb26bf1b355881c06. Но стоит пользователю просто обновить страницу, как тот же самый сетевой стек генерирует уже другую строку: 771,4865-4866-4867-49195-49199-49196-49200-52393-52392-49171-49172-156-157-47-53,51764-16-65281-35-23-45-0-65037-13-5-11-43-51-27-10-17613-18-41,4588-29-23-24,0. При неизменных шифрах и эллиптических кривых третья группа — список расширений — полностью перетасовалась, а на первое место вылезло число 51764. В шестнадцатеричной системе это 0xCAAC, типичный динамический мусор из зарезервированного пула GREASE. Из‑за этого минимального шума на выходе получается абсолютно новый хеш 2a91540461ce04bac4c1c785ce9fa811. Для любого классического WAF эти два запроса выглядят как посещения от двух совершенно незнакомых и никак не связанных между собой клиентов, что полностью обнуляет эффективность старых баз сигнатур. Новый стандарт: JA4+ На смену устаревающему стандарту пришло семейство алгоритмов JA4+, анонсированное в 2023 году. JA4 кардинально меняет логику фингерпринтинга, делая отпечаток не просто слепым хешем, а легко читаемой человеком строкой, устойчивой к GREASE‑рандомизации. Базовый отпечаток клиента JA4 состоит из трех текстовых блоков, разделенных нижними подчеркиваниями, каждый из которых отвечает за свой уровень абстракции. Первый блок, JA4_A, собирает мягкие свойства в виде 10-символьного кода. Первый символ обозначает тип транспорта (t для TCP, q для QUIC, d для DTLS), следующие два — версию TLS (например, 13). Затем идет один символ присутствия SNI (d — указан домен, i — указан IP/отсутствует). Далее фиксируются количество шифров (2 цифры), количество расширений (2 цифры) и два символа, взятые из первого и последнего знака протокола ALPN (например, h2 для HTTP/2 или 00, если ALPN нет). Важно, что из этого блока полностью исключены все GREASE‑значения, благодаря чему общая структура пакета остается стабильной при любых условиях. Второй блок, JA4_B, отвечает за хеш шифров. Здесь берется список всех поддерживаемых Cipher Suites (опять же, с автоматическим вырезанием случайных GREASE‑байтов), сортируется по возрастанию номеров и хешируется по алгоритму SHA-256, от которого в финальный отпечаток попадают первые 12 символов. Сортировка — это решение проблемы, когда клиенты меняют порядок шифров ради оптимизации, намертво ломая старый JA3. Наконец, третий блок, JA4_C, обрабатывает список расширений: они так же сортируются по возрастанию без учета GREASE (за исключением SNI и ALPN), а затем к ним добавляются алгоритмы подписи в их исходном порядке. Полученная строка превращается во второй 12-символьный SHA-256 хеш. [JA4: t13d1518h2_8daaf6152771_e2d80978ab2e] [JA4_r: t13d1518h2_002f,0035,009c,009d,1301,1302,1303,c013,c014,c02b,c02c,c02f,c030,cca8,cca9_0005,000a,000b,000d,0012,0017,001b,0023,0029,002b,002d,0033,44cd,ca34,fe0d,ff01_0904,0905,0906,0403,0804,0401,0503,0805,0501,0806,0601]sourcecraft.dev Разберем реальный отпечаток выше и его сырой формат: В блоке A (t13d1518h2) мы сразу считываем контекст соединения: клиент работает по протоколу TCP (t), использует TLS 1.3 (13), указал имя домена в SNI (d), предлагает ровно 15 шифров и 18 расширений (без учета GREASE), а также планирует общаться по протоколу HTTP/2 (h2). В блоке B (8daaf6152771) скрывается SHA-256 хеш от списка шифров. Благодаря предварительной сортировке по возрастанию, этот хеш останется неизменным, даже если клиент намеренно перемешает их порядок. В блоке C (e2d80978ab2e) зашифрован список TLS‑расширений, предварительно очищенный от SNI и ALPN и отсортированный по возрастанию, к которому в хвост приклеены алгоритмы подписи (0904,0905...0601) в их первоначальном, родном порядке. В то время как JA3 легко обходится разработчиками ботов с помощью простой перетасовки расширений, меняющей итоговый MD5-хеш, протокол JA4 позволяет защитным системам действовать гораздо гибче. Даже если автоматизированный скрипт попытается перемешать расширения или подмешать случайные байты, блок JA4_A и предварительно отсортированные блоки B и C останутся идентичными оригинальному браузеру. Более того, системы анализа теперь могут блокировать клиентов избирательно: например, пропускать трафик, если блоки B и C валидны для Chrome, но дропать сессию, если блок JA4_A показывает, что клиент пытается работать по HTTP/1.1 там, где реальный браузер обязан использовать HTTP/2. P.S если лень качать wireshark и настраивать его, то есть сайт для просмотра своего tls отпечатка браузера. Там же можно тестировать свои реализации uTLS или скриптов на разных эндпоинтах: Да, вставка мелкая, но всегда можно перейти по ссылке выше Анатомия HTTP/2-фингерпринтинга После успешного TLS‑рукопожатия общение клиента и сервера переходит на прикладной уровень. Для современных сайтов это практически всегда означает переход на протокол HTTP/2. На этом этапе защитные системы получают второй эшелон идентификации — h2-фингерпринтинг. Боту недостаточно просто сымитировать TLS‑приветствие браузера, ему нужно без ошибок воспроизвести механику его HTTP‑движка. В отличие от текстового HTTP/1.1, бинарный протокол HTTP/2 работает через фреймы. Сразу после открытия сессии клиент обязан отправить серверу стартовые пакеты конфигурации. Порядок этих фреймов, лимиты и приоритеты потоков зависят от конкретной сетевой библиотеки. Зачем злоумышленнику тратить колоссальные ресурсы на идеальную подделку TLS‑отпечатка JA4, если его скрипт выдаст себя на первом же HTTP/2-кадре, отправив настройки, которые реальный Google Chrome не использует уже несколько лет? Долгое время стандартом для анализа оставался алгоритм HTTP2-fingerprint разработанный инженерами Akamai и Salesforce, который собирает хеш из четырех ключевых параметров бинарной сессии: Akamai HTTP/2 fingerprint MD5-хеш 52d84b11737d980aef856699f885ca86 Фрейм SETTINGS 1:65536;2:0;4:6291456;6:262144|15663105|0|m,a,s,psourcecraft.dev SETTINGS_Frames (1:65536;2:0;4:6291456;6:262144): Список пар key:value из стартового фрейма настроек. Здесь клиент указывает свои лимиты: например, максимальное количество параллельных потоков или начальный размер окна отправки. Каждый параметр имеет свой ID, итоговая строка формируется как ID:значение, разделенные точкой с запятой. В данном примере клиент заявляет размер таблицы сжатия HPACK в 65 536 байт (1:65536), отключает Server Push (2:0), задает начальное окно потока в 6 МБ (4:6291456) и максимальный размер списка заголовков в 256 КБ (6:262144). WINDOW_UPDATE_Frame (15663105): Значение окна из фрейма управления потоком, который клиент отправляет следом, чтобы задать размер буфера для всего соединения (в примере — около 15 МБ). PRIORITY_Frames (0): Параметры приоритезации потоков трафика, где указываются ID зависимых потоков и их веса. Если фрейм отсутствует или механизмы приоритетов не задействованы в явном виде, ставится 0 или пустая строка. Pseudo‑Header Order(m,a,s,p): Порядок следования служебных псевдо‑заголовков HTTP/2. Буквенные маркеры заголовков записываются через запятую. Последовательность m,a,s,p расшифровывается по первым буквам: :method, :authority, :scheme, :path. Браузеры на движке Chromium всегда отправляют их именно в таком порядке, в то время как Firefox или кастомные сетевые библиотеки используют другие комбинации. Полученная строка хешируется через MD5, формируя уникальный 32-символьный отпечаток сессии. Если WAF фиксирует легитимный TLS‑отпечаток под Windows‑браузер, но привязанный к нему HTTP/2-хеш совпадает со специфичным почерком консольной утилиты curl или библиотеки Hyper, запрос классифицируется как попытка спуфинга и блокируется. Однако, классический h2-фингерпринтинг привязан исключительно к бинарным фреймам сессии, параметры которые браузеры регулярно оптимизируют, что плодит сущности в базах сигнатур. В ответ на это для детального анализа самих HTTP‑запросов был представлен модуль JA4H (JA4 HTTP). Вместо фреймов, JA4H раскладывается на четкие блоки и хеширует контекст: метод запроса, версию протокола, точное количество текстовых заголовков (не считая Cookie и Referer), их алфавитный порядок и наличие специфических маркеров. Комбинация TLS и HTTP/2-фингерпринтинга создает для WAF мощный двухуровневый фильтр: автоматизированным скриптам уже недостаточно просто подменить User‑Agent — им приходится полностью воссоздавать поведение реального браузера от криптографии TLS 1.3 до структуры бинарных фреймов. Однако для современных защитных систем уровня Cloudflare эти технические отпечатки (включая JA4) служат лишь базовым фундаментом. Анализируя колоссальные объемы глобального трафика, продвинутые фильтры обогащают фингерпринты поведенческим анализом на базе ИИ. Они в реальном времени оценивают репутацию IP, скорость запросов и сетевые задержки, поэтому даже идеально сэмулированный отпечаток будет мгновенно заблокирован, если его контекстное поведение не совпадает с паттернами живого пользователя. Маскировка с помощью uTLS Почему стандартный net/http выдаёт бота? Представим типичную ситуацию: мы пишем парсер на Go, добавляем в запрос заголовки User-Agent, Accept, Accept-Language и другие атрибуты, характерные для Google Chrome. На первый взгляд запрос выглядит вполне убедительно. Однако после отправки через стандартный net‑ или http‑сервер может мгновенно определить, что перед ним вовсе не браузер. Причина кроется в том, что HTTP‑заголовки и TLS‑отпечаток формируются на разных уровнях. Заголовок User-Agent — это всего лишь строка, которую клиент передает серверу на уровне L7 HTTP. Она никак не влияет на то, какие шифронаборы, расширения и криптографические группы будут отправлены в приветственном сообщении Client Hello. Стандартная библиотека Go использует собственную реализацию TLS (crypto/tls), которая формирует рукопожатие в соответствии со своей внутренней логикой. В результате возникает критическое несоответствие: на уровне HTTP клиент притворяется Chrome, а на уровне TLS демонстрирует характерный отпечаток Go. Управляем TLS‑рукопожатием Библиотека uTLS — это форк и расширение реализации TLS для Go, основанное на crypto/tls, но предоставляющее низкоуровневый контроль над сообщением Client Hello. Её ключевая особенность — возможность использовать заранее подготовленные профили популярных клиентов, включая Chrome, Firefox и Safari. Вместо того чтобы вручную собирать каждый параметр рукопожатия, разработчик выбирает готовый пресет, который полностью имитирует структуру TLS‑приветствия нужного браузера. На практике это означает, что мы полностью заменяем стандартное поведение Go на логику Chrome ещё до отправки первого TLS‑пакета. Библиотека сформирует список шифронаборов и расширений в соответствии с выбранным пресетом. Самая критическая ошибка при работе с TLS‑мимикрией — настроить пресет один раз и забыть о нем. Браузеры постоянно обновляются, а вместе с ними обновляются и их TLS‑отпечатки. Разработчики Chromium регулярно модифицируют свой криптографический стек: внедряют шифрование SNI (Encrypted Client Hello / ECH), делают обязательной поддержку постквантовых алгоритмов (таких как ML‑KEM / X25519MLKEM768), отказываются от устаревших шифронаборов или меняют логику работы TLS‑паддинга. Отправляем запрос с профилем Chrome Рассмотрим пример, в котором мы перехватываем создание TLS‑соединения и устанавливаем его самостоятельно с помощью uTLS: Данный код приводится только в учебных целях и он далек от хороших реализацийpackage main import ( "context" "fmt" "io" "log" "net" "strings" "time" http "github.com/useflyent/fhttp" utls "github.com/refraction-networking/utls" ) func main() { fmt.Println("=== Выберите эндпоинт для проверки отпечатка ===") fmt.Println("1 - All of the information (/api/all)") fmt.Println("2 - Only fingerprints (/api/clean)") fmt.Println("3 - Only TLS data (/api/tls)") fmt.Print("Введите цифру (1-3): ") var choice int _, err := fmt.Scanf("%d", &choice) if err != nil || choice < 1 || choice > 3 { log.Fatal("Некорректный ввод. Введите число от 1 до 3.") } var path string switch choice { case 1: path = "/api/all" case 2: path = "/api/clean" case 3: path = "/api/tls" } baseURL := "tls.peet.ws" fullURL := baseURL + path transport := &http.Transport{ DialTLSContext: func(ctx context.Context, network, addr string) (net.Conn, error) { host, _, err := net.SplitHostPort(addr) if err != nil { return nil, err } rawConn, err := (&net.Dialer{ Timeout: 10 * time.Second, }).DialContext(ctx, network, addr) if err != nil { return nil, err } config := &utls.Config{ ServerName: host, // Указываем h2 для ALPN-согласования на уровне TLS NextProtos: []string{"h2", "http/1.1"}, } // Используем профиль автоматического подбора отпечатка Chrome conn := utls.UClient(rawConn, config, utls.HelloChrome_Auto) if err := conn.HandshakeContext(ctx); err != nil { conn.Close() return nil, err } return conn, nil }, TLSHandshakeTimeout: 5 * time.Second, MaxIdleConns: 100, IdleConnTimeout: 10 * time.Second, ExpectContinueTimeout: 1 * time.Second, ForceAttemptHTTP2: true, } client := &http.Client{ Transport: transport, Timeout: 25 * time.Second, } req, err := http.NewRequest(http.MethodGet, fullURL, nil) if err != nil { log.Fatalf("Ошибка при создании HTTP-запроса: %v", err) } req.Header = http.Header{ "cache-control": {"no-cache"}, "upgrade-insecure-requests": {"1"}, "user-agent": {"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"}, "accept": {"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,/;q=0.8,application/signed-exchange;v=b3;q=0.7"}, "accept-language": {"ru-RU,ru;q=0.9,en-US;q=0.8,en;q=0.7"}, "priority": {"u=0, i"}, // Задаем порядок обычных заголовков http.HeaderOrderKey: { "cache-control", "upgrade-insecure-requests", "user-agent", "accept", "accept-language", "priority", }, // Задаем порядок псевдо-заголовков http.PHeaderOrderKey: { ":method", ":path", ":authority", ":scheme", }, } fmt.Printf("\nОтправка запроса на %s...\n", fullURL) resp, err := client.Do(req) if err != nil { log.Fatalf("Не удалось выполнить запрос: %v", err) } defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { log.Fatalf("Не удалось прочитать тело ответа: %v", err) } fmt.Println("\n=== СЕТЕВАЯ ДИАГНОСТИКА ===") fmt.Println("Статус ответа сервера:", resp.Status) fmt.Println("Согласованный протокол:", resp.Proto) if strings.Contains(resp.Header.Get("Content-Type"), "application/json") { fmt.Println("\nДанные отпечатка от сервера (JSON):") fmt.Println(string(body)) } else { fmt.Println("\nТело ответа (не JSON):") fmt.Println(string(body)) } } sourcecraft.dev Уникальный TLS‑fingerprint скрипта выше=== Выберите эндпоинт для проверки отпечатка === 1 - All of the information (/api/all) 2 - Only fingerprints (/api/clean) 3 - Only TLS data (/api/tls) Введите цифру (1-3): Отправка запроса на tls.peet.ws... === СЕТЕВАЯ ДИАГНОСТИКА === Статус ответа сервера: 200 OK Согласованный протокол: HTTP/2 Данные отпечатка от сервера (JSON): { "ja3": "771,4865-4866-4867-49195-49199-49196-49200-52393-52392-49171-49172-156-157-47-53,45-11-27-51764-13-51-65281-35-23-0-65037-10-16-18-17613-5-43-41,4588-29-23-24,0", "ja3_hash": "efa0c28162e6cccca786c71779268784", "ja4": "t13d1518h2_8daaf6152771_e2d80978ab2e", "ja4_r": "t13d1518h2_002f,0035,009c,009d,1301,1302,1303,c013,c014,c02b,c02c,c02f,c030,cca8,cca9_0005,000a,000b,000d,0012,0017,001b,0023,0029,002b,002d,0033,44cd,ca34,fe0d,ff01_0904,0905,0906,0403,0804,0401,0503,0805,0501,0806,0601", "akamai": "1:65536;2:0;4:6291456;6:262144|15663105|0|m,a,s,p", "akamai_hash": "52d84b11737d980aef856699f885ca86", "peetprint": "GREASE-772-771|2-1.1|GREASE-4588-29-23-24|GREASE-2308-2309-2310-1027-2052-1025-1283-2053-1281-2054-1537|1|2|GREASE-4865-4866-4867-49195-49199-49196-49200-52393-52392-49171-49172-156-157-47-53|0-10-11-13-16-17613-18-23-27-35-41-43-45-5-51-51764-65037-65281-GREASE-GREASE", "peetprint_hash": "5fa343c29062ede7d0e28fd46c1052a7", "http_version": "h2" }sourcecraft.dev Сначала открывается базовое TCP‑соединение, а библиотека uTLS маскирует его под отпечаток браузера Chrome с помощью профиля HelloChrome_Auto, безопасно выполняя TLS‑рукопожатие с сохранением проверки сертификатов и SNI. Затем управление передаётся специализированной библиотеке fhttp, которая заменяет стандартный HTTP‑транспорт Go. Благодаря флагу ForceAttemptHTTP2, она автоматически переключает соединение на протокол HTTP/2 и позволяет вручную настроить точный порядок обычных заголовков и псевдо‑заголовков (:method, :path и так далее) Как пример ещё приведу уникальный отпечаток curl для mac: ~ curl tls.peet.ws { "ja3": "771,4867-4866-4865-52393-52392-52394-49200-49196-49192-49188-49172-49162-159-107-57-65413-196-136-129-157-61-53-192-132-49199-49195-49191-49187-49171-49161-158-103-51-190-69-156-60-47-186-65-49169-49159-5-4-49170-49160-22-10-255,43-51-0-11-10-13-16,29-23-24-25,0", "ja3_hash": "375c6162a492dfbf2795909110ce8424", "ja4": "t13d497h2_0d8feac7bc37_7395dae3b2f3", "ja4_r": "t13d497h2_0004,0005,000a,0016,002f,0033,0035,0039,003c,003d,0041,0045,0067,006b,0081,0084,0088,009c,009d,009e,009f,00ba,00be,00c0,00c4,00ff,1301,1302,1303,c007,c008,c009,c00a,c011,c012,c013,c014,c023,c024,c027,c028,c02b,c02c,c02f,c030,cca8,cca9,ccaa,ff85_000a,000b,000d,002b,0033_0806,0601,0603,0805,0501,0503,0804,0401,0403,0201,0203", "akamai": "3:100;4:10485760;2:0|1048510465|0|m,s,a,p", "akamai_hash": "64a832f547be33249bf4d33e8a46c5dc", "peetprint": "772-771-770-769|2-1.1|29-23-24-25|2054-1537-1539-2053-1281-1283-2052-1025-1027-513-515|0||4867-4866-4865-52393-52392-52394-49200-49196-49192-49188-49172-49162-159-107-57-65413-196-136-129-157-61-53-192-132-49199-49195-49191-49187-49171-49161-158-103-51-190-69-156-60-47-186-65-49169-49159-5-4-49170-49160-22-10-255|0-10-11-13-16-43-51", "peetprint_hash": "0d6ceceb9e71ec096d2022ad0651e2f2", "http_version": "h2" }sourcecraft.dev Собственный Client Hello Если готовых пресетов недостаточно, uTLS позволяет полностью контролировать структуру запроса через режим utls.HelloCustom. Можно вручную сформировать список TLS‑расширений и передать его библиотеке. Это необходимо для тестирования совместимости, анализа сетевого стека или эмуляции редких версий клиентов. Однако ручная сборка Client Hello требует глубокого понимания протокола. TLS‑расширения часто зависят друг от друга, а их параметры должны строго соответствовать общим настройкам криптографии. Случайная или некорректная комбинация элементов приведет к невалидному рукопожатию либо к аномалиям, которые легко детектируются сервером. Finch: фильтруем трафик по TLS‑отпечаткам До этого момента мы рассматривали фингерпринтинг преимущественно со стороны клиента. Но что, если нам нужно не просто посмотреть на JA3/JA4 в Wireshark, а использовать их для фильтрации входящего трафика? Для этого можно попробовать Finch — open‑source reverse proxy на Go от разрабочтика 0×4D31, который анализирует TLS‑рукопожатие и HTTP‑запросы, вычисляет JA3, JA4, JA4H и отпечатки HTTP/2, а затем принимает решения на основе заданных правил. По сути, это небольшой специализированный прокси с механизмом фильтрации по сетевым отпечаткам, а не полноценная замена крупным коммерческим WAF. Допустим, у нас уже есть приложение, работающее на 8080, а Finch должен принимать HTTPS‑соединения на 8443 и проксировать запросы к приложению. Запустить его в упрощенном режиме можно следующей командой: finch serve --listen :8443 --rules my.rules.hcl --upstream http://localhost:8080[https://sourcecraft.dev/](sourcecraft.dev) Для начала можно вообще не блокировать трафик, а посмотреть, какие отпечатки приходят на сервер. Для этого у Finch предусмотрен отдельный режим echo, который запускает тестовый сервер и возвращает информацию о клиенте и вычисленных фингерпринтах вместо проксирования запросов: finch echo --listen :8443sourcecraft.dev После запуска можно обратиться к /fp/, чтобы получить краткую JSON‑структуру с отпечатками, или к /fp/detail, чтобы посмотреть максимально подробные данные, включая параметры TLS ClientHello, заголовки и фреймы HTTP/2. Такой режим хорош для экспериментов: можно отправить запросы из Chrome, curl или кастомного скрипта, а затем сравнить, как их видит сервер. Но самое интересное начинается, когда мы переходим от наблюдения к фильтрации. Finch поддерживает правила в формате HCL, которые позволяют гибко обрабатывать запросы по JA3, JA4, HTTP‑отпечаткам, сигнатурам Suricata и IP‑адресам. Например, следующее правило демонстрирует жесткую блокировку клиента с известным подозрительным JA3 сканера ZMap: rule "block-scanner-ja3" { action = "deny" when { condition = tls.ja3 == "3518c438fe56bd7dba3f6e28example" } } sourcecraft.dev Если TLS‑отпечаток совпадет с указанным в списке значением, Finch применит действие deny и мгновенно отклонит запрос. Можно пойти дальше и учитывать сразу несколько признаков, комбинируя их. Например, правило может срабатывать, если совпал хотя бы один из подозрительных параметров трафика. Для этого используется блок when any: rule "suspicious-client" { action = "tarpit" when { any = [ tls.ja4 == "q13d0312h3_55b375c5d22e_c183556c78e2", http.ja4h == "bad-ja4h" ] } } sourcecraft.dev Действие tarpit предназначено для намеренного замедления подозрительных клиентов по аналогии с tarpit HAProxy. Один и тот же JA4 может встречаться у множества легитимных пользователей, а TLS‑профиль браузера со временем меняется. Поэтому правильнее сначала собирать статистику, изучать реальные значения и только потом добавлять адресные правила для подтвержденных подозрительных клиентов. У Finch есть так скажем нюанс: проект находится в активной разработке, а его текущая версия 0.1.0 прямо обозначена авторами, как экспериментальная и не готовая для полноценного релиза. Поэтому рассматривать его стоит прежде всего как интересный инструмент для исследований и изучения TLS‑фингерпринтинга. Будущее протокола: ECH и его отпечаток ECH (Encrypted Client Hello) — это утверждённый стандарт RFC 9849, который скрывает SNI и другие чувствительные расширения, например, ALPN от сторонних наблюдателей. При обычном TLS 1.3 домен передаётся открыто, чтобы сервер понимал, какой сайт нужно отобразить. ECH решает эту проблему: реальные параметры упаковываются в структуру ClientHelloInnerи шифруются с помощью гибридного шифрования HPKE, а наружу передаётся стандартизированная заглушка ClientHelloOuter. Хотя стандарт механически допускает преднастройку ключей в клиенте, на практике для безопасного получения структуры ECHConfig необходим зашифрованный DNS (DoH/DoT/DoQ) — иначе запрос утечёт ещё на этапе DNS. У меня как раз недавно вышла статья где в Nginx добавили поддержку ECH и как это все можно настроить. Для промежуточного сетевого фингерпринтинга на транзитных узлах это означает, что внешние наблюдатели больше не могут собирать корректные TLS‑отпечатки вроде JA3 или JA4. Во‑первых, они видят лишь стандартизированную внешнюю заглушку. Во‑вторых, даже если клиент не использует ECH, стандарт обязывает его применять механизм GREASE ECH (отправку пустых случайных ECH‑полей), благодаря чему реальные ECH‑сессии визуально не выделяются в общем потоке трафика и защищены от выборочных блокировок. Однако, ECH не скрывает анализ трафика полностью. Защитные системы вроде WAF внутри инфраструктуры по‑прежнему видят уже расшифрованные внутренние параметры рукопожатия. Внешние же провайдеры всё ещё могут анализировать IP‑адреса, размеры пакетов, тайминги, а также поведение протоколов HTTP/2 и HTTP/3 с их залповыми TCP/UDP‑соединениями, которые у скриптов‑ботов и реальных браузеров сильно различаются. В итоге современные системы обнаружения ботов окончательно переходят от простых статических TLS‑хешей к комплексному поведенческому анализу всей совокупности действий пользователя.

  5. > but these scanners did successfully exploit Adel Karimi's honeypot, which was a likely contributing factor to CISA’s decision to include PHP2Shell in their KEV list. Adel is doing an amazing job. Both in open source (perplexityai/bumblebee, perplexityai/numbat, 0x4D31/galah) and with his reports. So proud of him!

  6. This is a good read github.com/0x4D31/honeypot-data/blob/main/reports…

    1 like
  7. thanks for covering my findings (x.com)! but also, don’t panic. @UK_Daniel_Card has a good point: > successfully exploited in the wild *against a honeypot. dataset published here: github.com/0x4D31/honeypot-data/tree/main/reports…

    25 likes · 5 reposts · 1,608 views
  8. Confirmed exploitation of CVE-2026-109618? It might only be a honeypot but I would be cautious of 15.204.59[.]70 and 162.35.99[.]94 github.com/0x4D31/honeypot-data/blob/main/reports… #IOC #php2shell #php #eitw #threatintel

    1 comment
  9. @bd808 this *seems* legit: github.com/0x4D31/honeypot-data/blob/main/reports… Stealmybitcoin.com is running nginx, while some folks have reported apache instances being easier to pop. But like, log4shell was 100% successful on all systems and was […]

  10. L'informe complet github.com/0x4D31/honeypot-data/blob/main/reports… és a GitHub

    1 like · 1 repost

Achievements

Awards earned by the repositories 0x4D31 owns. Select an award to embed its badge.

No awards yet

Repositories earn awards by reaching the top three in a completed trending day or week.

A repository earns an award for finishing first, second or third in a completed trending day or week, ranked by new stars, new forks or pull request authors. Awards follow a repository through renames and transfers and count toward its current owner. Weeks run Monday to Sunday, UTC.