Сергей Попов

Администратор
30.12.2015
6 273
6 981
Специализация
  1. OSINT
  2. Веб-безопасность
Статус верификации
  1. ✓ Verified
Разобранный сетевой HSM-модуль на чёрном коврике сверху, зонды логического анализатора подключены к контроллеру handshake на плате. Диагностический экран светится холодным белым с зелёным моноширин...


Ни один TLS-стек, который я пропускал через дифференциальный фаззинг, не отвергал все невалидные state transitions. Ни OpenSSL, ни BoringSSL, ни GnuTLS - каждый принимал хотя бы часть переходов, не описанных в RFC. FlexTLS (2015) подтвердила это систематически: все протестированные реализации допускали invalid state transitions с security implications - от client impersonation до downgrade атак на cipher suite. Это не коллекция изолированных багов. Внутри TLS handshake живёт незадокументированный вычислительный субстрат - weird machine, программируемая через crafted-сообщения. Ниже - конкретные code path'ы, через которые эти скрытые вычислительные примитивы проявляются в OpenSSL и BoringSSL, с разбором CVE, архитектурных решений и workflow фаззинга.

Weird machines в TLS handshake: как state machine превращается в скрытый вычислительный примитив​

Weird machine - вычислительная модель, которая возникает в зазоре между спецификацией и реализацией. Thomas Dullien формализовал концепцию в «Weird Machines, Exploitability, and Provable Unexploitability»: когда реализация симулирует абстрактный автомат спецификации, но допускает дополнительные состояния и переходы - лишние переходы образуют скрытый вычислительный субстрат. В arxiv:1911.00157 (Weird Machines as Insecure Compilation) тот же эффект описан через призму теории компиляции: weird machines - множество поведений скомпилированной программы в target language, недостижимых в source language. Эксплойты здесь - свидетельства insecure compilation.

Trail of Bits развивает идею через «loose contracts». Preconditions функции в машинном коде шире, чем задумывал программист. Их пример: ListItem::TrySetItem на уровне исходного кода требует два валидных указателя на ListItem, но после компиляции preconditions ослабляются - this превращается в любой указатель на 8+ байт выделенной памяти, item - в любой параметр любого типа. Зазор между intended contract и actual contract - и есть weird machine.

TLS handshake - идеальная среда для weird machines. Три структурные причины, и каждая хорошо видна в коде.

Составной конечный автомат. RFC 8446 описывает handshake как линейную последовательность (ClientHello → ServerHello → EncryptedExtensions → Certificate → CertificateVerify → Finished), но state machine в ssl/statem/statem.c содержит десятки промежуточных состояний: DTLS flush, renegotiation states, early data (0-RTT), post-handshake authentication. Каждое промежуточное состояние - потенциальный entry point для weird machine.

Множественные точки входа. SSL_read(), SSL_write(), SSL_do_handshake() - три функции, обращающиеся к одному автомату с различной логикой проверки текущего состояния. Разница в проверках - тот самый loose contract, порождающий скрытые вычислительные примитивы протоколов.

Implicit state transitions. Вызов SSL_read() на незавершённом handshake неявно триггерит его продолжение. В RFC этого нет, но production-код активно пользуется. По аналогии с ROP, где gadget chains из легитимного кода формируют произвольные вычисления, в TLS state machine цепочки невалидных переходов дают ROP-подобные примитивы: conditional branch (ветвление по alert/no-alert), data leak (утечка через unencrypted fallback), loop (re-negotiation cycles).

CVE-2017-3737: эксплуатация состояний TLS протокола через error state​

CVE-2017-3737 - один из самых показательных случаев, где уязвимость OpenSSL handshake раскрывает weird machine primitive в чистом виде. По NVD, уязвимость затрагивает OpenSSL 1.0.2 начиная с версии 1.0.2b. Механизм «error state» задумывался просто: при фатальной ошибке handshake соединение немедленно отклоняет дальнейшие операции. Для SSL_do_handshake(), SSL_accept(), SSL_connect() это работало. Для SSL_read() и SSL_write() - нет. NVD указывает CWE-125 (Out-of-bounds Read) и CWE-787 (Out-of-bounds Write).

Анатомия weird machine: от error state до unencrypted write​

