РАЗБОР
На проверке
Weird machines в сетевом стеке: парсеры DNS, TCP, HTTP
Режим чтения
[ обложка статьи ]
При фаззинге DNS-ответов в проприетарном TCP/IP-стеке промышленного контроллера через AFL++ с кастомным харнесом под функцию декомпрессии DNS-имён я обнаружил штуку, от которой сел перечитывать RFC 1035. Цепочка из трёх compression-указателей в одном ответе заставляла парсер пройти пять состояний - ни одно из них в спецификации не описано. Каждый двухбайтовый offset работал как инструкция безусловного перехода, а сам парсер превращался в программируемый автомат, управляемый содержимым сетевого пакета. Это и есть weird machine в сетевом стеке - скрытый вычислитель, который возникает в коде разбора протокола без ведома разработчика и программируется крафтованными пакетами атакующего. Русскоязычных материалов, связывающих теорию weird machines Bratus-Dullien именно с парсерами сетевых протоколов (не с ROP/JOP), на момент написания нет.
Парсер сетевого протокола как weird machine
Weird machine - вычислительный артефакт, где исполнение кода выходит за рамки исходной спецификации программы. В работах Bratus, Locasto, Patterson, Sassaman она определена как набор непредусмотренных состояний и переходов, активируемых при обработке специально подготовленного ввода. В ROP/JOP (подробно разобранном на Codeby) weird machine живёт в стеке вызовов или dispatch table, а «лентой» служат адреса гаджетов. В сетевых протоколах роль ленты берёт на себя сам пакет - его поля, указатели и значения длин.Почему сетевые парсеры - идеальная среда для нестандартных вычислений в сетевых пакетах?
Loose contracts по определению. Trail of Bits определяют loose contract как код, где множество допустимых предусловий шире необходимого. Сетевой парсер принимает байты из сети - от кого угодно, без аутентификации. Его входной контракт максимально loose: каждая непроверенная ветка
if, каждый пропущенный bounds check расширяет пространство состояний weird machine.Многослойность парсинга. TCP/IP-стек обрабатывает DNS, HTTP, FTP, ARP, ICMP, TFTP. По данным Forescout, TCP/IP-стеки «expose a significant attack surface that can often be exploited without authentication». Комбинаторика форматов умножает число парсерных состояний - и это ещё мягко сказано.
Достижимость до аутентификации. DNS-ответ обрабатывается стеком автоматически. TCP SYN принимается ядром без участия приложения. Результат - Exploitation for Client Execution (T1203) или Exploitation for Privilege Escalation (T1068) из сети, без предварительного доступа. Пакет прилетел - парсер уже работает.
Бизнес-логика атаки через парсерные weird machines
Зачем атакующему «программировать» парсер? Ответ простой - initial access без клика. На пентесте embedded-устройства (PLC, сетевой контроллер, IoT-шлюз) классический payload через фишинг не работает: нет ни пользователя, ни браузера. Единственный вход - сетевой пакет. Weird machine в DNS-парсере такого устройства превращает один крафтованный ответ в полный захват, минуя все уровни защиты прикладного слоя. Один пакет - и ты внутри.DNS-парсер эксплуатация: компрессия указателей как вычислительный примитив
Исследование Name:Wreck (Forescout, 2021) выявило девять уязвимостей парсеров DNS-имён в TCP/IP-стеках FreeBSD, IPNet, NetX и Nucleus NET. По данным Palo Alto Networks, уязвимости «mainly caused by improperly parsing the names in the DNS responses or during domain name record decompression». Атакующий в позиции DNS-сервера или MitM отвечает крафтованными ответами, эксплуатируя парсерную weird machine - декомпрессия выполняет переходы, не предусмотренные спецификацией, и достигается запись за пределы буфера.
В NicheStack (проприетарный стек, который использовали Siemens, Honeywell, Rockwell Automation) исследователи Forescout и JFrog нашли RCE-уязвимость CVE-2020-25928 в DNSv4-парсере с CVSS 9.8 - out-of-bounds read/write при разборе DNS-ответа. Weird machine в парсере позволяла из одного крафтованного пакета перезаписать стековый буфер контролируемыми данными. По отчёту Forescout, уязвимость затрагивала версии NicheStack до 4.3. Стек разрабатывался с 1996 года - тогда о безопасности парсеров думали, мягко говоря, не так много.
| Параметр | Значение |
|---|---|
| Позиция атакующего | DNS-сервер или MitM на DNS-трафике |
| Аутентификация | Не требуется |
| Взаимодействие пользователя | Не требуется |
| Затронутые стеки | NicheStack < 4.3, FreeBSD, IPNet, NetX, Nucleus NET |
| Результат | RCE (CVSS 9.8), DoS, DNS cache poisoning |
State machine атаки на TCP/IP: скрытые переходы в tcp_input.c и tcpip.sys
TCP state machine - один из наиболее формализованных конечных автоматов в сетевом стеке. RFC 793 определяет 11 состояний и детерминированные переходы. На практике реализация вnet/ipv4/tcp_input.c ядра Linux - это десятки тысяч строк, обрабатывающих таймеры, ретрансмиссии, window scaling, SACK, ECN. Каждая подсистема добавляет состояния, которых в RFC нет и в помине.Predictable TCP ISN. В NicheStack обнаружены предсказуемые Initial Sequence Numbers. ISN - входной параметр TCP state machine. Предсказуемый ISN расширяет набор достижимых состояний weird machine: off-path атакующий инжектирует пакеты в чужую TCP-сессию без позиции MitM. По формальной модели Dullien, предсказуемость ISN ослабляет контракт - переходы, требующие знания ISN, становятся доступны наблюдателю. В 2024 году. Предсказуемые ISN. Серьёзно.
ICMP-triggered transitions. По исследованию ACM «Off-Path Attacks on the TCP/IP Protocol Suite», атакующие используют поддельные ICMP error messages для манипуляции состоянием TCP-соединения. ICMP Destination Unreachable с корректным embedded TCP header заставляет ядро закрыть или модифицировать соединение. Код обработки ICMP ошибок - часть спецификации, но его комбинация с TCP state machine создаёт переходы, не предусмотренные проектировщиком. Атакующий «программирует» эти переходы последовательностью крафтованных ICMP-пакетов - каждый пакет меняет одну переменную состояния TCP.
Weird machines Windows kernel сетевой стек: tcpip.sys. В Windows TCP/IP-стек реализован драйвером
tcpip.sys в пространстве ядра. Уязвимости парсинга TCP-пакетов в этом драйвере ведут к Exploitation for Privilege Escalation (T1068): код исполняется с привилегиями SYSTEM. По данным SecureLayer7, «attackers can manipulate TCP packets to gain elevated privileges», причём эксплуатация «can be exploited without additional user interaction» - пакет обрабатывается парсером до любой прикладной проверки. Sequence number, acknowledgment number, TCP flags, опции - всё это входные символы weird machine в tcpip.sys. Пакет прилетел в ядро - и ядро уже «исполняет программу» атакующего.Ключевое отличие от гаджетов ROP в сетевых стеках: здесь не нужно переиспользовать фрагменты кода через подмену адресов возврата. Парсер сам выполняет ветвления на основе содержимого пакета. Гаджет-ориентированное программирование заменяется «программированием полей пакета» - и это принципиально другая механика.
Уязвимости парсеров HTTP: weird machine в chunked encoding
HTTP/1.1 chunked transfer encoding (RFC 7230) передаёт тело порциями. Каждая начинается с hex-строки размера, за ней CRLF, данные, CRLF. Парсер читает hex-размер, аллоцирует буфер, копирует данные. Завершение -0\r\n\r\n.Weird machine возникает в разборе hex-размера. RFC допускает chunk extensions после точки с запятой. Реализации обрабатывают ведущие нули, пробелы, mixed case hex по-разному - и вот тут начинается самое интересное. HTTP-парсер NicheStack содержал критическую RCE-уязвимость (buffer overflow при парсинге HTTP-запросов, точный CVSS зависит от конкретного CVE).
Механика: если парсер chunk size использует аналог
strtoul() без проверки переполнения, chunk size FFFFFFFF на 32-битной системе вызовет integer overflow при аллокации. size + 1 при size = 0xFFFFFFFF даёт wrap around к 0. Дальше memcpy запишет данные за пределы нулевого буфера. Один hex-строка - и парсер пишет куда не надо.Weird machine программируется двумя «инструкциями»:
- Hex-строка chunk size - управляет размером аллокации (какой буфер выделить)
- Тело chunk - управляет содержимым записи (что и куда записать)
Фаззинг сетевых парсеров: поиск weird machine примитивов
Поиск weird machines в сетевом стеке - задача для structure-aware фаззинга. Случайные байты не пройдут первичный парсинг заголовков - парсер их просто выбросит. Нужен фаззер, который мутирует поля внутри грамматически корректных пакетов.AFL++ для embedded-стеков. Для NicheStack, lwIP, uIP: выдёргиваем функцию парсинга DNS или HTTP, компилируем с AddressSanitizer, подаём AFL++. Corpus seeds - реальные DNS-ответы, собранные через
tcpdump -w dns_corpus.pcap port 53 и сконвертированные в файлы по одному пакету. Я обычно начинаю с 50-100 реальных ответов - чем разнообразнее, тем лучше покрытие.
Bash:
# Компиляция харнеса с ASan:
# AFL_USE_ASAN=1 afl-clang-fast -o dns_parser_harness harness.c
afl-fuzz -i corpus/dns_responses -o findings/ \
-m none -t 1000 \
-- ./dns_parser_harness @@
# Для ускорения используйте persistent mode (__AFL_FUZZ_TESTCASE_BUF)
tcp_input.c и net/dns_resolver/ требует ядерного фаззера. syzkaller генерирует последовательности системных вызовов (socket(), bind(), sendto() с крафтованными данными), прогоняющих пакеты через весь сетевой стек. Для целенаправленного фаззинга DNS-парсера нужно описание структуры DNS-пакета в syzlang-шаблонах для sendto(). Описание syzlang-шаблонов - отдельная история, но без него syzkaller будет генерировать мусор вместо валидных DNS-пакетов.WinDbg для tcpip.sys. Реверс через IDA Pro/Ghidra с фокусом на функции парсинга TCP-опций. Точка останова
bp tcpip!TcpSegmentTcbReceive позволяет трейсить ветвления парсера при получении крафтованного пакета, отправленного через Scapy с хоста атакующего. Без исходников трассировка парсерных ветвлений занимает дни - но именно она показывает, какие поля пакета управляют какими переходами weird machine. Нудная работа, но альтернативы нет.Что искать. Не crash сам по себе (крашей будет много), а управляемые примитивы: контролируемая запись за пределы буфера (длина определяется полем пакета), контролируемый указатель перехода (DNS compression offset), контролируемый размер аллокации (HTTP chunk size). Каждый такой примитив - «инструкция» weird machine, из которых собирается эксплойт на основе анализа протокольных парсеров.
Детектирование активации weird machines в сетевом стеке
Weird machine активируется, когда парсер входит в непредусмотренное состояние. Детектировать это можно на нескольких уровнях:| Уровень | Инструмент | Что обнаруживает |
|---|---|---|
| Ядро (runtime) | KASAN / KFENCE (Linux) | OOB-доступ при парсинге пакета |
| Ядро (hardening) | Stack canaries (D3-SFCV) | Перезапись стека в функции парсера |
| Сеть (IDS) | Suricata / Snort | Аномальные DNS/HTTP-пакеты |
| SIEM | Sigma lnx_buffer_overflows.yml | Kernel oops от buffer overflow |
| Endpoint | Shadow Stack (D3-SSC) | ROP-цепочка после активации weird machine |
| Harden | DEP / NX (D3-PSEP) | Исполнение из data-секций после exploit |
| Harden | ASLR (D3-SAOR) | Рандомизация адресов стека и кучи |
IDS-уровень - самый ранний. Правила для Suricata, проверяющие глубину рекурсии DNS compression-указателей (>3 уровня - подозрительно, >6 - почти наверняка попытка эксплуатации), или HTTP-ответы с chunk size, превышающим Content-Length, закрывают вектор до того, как пакет дойдёт до уязвимого парсера. Но есть нюанс: правило нужно написать, а для этого нужно знать, что искать.
Критическая разница: ядро Linux прошло три десятилетия security review и включает KASAN, KFENCE, stack protector. Проприетарные стеки вроде NicheStack разрабатывались, по данным Forescout, «at a time when software vulnerabilities were not as well understood as today» - без ASLR, без stack canaries, часто без NX. Weird machine в таком стеке эксплуатируется тривиально: от OOB write до перехвата управления - один шаг, буквально. Shodan показывал порядка 6400 устройств с NicheStack, доступных из интернета. Forescout насчитал почти 200 вендоров-клиентов InterNiche, включая Siemens, Honeywell, Schneider Electric. Патчи от HCC Embedded (текущий владелец) доступны только по запросу OEM-клиентов - конечные пользователи ждут обновлений от производителей устройств, многие из которых прекратили поддержку. Тупик.
Последние два года я трачу больше времени на парсеры протоколов, чем на классический binary exploitation с переполнениями и ROP-цепочками. Скрытые вычисления в парсерах TCP/IP, DNS и HTTP достижимы с сети, не требуют утечки адресов, не требуют обхода ASLR и CFI - потому что парсер сам становится вычислителем. В embedded-стеках, где нет KASAN, canaries и Shadow Stack, путь от крафтованного пакета до RCE - десятки байт и одна функция. Для Linux kernel ситуация лучше, но не решена: KASAN ловит многие OOB при фаззинге через syzkaller, однако coverage сетевых парсеров в syzkaller до сих пор неполный - edge cases в обработке DNS-опций или TCP SACK остаются нефаззенными. Я убеждён, что следующая волна сетевых эксплойтов пойдёт не через memory corruption в привычном понимании, а через композицию парсерных примитивов - серию пакетов, где каждый «исполняет инструкцию» на скрытом вычислителе внутри парсера. ROP учил нас собирать вычисления из гаджетов. Парсерные weird machines учат собирать вычисления из полей протокола - и это принципиально другой skill set, для которого нужно думать не «где мой
pop rdi; ret», а «какой offset в compression pointer переведёт парсер в нужное мне состояние». На HackerLab лежат сценарии, где бинарный примитив нужно раскрутить в полноценную цепочку от начального доступа до перехвата исполнения - формат, который быстро показывает разницу между пониманием парсера и умением его «запрограммировать».
AI-выжимка
сгенерировано ИИ
Тезисы статьи скоро
Статья читается полностью. Тезисы со ссылками на разделы появятся позже.
Содержание
Codeby Academy
Практика и мастерство
От основ до продвинутого — программы для практиков Codeby.
Перейти к курсу →
Поиск в обсуждении
Карта ветки
Продолжить чтение
Следующий разбор
Сканирование внешнего периметра компании: Nmap+OSINT
Ещё по теме
- Статья
- Статья
Комментарии
0