Уязвимость BDU: 2026-04946 (CVE-2026-3805) в cURL
В cURL обнаружена уязвимость BDU: 2026-04946 (CVE-2026-3805) использования памяти после освобождения (Use-After-Free) в обработчике протокола SMB. Проблема возникает, когда выполняется второй SMB-запрос к тому же хосту с повторным использованием соединения. При этом libcurl ошибочно использует указатель на данные в уже освобождённую область памяти.
В результате возникает классическая ситуация Use After Free: программа продолжает обращаться к участку памяти после его освобождения, что может привести либо к утечке конфиденциальной информации, либо к аварийному завершению процесса (отказу в обслуживании).
Анализ уязвимости
Уровень опасности: 7.5 (Высокий)
Вектор атаки: AV: N/AC: L/PR: N/UI: N/S: U/C: N/I: N/A: H
Расшифровка вектора CVSS 3.1:
AV: N — Вектор атаки: Сетевой
AC: L — Сложность атаки: Низкая
PR: N — Необходимые привилегии: Не требуются
UI: N — Взаимодействие с пользователем: Не требуется
S: U — Область действия: Не оказывает
C: N — Влияние на конфиденциальность: Отсутствует
I: N — Влияние на целостность: Отсутствует
A: H — Влияние на доступность: Высокое
Условия эксплуатации
Уязвимость проявляется при соблюдении всех следующих условий:
- Версия curl: от 8.13.0 до 8.18.0 включительно.
- Включена поддержка SMB (CURL_DISABLE_SMB не определён), присутствует USE_CURL_NTLM_CORE, sizeof(curl_off_t) > 4.
- Сценарий выполнения: два последовательных SMB-запроса к одному и тому же хосту через одно и то же CURL*- или CURLM-взаимодействие, при котором первое соединение завершается, а второе повторно использует соединение из кеша.
- Контролируемая сторона: для извлечения утечки — атакующий контролирует целевой SMB-сервер (через SSRF, подключение к злонамеренному серверу, либо пассивный перехват трафика); для DoS — достаточно повторного использования соединения.
Технический анализ уязвимости
Механизм повторного использования соединений в curl
curl агрессивно переиспользует соединения для повышения производительности. При выполнении нескольких запросов к одному хосту библиотека проверяет наличие существующего соединения, которое можно задействовать повторно, вместо установки нового. Механизм работает следующим образом:
- Создаётся временное соединение с параметрами нового запроса
- Производится поиск в кеше соединений подходящего совпадения
- Если совпадение найдено: используется существующее соединение, а временное прекращается
- Если совпадение не найдено: временное соединение становится фактическим
В обработчике протокола SMB состояние хранится в двух местах:
- smbc (состояние соединения) в meta_hash соединения
- req (состояние запроса) в meta easy-дескриптора
Первопричина: заимствование указателя вместо копирования
Корень проблемы находится в функции smb_parse_url_path(). Рассмотрим уязвимый код для понимания механизма возникновения ошибки (lib/smb.c, строка 431):
// lib/smb.c, smb_parse_url_path() line 431: smbc->share = curlx_strdup((*path == '/' || *path == '\\') ? path + 1 : path); // ... *slash++ = 0; req->path = slash; // <--- указатель указывает внутрь smbc->share временное соединения
Для URL вида smb://server/share1/file1.txt создаётся:
- smbc->share = «share1\0file1.txt» (принадлежит временному соединению)
- req->path = указатель на «file1.txt» (внутри smbc->share)
Критическая ошибка заключается в том, что req->path не создаёт собственную копию пути, а лишь заимствует указатель на область внутри smbc->share, которая принадлежит временному соединению. Когда кеш соединений находит подходящее существующее соединение, временное прекращается — и smbc->share освобождается. При этом req->path, принадлежащий easy-дескриптору (который продолжает существовать), продолжает указывать в уже освобождённую память.
В файле lib/url.c при поиске или создании соединения происходит следующее:
// lib/url.c, url_find_or_create_conn() line 3619:
out:
if(needle)
Curl_conn_free(data, needle); // Уничтожает временное соединение -> освобождает smbc->share
Curl_conn_free() вызывает Curl_hash_destroy(& conn->meta_hash), что в свою очередь приводит к вызову smb_conn_dtor(), которая освобождает smbc->share. При этом req->path на easy-дескрипторе продолжает указывать в уже невалидную область памяти.
Сработка уязвимости: чтение после освобождения
Когда выполняется SMB-запрос с повторным использованным соединения, функция smb_send_open() строит пакет SMB OPEN и выполняет следующие операции:
// lib/smb.c, smb_send_open() line 750-769: const size_t byte_count = strlen(req->path) + 1; // UAF READ // ... curlx_strcopy(msg.bytes, sizeof(msg.bytes), req->path, byte_count - 1); // UAF READ
Здесь strlen(req->path) и curlx_strcopy читают данные по висячему указателю req->path. Что именно будет прочитано, зависит от состояния в конкретный момент времени.
Важное техническое ограничение: в smb_send_open() перед копированием выполняется проверка, что размер строки не превышает 1024 байта. strlen() сканирует память до первого нулевого байта. Если в освобождённой области нулевой байт находится в пределах первых 1024 байт, данные будут скопированы в исходящий пакет; если нулевой байт не найден, strlen() продолжит чтение за пределы допустимой области, что с высокой вероятностью приведёт к нарушению доступа (segmentation fault) и немедленному падению процесса.
Анализ эксплоита
Рассмотрим код эксплоита для детального понимания вектора атаки с использованием этой уязвимости.
Структура эксплоита
Эксплоит демонстрирует два различных сценария эксплуатации:
- Через CLI curl — простая демонстрация с двумя URL в одной команде
- Через libcurl с multi-handle — более реалистичный сценарий для приложений
Эксплуатация через CLI
curl smb://192.168.1.100/share1/file1.txt -o /dev/null \ smb://192.168.1.100/share2/file2.txt -o /dev/null
В этой команде curl последовательно выполняет два SMB-запроса к одному и тому же хосту. Первый запрос устанавливает соединение и сохраняет его в кеш. При выполнении второго запроса:
- curl создаёт временное соединение с параметрами второго запроса (share2/file2.txt)
- В smb_parse_url_path() для временного соединения выделяется smbc->share со значением «share2\0file2.txt», а req->path указывает на «file2.txt»
- curl находит в кеше существующее соединение от первого запроса и решает переиспользовать его
- Временное соединение уничтожается, освобождается память smbc->share
- req->path на easy-дескрипторе второго запроса становится висячим указателем
- При построении SMB-пакета для второго запроса происходит чтение по висячему указателю
Эксплуатация через libcurl API
Более сложный сценарий предполагает использование CURLM (multi-интерфейса):
CURLM *multi = curl_multi_init(); CURL *e1 = curl_easy_init(); curl_easy_setopt(e1, CURLOPT_URL, "smb://server/share1/file1"); curl_multi_add_handle(multi, e1); CURL *e2 = curl_easy_init(); curl_easy_setopt(e2, CURLOPT_URL, "smb://server/share2/file2"); curl_multi_add_handle(multi, e2); // Когда e2 выполняется после завершения e1 и переиспользует соединение: срабатывает UAF
Данный сценарий особенно важен, поскольку многие приложения используют libcurl именно через multi-интерфейс для асинхронной обработки множественных запросов в рамках одного цикла событий.
Вспомогательная уязвимость: неправильное имя шары
Даже если уязвимость Use-After-Free не приводит к немедленным последствиям, повторное использование SMB-соединения в libcurl имеет ещё одну семантическую проблему. Функция smb_send_tree_connect() использует smbc->share из переиспользованного соединения (старое имя шары), а не из нового запроса. Это означает, что TREE_CONNECT-запрос будет направлен к неправильной шаре, что может привести к логическим ошибкам в работе приложения.
Примечание разработчиков
Интересно, что разработчики libcurl были частично осведомлены о риске висячего указателя. В smb_easy_dtor() присутствует комментарий:
/* `req->path` points to somewhere in `struct smb_conn` which is * kept at the connection meta. If the connection is destroyed first, * req->path points to free'd memory. */
Однако этот комментарий рассматривает только сценарий, когда соединение уничтожается раньше easy-дескриптора, упуская сценарий уничтожения временного соединения при повторном использовании — именно тот сценарий, который приводит к фактическому срабатыванию уязвимости.
Потенциальное воздействие
Утечка информации
Освобождённая память могла быть перераспределена и содержать конфиденциальные данные, которые ранее находились там в процессе работы приложения. strlen(req->path) сканирует память до первого нулевого байта, и curlx_strcopy() копирует полученные данные в пакет SMB. Таким образом, содержимое (возможно, включающее пароли, токены или ключи шифрования) отправляется на SMB-сервер в качестве «имени файла» в запросе NT_CREATE_ANDX.
Сценарий атаки: SSRF (Server-Side Request Forgery) — атакующий контролирует SMB-сервер, к которому подключается уязвимое приложение. Приложение жертвы выполняет два SMB-запроса к серверу атакующего. Второй запрос приводит к утечке на контролируемый сервер.
Отказ в обслуживании (DoS)
Если освобождённая память была возвращена операционной системе, вызов strlen() приводит к SIGSEGV/access violation, вызывая немедленное аварийное завершение процесса. Для DoS-атаки атакующему даже не требуется контролировать SMB-сервер — достаточно вызвать повторное использование соединения.
Ограничения эксплуатации
Официальный бюллетень curl указывает, что риск утечки информации минимален по нескольким причинам: атакующему крайне сложно целенаправленно подготовить всё для утечки конкретных данных, а вероятность того, что приложение выполнит описанную последовательность без немедленного падения, оценивается как низкая.
Тем не менее, в определённых условиях и при определённых паттернах выделения памяти утечка чувствительных данных остаётся возможной.
Способы защиты и устранения уязвимости
Основные меры противодействия
- Обновление curl до версии 8.19.0 или выше. В версии 8.19.0 исправление включено официально (коммит e090be9f73a7a71459ef678c). Это наиболее рекомендуемый и полный метод устранения уязвимости.
- Применение патча и пересборка libcurl. Для окружений, где немедленное обновление невозможно, следует применить соответствующий патч к исходному коду и пересобрать библиотеку.
- Временное отключение поддержки SMB. Если SMB-трансферы в приложении не используются, их можно отключить, собрав libcurl с флагом -DCURL_DISABLE_SMB. Это полностью устраняет уязвимый код.
Где используется libcurl
libcurl — чрезвычайно широко распространённая библиотека. Она используется в качестве транспортного движка в огромном количестве приложений: от командной строки curl до таких проектов, как PHP (расширение cURL), Python (pycurl), Node.js (node-libcurl), а также встраивается в прошивки маршрутизаторов, принтеров, телевизоров, автомобильных систем, мобильных устройств и встраеваемых систем. Официальная статистика curl утверждает, что библиотека установлена более чем на 20 миллиардах устройств.
Важно отметить, что приложения, использующие libcurl, могут не обозначать этот факт, поэтому аудит зависимостей критически важен для выявления уязвимых компонентов.
Обнаружение атак и мониторинг
Поскольку уязвимость эксплуатируется через SMB-протокол (порт 445/TCP), обнаружение атак возможно на сетевом уровне. Ниже представлены примеры правил для систем обнаружения вторжений (IDS/IPS), таких как Suricata.
Пример правила для Suricata
alert tcp $HOME_NET any -> $EXTERNAL_NET 445 (msg:"CVE-2026-3805 - Potential curl SMB Connection Reuse UAF Exploit"; flow:to_server,established; content:"|00 00 00|"; depth:4; content:"|FF 53 4D 42|"; distance:0; within:4; content:"|A2|"; distance:4; within:1; threshold:type both, track by_src, count 2, seconds 30; classtype:attempted-user; reference:cve,2026-3805; sid:20263805; rev:1;)
Пояснение правила:
- content:"|FF 53 4D 42|» — сигнатура SMB-протокола (заголовок пакета)
- content:"|A2|» — код команды NT_CREATE_ANDX, именно в этих пакетах передаётся утёкшее содержимое
- threshold: type both, track by_src, count 2, seconds 30 — детектирует два SMB-запроса с одного источника к одному серверу в течение 30 секунд (условие срабатывания уязвимости)
ВНИМАНИЕ, это просто пример правила и оно соответствующим образом не проверялось, поэтому используйте на свой страх и риск.
Размещение: средство обнаружения должно находиться в точке, где он может видеть исходящий SMB-трафик от потенциально уязвимых клиентов. На практике это означает размещение IDS/IPS на границе сегмента сети, в котором работают приложения, использующие libcurl, либо непосредственно на хостах с такими приложениями (host-based IDS).
Ограничения правила: злоумышленник может изменять временные интервалы между запросами или использовать различные имена файлов. Правило следует рассматривать как эвристику, а не как абсолютную гарантию обнаружения.