HollowByte: 11 байтов способны положить сервер. Разработчики OpenSSL решили скрыть критическую уязвимость

15746
HollowByte: 11 байтов способны положить сервер. Разработчики OpenSSL решили скрыть критическую уязвимость

Ошибка в OpenSSL позволяет удаленному пользователю истощать ресурсы системы без прохождения авторизации.

image

Одиннадцати байтов достаточно, чтобы заставить сервер с уязвимой версией OpenSSL выделить до 131 КБ оперативной памяти и ждать продолжения сообщения, которое никогда не придёт. После разрыва соединения библиотека освобождает буфер, однако на системах с glibc память может остаться внутри процесса. Повторение запроса постепенно увеличивает потребление ресурсов и способно привести к остановке сервера.

Ошибку назвали HollowByte. Специалисты Red Team компании Okta обнаружили её в обработчике начального этапа TLS-соединения. Исправление вошло в июньские версии OpenSSL без отдельного уведомления, идентификатора CVE и упоминания в списке устранённых уязвимостей.

Проблема возникает до аутентификации, обмена ключами и создания защищённого сеанса. Злоумышленнику не нужны учётные данные или действующий сертификат. Достаточно открыть соединение и передать начало специально сформированного TLS-сообщения.

TLS используется для защиты соединений с веб-сайтами и другими сетевыми службами. Перед отправкой основного содержимого клиент и сервер проводят рукопожатие: согласуют версию протокола, криптографические алгоритмы и ключи. Одним из первых клиент обычно отправляет сообщение ClientHello с поддерживаемыми параметрами.

Каждое сообщение рукопожатия начинается с четырёхбайтового заголовка. Первый байт указывает тип сообщения, а следующие три содержат заявленную длину основной части. В уязвимых версиях OpenSSL библиотека доверяла указанному размеру и сразу увеличивала приёмный буфер, хотя содержимое ещё не поступило.

Для входящего ClientHello размер буфера мог достигать 131 КБ. После обработки короткого заголовка рабочий поток продолжал ждать оставшиеся данные. Атакующий при этом ничего больше не отправлял и удерживал соединение открытым либо закрывал его после выделения памяти.

Подобная схема напоминает атаки семейства Slowloris, при которых сервер занимает соединения и ожидает медленно поступающие запросы. Однако HollowByte воздействует не только на число доступных подключений. Основной ущерб связан с тем, как системный распределитель памяти обрабатывает освобождённые буферы.

На многих Linux-системах памятью управляет стандартная библиотека glibc. После вызова функции освобождения она не всегда сразу возвращает небольшие и средние участки ядру операционной системы. Распределитель сохраняет их внутри процесса, чтобы ускорить последующие операции выделения.

Само по себе подобное поведение считается нормальной оптимизацией. Проблема появляется, когда запросы заставляют OpenSSL создавать буферы разного размера. Освободившиеся участки перестают подходить для новых выделений, между занятыми областями возникают пустые промежутки, а куча процесса постепенно фрагментируется.

Формально OpenSSL уже освободила ненужные буферы, но сервер не может эффективно использовать оставшиеся фрагменты и не возвращает их операционной системе. Показатель RSS, отражающий объём физической памяти процесса, продолжает расти и не снижается после прекращения атаки. Полностью вернуть ресурсы удаётся только после перезапуска процесса.

Во время испытания NGINX на сервере с 1 ГБ памяти система завершила процесс из-за нехватки ресурсов. К этому моменту около 547 МБ оставались заблокированы во фрагментированной куче. На сервере с 16 ГБ атака заняла около четверти всей памяти, не превысив установленное ограничение по числу одновременных соединений.

Поэтому обычная защита, которая ограничивает количество подключений с одного адреса или общее число клиентов, может не остановить HollowByte. Атакующему необязательно держать открытыми множество соединений. Каждый завершённый запрос способен оставлять после себя новые неудобные для повторного использования фрагменты памяти.

Опубликованные показатели получены в испытаниях Okta и пока не подтверждены независимыми тестами. Исследователи не выпустили готовый код для эксплуатации ошибки. Также остаётся неизвестным, насколько сильно эффект проявляется с другими распределителями памяти вместо glibc.

OpenSSL исправила обработку TLS-сообщений в версиях 4.0.1, 3.6.3, 3.5.7, 3.4.6 и 3.0.21, выпущенных 9 июня. Более ранние релизы соответствующих веток содержат уязвимый код.

После обновления библиотека больше не увеличивает буфер до заявленного клиентом размера сразу после получения заголовка. Память выделяется постепенно, по мере реального поступления данных. Клиент уже не может заставить сервер зарезервировать крупный участок одним обещанием прислать длинное сообщение.

Разработчики OpenSSL отнесли изменение к исправлению обычной ошибки или усилению защиты, а не к уязвимости безопасности. Поэтому HollowByte не получила CVE, отдельного бюллетеня и записи на странице уязвимостей проекта.

Подобная классификация осложняет обновление корпоративных систем. Сканеры обычно сопоставляют установленные пакеты с номерами CVE, а администраторы ориентируются на бюллетени поставщиков. Без идентификатора стандартные средства могут не показать, что сервер нуждается в исправлении.

Определить защищённость только по номеру OpenSSL тоже удаётся не всегда. Некоторые дистрибутивы Linux не заменяют библиотеку целиком, а переносят исправления в более старую ветку. Версия пакета при этом может выглядеть уязвимой, хотя нужный патч уже установлен.

Администраторам таких систем необходимо проверить журнал изменений пакета или запросить сведения у сопровождающего дистрибутива. Исправление внесено в запросах на слияние 30792 для основной ветки и OpenSSL 4.0, 30793 для веток 3.6, 3.5 и 3.4, а также 30794 для 3.0.

При самостоятельной сборке OpenSSL следует перейти как минимум на 4.0.1, 3.6.3, 3.5.7, 3.4.6 или 3.0.21 в зависимости от используемой ветки. После установки необходимо перезапустить все службы, загрузившие прежнюю библиотеку. Иначе работающий процесс продолжит использовать старый код из памяти.

Исправление касается только TLS. Вариант протокола DTLS, предназначенный для передачи защищённых данных поверх ненадёжных транспортов вроде UDP, продолжает использовать заявленную собеседником длину при выделении буфера.

Разработчик патча объяснил, что аналогичная переработка DTLS потребовала бы значительно более серьёзных изменений. Поэтому соответствующую часть кода пока оставили без исправления. Проект не сообщил, считает ли поведение DTLS уязвимостью и планирует ли менять его в следующих версиях.

Ситуация выделяется на фоне других ошибок OpenSSL, связанных с исчерпанием памяти. В январе похожая проблема при обработке сжатых сертификатов TLS 1.3 получила идентификатор CVE и низкий уровень опасности, хотя для эксплуатации требовалось одновременное выполнение нескольких условий. В июньском выпуске отдельный номер также присвоили неограниченному росту памяти в обработчике QUIC PATH_CHALLENGE.

Для HollowByte дополнительные функции и предварительное согласование возможностей не нужны. Запрос достигает уязвимого участка во время обычного начала TLS-рукопожатия. Несмотря на доступность атаки без аутентификации, OpenSSL не включила ошибку даже в категорию Low.

Обновления от 9 июня одновременно закрыли 18 официально зарегистрированных уязвимостей. Поэтому пользователи актуальных выпусков уже защищены от HollowByte, хотя описание релиза об этом не сообщает. Основной риск сохраняется для серверов, где обновления устанавливают только после появления CVE или отдельного бюллетеня безопасности.