Сценарий из анализа FortiGuard Labs: атакующий контролирует серверную сторону и отправляет malformed ServerHello (например, с невалидным cipher). Функция ssl3_get_server_hello() ловит ошибку и переводит соединение в error state:
C:
// ssl/s3_clnt.c - обработка невалидного cipher
ssl3_send_alert(s, SSL3_AL_FATAL, al);  // отправка alert
s->state = SSL_ST_ERR;                   // переход в error state
return (-1);
Если клиентское приложение не проверяет return code и дёргает SSL_write() повторно, ssl3_write_bytes() проверяет состояние через макрос SSL_in_init():
C:
// ssl/s3_pkt.c, line 654
if (SSL_in_init(s) && !s->in_handshake) {
    i = s->handshake_func(s); // повторный handshake
}
// SSL_in_init: (SSL_state(a) & SSL_ST_INIT)
// SSL_ST_ERR & SSL_ST_INIT == 0 → условие FALSE
Побитовое AND возвращает false - SSL_ST_ERR не содержит бит SSL_ST_INIT. Handshake не инициируется. Application data уходит без шифрования. Вот так, одна побитовая операция - и весь TLS превращается в plaintext-трубу.

Вычислительные примитивы CVE-2017-3737 как gadget chains в сетевых протоколах​

В терминах weird machine анализа бинарных протоколов эта уязвимость даёт три composable примитива:

Conditional write. Отправка открытых данных условна - зависит от того, проверит ли caller error state. Атакующий не контролирует это напрямую, но может провоцировать, если знает, что целевое приложение использует паттерн «write-after-error».

State oracle. Атакующий на серверной стороне видит plaintext вместо ciphertext - определяет, какие данные клиент отправляет, и подтверждает, что weird machine активирована.

Loop composability. Если приложение поддерживает re-negotiation, атакующий может циклически переводить соединение между error state и init state, создавая loop primitive - аналог Turing-complete side effects в протоколах.

В терминах MITRE ATT&CK этот класс уязвимостей соответствует технике Weaken Encryption (T1600, тактика defense-impairment): атакующий не ломает криптографию, а заставляет реализацию обойти шифрование через манипуляцию состоянием автомата. Данные идут в открытом виде - готовый downgrade через Encrypted Channel (T1573).

Предусловия и ограничения:

Работает если: приложение вызывает SSL_write()/SSL_read() без проверки return code handshake; OpenSSL 1.0.2b–1.0.2m.

Не работает если: приложение использует SSL_do_handshake() с явной проверкой ошибок; OpenSSL < 1.0.2b (error state mechanism отсутствует) или ≥ 1.0.2n (исправлено); BoringSSL (fail-fast по умолчанию).

BoringSSL внутреннее устройство: архитектурные решения против weird machines в TLS​

BoringSSL - форк OpenSSL от Google - архитектурно сужает поверхность для скрытых вычислительных примитивов протоколов. Fastly, мигрировавшая с OpenSSL на BoringSSL в 2023 году, формулирует мотивацию прямо: «reduce the frequency and impact of CVEs and improve the security of our TLS termination system.»

Как BoringSSL сокращает weird machine surface​

Renegotiation отключена. Renegotiation - генератор loop primitive в protocol state machine: каждый цикл создаёт повторные переходы через handshake states, и state cleanup между циклами исторически был рассадником уязвимостей. Убрали renegotiation - убрали целый класс gadget chains в сетевых протоколах.

Удаление legacy cipher suites. Каждый дополнительный cipher - дополнительная ветка в state machine парсера ServerHello и дополнительная возможность для TLS parsing confusion. BoringSSL вырезал слабые CBC ciphers, оставив (под давлением мейнтейнеров) один - ECDHE-RSA-AES128-SHA256. По данным Fastly, количество несовместимых клиентов оказалось «negligible», хотя «even a small percentage of traffic can have a real impact» на масштабе крупного CDN.

Fail-fast error handling. SSL_read()/SSL_write() в BoringSSL не пытаются неявно продолжить handshake после ошибки. Класс weird machines типа CVE-2017-3737 просто не существует.

Компактная кодовая база. Fastly приводит прямое сравнение объёма кода: BoringSSL заметно компактнее за счёт удалённого legacy. Меньше кода - меньше implicit state transitions - меньше скрытых вычислительных примитивов. Арифметика простая.

ХарактеристикаOpenSSLBoringSSL
RenegotiationПоддерживаетсяОтключена по умолчанию
CBC cipher suitesПолный наборОдин (ECDHE-RSA-AES128-SHA256)
Error handlingImplicit (caller проверяет)Fail-fast
VersioningПериодические релизыНет версий, continuous upstream
OCSP StaplingНативноОтсутствует (добавлено Fastly самостоятельно)

