Сергей Попов
Администратор
- 30.12.2015
- 6 275
- 6 981
- Специализация
- OSINT
- Веб-безопасность
- Статус верификации
- ✓ Verified
Ни один 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);
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
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 - меньше скрытых вычислительных примитивов. Арифметика простая.
| Характеристика | OpenSSL | BoringSSL |
|---|---|---|
| Renegotiation | Поддерживается | Отключена по умолчанию |
| CBC cipher suites | Полный набор | Один (ECDHE-RSA-AES128-SHA256) |
| Error handling | Implicit (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.