Команда Red Team компании Okta обнаружила в OpenSSL уязвимость и дала ей имя HollowByte — «пустой байт». Название точное: атака строится на том, что сервер ждёт данные, которые никогда не придут. Хватает 11 байт специально сформированного TLS-запроса, чтобы непропатченный сервер выделил под тело сообщения до 131 килобайта памяти — и потом просто завис в ожидании.

Причина в том, как OpenSSL обрабатывает заголовок TLS-сообщения. У каждого такого сообщения есть 4-байтовый заголовок, три байта которого сообщают ожидаемую длину тела. В старых версиях библиотеки буфер приёма растягивался до заявленного размера сразу же, как только приходил заголовок — ещё до того, как поступит хоть один байт тела и до всех проверок валидности handshake. Для входящего ClientHello максимальный размер такого буфера — те самые 131 КБ. Рабочий поток при этом блокируется, ожидая тело сообщения, которое так и не приходит: ни аутентификации, ни установления сессии, ни обмена ключами не происходит вовсе.
Сама по себе идея исчерпания соединений не нова — это родственник классической атаки Slowloris. Особенность HollowByte в другом: в том, как она взаимодействует с glibc. Когда атакующий обрывает соединение, OpenSSL честно освобождает буфер. Но glibc не спешит возвращать небольшие и средние куски памяти ядру — она держит их про запас для повторного использования. Если на каждом новом соединении менять заявленный размер тела, аллокатор glibc не может переиспользовать освобождённые фрагменты. В результате куча дробится, резидентная память (RSS) растёт и остаётся высокой даже после того, как атака прекратилась. На системах с glibc, которые тестировала Okta, эта память фактически потеряна до перезапуска процесса.
Цифры из тестов Okta выглядят убедительно. Сервер с 1 ГБ памяти под управлением NGINX был убит OOM-killer'ом — при этом 547 МБ памяти оказались заморожены во фрагментах. На сервере с 16 ГБ атака заблокировала четверть всей системной памяти, причём это произошло без превышения лимита на количество соединений. Команда Okta прокомментировала это так: «стандартные защиты, ограничивающие число соединений, здесь не сработают». При этом Okta опубликовала эти цифры, но не выложила эксплоит. По состоянию на 18 июля The Hacker News не нашла ни одного публичного proof-of-concept на GitHub.
Патч уже существует. Исправленные релизы OpenSSL — 4.0.1, 3.6.3, 3.5.7, 3.4.6 и 3.0.21 — вышли 9 июня, и любая более ранняя версия на этих ветках содержит уязвимость. Автор патча — Матт Касуэлл (Matt Caswell). Изменения оформлены тремя pull request'ами: PR 30792 закрывает master и ветку 4.0, PR 30793 — ветки 3.6, 3.5 и 3.4, PR 30794 — ветку 3.0.
А вот дальше начинается спорная часть истории. В тексте самого PR служба безопасности OpenSSL прямо указывает, что решила «обработать это как исправление категории «баг или ужесточение»». Проблема в том, что официальная политика серьёзности OpenSSL предусматривает четыре уровня — Critical, High, Moderate, Low — и категории «баг или ужесточение» среди них нет. Следствия этого решения ощутимы: HollowByte не получила CVE, не вышло security advisory, нет записи в changelog, нет упоминания в release notes. Журналисты The Hacker News проверили все 23 пункта changelog версии 4.0.1 — упоминания не нашли. Даже уязвимости с рейтингом Low обычно получают CVE, строчку в changelog и запись на странице уязвимостей. HollowByte не получила ничего из этого.
Логику OpenSSL можно восстановить косвенно: 131 КБ на соединение — не такая большая цифра, любой TLS-сервер так или иначе выделяет память под каждое соединение, а ограниченное по размеру выделение памяти само по себе не обязательно является уязвимостью. Контраргумент Okta прост: память не возвращается в пул из-за фрагментации в glibc, и в этом всё различие между обычным потреблением ресурсов и настоящей DoS-атакой.
Полезно сравнить HollowByte с другими уязвимостями памяти, закрытыми в том же релизе 9 июня. CVE-2025-66199 получила рейтинг Low ещё в январе — это баг в сжатии сертификатов TLS 1.3, где заявленная пиром длина увеличивала кучу-буфер до проверки, с эффектом около 22 МиБ на соединение. Но чтобы её эксплуатировать, нужно совпадение четырёх условий сразу: компиляция с поддержкой сжатия сертификатов, наличие алгоритма сжатия, согласованное расширение и запрос клиентских сертификатов на стороне сервера. HollowByte не требует ни одного из этих условий. Вторая уязвимость, CVE-2026-34183, получила рейтинг Moderate — неограниченный рост памяти в обработчике PATH_CHALLENGE протокола QUIC, тоже закрытая в июньском релизе и тоже связанная с исчерпанием памяти, но ей CVE присвоили без вопросов. Тот же релиз в целом закрыл 18 CVE, включая High-серьёзность use-after-free в PKCS7_verify(). Пользователи, собирающие OpenSSL из исходников напрямую, получают все эти исправления автоматически и без специальных уведомлений.
У дистрибутивов вроде Red Hat вся эта история создаёт отдельную головную боль. Red Hat традиционно backport'ит исправления, не меняя номер версии OpenSSL в пакете — то есть пропатченный пакет продолжает отчитываться исходной версией сборки. Администраторы обычно полагаются на security advisory и OVAL-фид, привязанные к идентификаторам CVE, чтобы понять, применён ли патч. Но раз CVE не существует, весь этот механизм отслеживания перестаёт работать. Практическая рекомендация: смотреть changelog пакета или напрямую спрашивать мейнтейнера, был ли пакет пересобран на основе июньского релиза или к нему применён конкретный патч из PR 30792, 30793 или 30794. Если же OpenSSL собирается вручную — нужно обновиться до указанного релиза и перезапустить все процессы, использующие старую библиотеку.
Отдельная проблема — что патч касается только TLS, а DTLS остаётся уязвимым. Матт Касуэлл прямо написал в тексте PR, что полноценное исправление DTLS «потребовало бы гораздо более инвазивных изменений», и проект решил пока не заниматься этой веткой протокола. The Hacker News провела собственную проверку — сравнила исходный код OpenSSL по тегам 3.6.2 и 3.6.3 и обнаружила, что файл обработки DTLS-handshake остался побайтово идентичен до и после «исправления». В самом свежем релизе 4.0.1 код DTLS по-прежнему определяет размер буфера, опираясь на длину, заявленную удалённой стороной — непропатченным способом. OpenSSL не классифицировала этот путь как уязвимость и не взяла на себя обязательств его исправить. Ни в release notes, ни в changelog, ни на странице уязвимостей об этом нет ни слова — упоминание существует только в самом pull request.
The Hacker News на момент публикации своего материала оставила без ответа три вопроса: почему HollowByte оценили ниже уровня Low, дошло ли исправление до веток расширенной поддержки OpenSSL 1.1.1 и 1.0.2, и — уже к Okta — затрагивает ли проблема фрагментации памяти аллокаторы, отличные от glibc. Издание пообещало обновить материал, если получит ответы.