Ограничения подхода BoringSSL. Отсутствие версионирования означает, что потребитель сам отслеживает upstream-изменения. Fastly пишет прямым текстом: «we now face the additional work of analyzing all recent BoringSSL changes upstream to assess whether we need, or should, perform updates.» Удаление OCSP Stapling (Google использует CRLSets вместо OCSP) - головная боль для non-Google потребителей. Fastly пришлось портировать эту функциональность самостоятельно. По документам BoringSSL безопаснее - на практике за эту безопасность платишь инженерным временем.

Отдельно стоит упомянуть Rustls - TLS-библиотеку на Rust. По данным ISRG (Internet Security Research Group), она превосходит и OpenSSL, и BoringSSL по производительности handshake и throughput. Memory safety Rust устраняет целые классы weird machines, связанных с memory corruption (паттерны CWE-125, CWE-787), но не защищает от логических weird machines в state machine - тех, что возникают из-за loose contracts в протокольной логике. Rust спасёт от buffer overflow, но не от кривого автомата.

Protocol state machine exploitation: поиск weird machines через дифференциальный фаззинг TLS парсеров​

📚 Часть контента скрыта. Этот материал доступен участникам сообщества с рангом One Level или выше
Получить доступ просто — достаточно зарегистрироваться и проявить активность на форуме

Шаг 5. Верификация controllability. Crash - кандидат, но не гарантия weird machine. Для подтверждения нужно показать, что атакующий контролирует вход в аномальное состояние и может извлечь side effect (information leak, unencrypted data, state confusion). Symbolic execution через angr помогает определить, какие байты input'а управляют переходом - от входного буфера до точки расхождения.

TLS parser вычислительные аномалии: паттерны для поиска​

Implicit state reset. Alert-сообщение не полностью очищает state структуры SSL → следующее handshake-сообщение попадает в «несуществующее» состояние автомата. Именно этот паттерн лежит в основе CVE-2017-3737.

Parsing confusion. Одна и та же последовательность байт интерпретируется по-разному в зависимости от текущего state - type confusion, но на уровне протокола. Пример: ServerHello extension, прочитанная как часть EncryptedExtensions при нарушении порядка сообщений.

Extension nesting overflow. TLS extension length - 16-bit поле, но вложенные extensions (supported_versions внутри EncryptedExtensions) могут создавать рекурсивные parsing loops при malformed length fields.

0-RTT state leakage. TLS 1.3 early data создаёт состояние, в котором данные отправляются до завершения handshake. Replay protection зависит от серверных anti-replay механизмов - если они реализованы с loose contract, получаем прямой weird machine primitive: атакующий повторяет 0-RTT данные, а сервер обрабатывает их в контексте незавершённого handshake.

Weird machines в TLS - направление, которое в русскоязычном исследовательском пространстве практически не существует. Все разбирают конкретные CVE как изолированные дефекты, но мало кто смотрит на state machine TLS-стека как на программируемую вычислительную среду. Между тем именно этот взгляд - через формализацию Dullien, через Hoare triples Trail of Bits, через insecure compilation - отвечает на фундаментальный вопрос: почему одни уязвимости эксплуатабельны, а другие приводят только к crash. Мой прогноз: с приходом post-quantum handshake (ML-KEM), Encrypted Client Hello и delegated credentials поверхность weird machines в protocol state machines будет расти. BoringSSL и Rustls движутся в правильном направлении - fail-fast, memory safety, минимальный state space - но гарантировать отсутствие скрытых вычислительных примитивов невозможно, пока реализация сложнее спецификации. Это не вопрос качества кода - это фундаментальное свойство любой системы, где abstract machine и concrete machine не изоморфны. Если хочешь пощупать protocol-level state confusion руками - задачи категории crypto и pwn на HackerLab.pro строятся на тех же примитивах: parser differentials, controlled state transitions, composable side effects.
 
Мы в соцсетях:

Взломай свой первый сервер и прокачай скилл — Начни игру на HackerLab

🚀 Первый раз на Codeby?
Гайд для новичков: что делать в первые 15 минут, ключевые разделы, правила
Начать здесь →
🧭 Навигатор · ИБ 2026
Не знаешь, какой трек твой?
5 направлений ИБ, реальные зарплаты и точка входа для каждого — в одном треде.
JuniorSenior+
100K → 600K+ ₽ /мес
Открыть навигатор →
🔴 Свежие CVE, 0-day и инциденты
То, о чём ChatGPT ещё не знает — обсуждаем в реальном времени
Threat Intel →
💼 Вакансии и заказы в ИБ
Pentest, SOC, DevSecOps, bug bounty — работа и проекты от проверенных компаний
Карьера в ИБ →

HackerLab