Изображение носит иллюстративный характер
Причина в том, как OpenSSL обрабатывает заголовок TLS-сообщения. У каждого такого сообщения есть 4-байтовый заголовок, три байта которого сообщают ожидаемую длину тела. В старых версиях библиотеки буфер приёма растягивался до заявленного размера сразу же, как только приходил заголовок — ещё до того, как поступит хоть один байт тела и до всех проверок валидности handshake. Для входящего ClientHello максимальный размер такого буфера — те самые 131 КБ. Рабочий поток при этом блокируется, ожидая тело сообщения, которое так и не приходит: ни аутентификации, ни установления сессии, ни обмена ключами не происходит вовсе.
Сама по себе идея исчерпания соединений не нова — это родственник классической атаки Slowloris. Особенность HollowByte в другом: в том, как она взаимодействует с glibc. Когда атакующий обрывает соединение, OpenSSL честно освобождает буфер. Но glibc не спешит возвращать небольшие и средние куски памяти ядру — она держит их про запас для повторного использования. Если на каждом новом соединении менять заявленный размер тела, аллокатор glibc не может переиспользовать освобождённые фрагменты. В результате куча дробится, резидентная память (RSS) растёт и остаётся высокой даже после того, как атака прекратилась. На системах с glibc, которые тестировала Okta, эта память фактически потеряна до перезапуска процесса.
Цифры из тестов Okta выглядят убедительно. Сервер с 1 ГБ памяти под управлением NGINX был убит OOM-killer'ом — при этом 547 МБ памяти оказались заморожены во фрагментах. На сервере с 16 ГБ атака заблокировала четверть всей системной памяти, причём это произошло без превышения лимита на количество соединений. Команда Okta прокомментировала это так: «стандартные защиты, ограничивающие число соединений, здесь не сработают». При этом Okta опубликовала эти цифры, но не выложила эксплоит. По состоянию на 18 июля The Hacker News не нашла ни одного публичного proof-of-concept на GitHub.
Патч уже существует. Исправленные релизы OpenSSL — 4.0.1, 3.6.3, 3.5.7, 3.4.6 и 3.0.21 — вышли 9 июня, и любая более ранняя версия на этих ветках содержит уязвимость. Автор патча — Матт Касуэлл (Matt Caswell). Изменения оформлены тремя pull request'ами: PR 30792 закрывает master и ветку 4.0, PR 30793 — ветки 3.6, 3.5 и 3.4, PR 30794 — ветку 3.0.
А вот дальше начинается спорная часть истории. В тексте самого PR служба безопасности OpenSSL прямо указывает, что решила «обработать это как исправление категории «баг или ужесточение»». Проблема в том, что официальная политика серьёзности OpenSSL предусматривает четыре уровня — Critical, High, Moderate, Low — и категории «баг или ужесточение» среди них нет. Следствия этого решения ощутимы: HollowByte не получила CVE, не вышло security advisory, нет записи в changelog, нет упоминания в release notes. Журналисты The Hacker News проверили все 23 пункта changelog версии 4.0.1 — упоминания не нашли. Даже уязвимости с рейтингом Low обычно получают CVE, строчку в changelog и запись на странице уязвимостей. HollowByte не получила ничего из этого.
Логику OpenSSL можно восстановить косвенно: 131 КБ на соединение — не такая большая цифра, любой TLS-сервер так или иначе выделяет память под каждое соединение, а ограниченное по размеру выделение памяти само по себе не обязательно является уязвимостью. Контраргумент Okta прост: память не возвращается в пул из-за фрагментации в glibc, и в этом всё различие между обычным потреблением ресурсов и настоящей DoS-атакой.
Полезно сравнить HollowByte с другими уязвимостями памяти, закрытыми в том же релизе 9 июня. CVE-2025-66199 получила рейтинг Low ещё в январе — это баг в сжатии сертификатов TLS 1.3, где заявленная пиром длина увеличивала кучу-буфер до проверки, с эффектом около 22 МиБ на соединение. Но чтобы её эксплуатировать, нужно совпадение четырёх условий сразу: компиляция с поддержкой сжатия сертификатов, наличие алгоритма сжатия, согласованное расширение и запрос клиентских сертификатов на стороне сервера. HollowByte не требует ни одного из этих условий. Вторая уязвимость, CVE-2026-34183, получила рейтинг Moderate — неограниченный рост памяти в обработчике PATH_CHALLENGE протокола QUIC, тоже закрытая в июньском релизе и тоже связанная с исчерпанием памяти, но ей CVE присвоили без вопросов. Тот же релиз в целом закрыл 18 CVE, включая High-серьёзность use-after-free в PKCS7_verify(). Пользователи, собирающие OpenSSL из исходников напрямую, получают все эти исправления автоматически и без специальных уведомлений.
У дистрибутивов вроде Red Hat вся эта история создаёт отдельную головную боль. Red Hat традиционно backport'ит исправления, не меняя номер версии OpenSSL в пакете — то есть пропатченный пакет продолжает отчитываться исходной версией сборки. Администраторы обычно полагаются на security advisory и OVAL-фид, привязанные к идентификаторам CVE, чтобы понять, применён ли патч. Но раз CVE не существует, весь этот механизм отслеживания перестаёт работать. Практическая рекомендация: смотреть changelog пакета или напрямую спрашивать мейнтейнера, был ли пакет пересобран на основе июньского релиза или к нему применён конкретный патч из PR 30792, 30793 или 30794. Если же OpenSSL собирается вручную — нужно обновиться до указанного релиза и перезапустить все процессы, использующие старую библиотеку.
Отдельная проблема — что патч касается только TLS, а DTLS остаётся уязвимым. Матт Касуэлл прямо написал в тексте PR, что полноценное исправление DTLS «потребовало бы гораздо более инвазивных изменений», и проект решил пока не заниматься этой веткой протокола. The Hacker News провела собственную проверку — сравнила исходный код OpenSSL по тегам 3.6.2 и 3.6.3 и обнаружила, что файл обработки DTLS-handshake остался побайтово идентичен до и после «исправления». В самом свежем релизе 4.0.1 код DTLS по-прежнему определяет размер буфера, опираясь на длину, заявленную удалённой стороной — непропатченным способом. OpenSSL не классифицировала этот путь как уязвимость и не взяла на себя обязательств его исправить. Ни в release notes, ни в changelog, ни на странице уязвимостей об этом нет ни слова — упоминание существует только в самом pull request.
The Hacker News на момент публикации своего материала оставила без ответа три вопроса: почему HollowByte оценили ниже уровня Low, дошло ли исправление до веток расширенной поддержки OpenSSL 1.1.1 и 1.0.2, и — уже к Okta — затрагивает ли проблема фрагментации памяти аллокаторы, отличные от glibc. Издание пообещало обновить материал, если получит ответы.