Sploitus

Exploit for CVE-2026-3805-curl-SMB-UAF

kitploit · 2026-08-22

Exploit Code

MARKDOWN245 lines
## https://sploitus.com/exploit?id=KITPLOIT:TOOLS-GITHUB-RAT5AK-CVE-2026-3805-CURL-SMB-UAF
# CVE-2026-3805: Use-After-Free при переиспользовании SMB-соединений curl

Я обнаружил уязвимость типа use-after-free в обработчике протокола SMB в libcurl. Когда вторая SMB-передача переиспользует существующее соединение с тем же сервером, путь к файлу для нового запроса (`req->path`) является висячим указателем на освобождённую память в куче. Эта память читается через `strlen()` и копируется в исходящий SMB-пакет, что приводит к утечке содержимого кучи на сервер или к краху.

Исправление в одну строку: заставить `req->path` владеть собственной копией, а не заимствовать указатель из `smbc->share` иглы (needle).

|   
---|---  
CVE| CVE-2026-3805  
Класс уязвимости| Use-After-Free (CWE-416)  
Корневая причина| `req->path` указывает внутрь `smbc->share` иглы, освобождаемого при переиспользовании соединения  
Появилась| `777c5209df` (2025-04-30) — curl 8.13.0  
Исправлена| `e090be9f73a7a71459ef678c` — curl 8.19.0 (11 марта 2026)  
Затронуты| curl 8.13.0 – 8.18.0  
Воздействие| Раскрытие содержимого кучи серверу, крах  
Серьёзность| 7.5 - HIGH - CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H  
  
## TL;DR

`smb_setup_connection()` выполняется на временном соединении-«игле» (needle), используемом для поиска в кэше соединений. Она устанавливает `req->path` так, чтобы он указывал _внутрь_ `smbc->share` (память в куче, принадлежащая игле). Когда кэш соединений находит переиспользуемое соединение, игла уничтожается — `smbc->share` освобождается, — но `req->path` остаётся на easy handle, теперь уже висячий.

Когда `smb_send_open()` формирует запрос SMB OPEN, она вызывает `strlen(req->path)` и копирует результат в исходящий пакет. Любые данные, оказавшиеся в этой освобождённой области кучи, уходят на сервер. Если атакующий контролирует сервер (SSRF либо пользователь подключается к вредоносному SMB-серверу), он получает утёкшее содержимое кучи в качестве «имени файла» в запросе SMB NT_CREATE_ANDX.

* * *

## Предыстория: переиспользование соединений в curl

libcurl агрессивно переиспользует соединения ради производительности. Когда вы выполняете несколько запросов к одному хосту, curl проверяет, можно ли переиспользовать существующее соединение, вместо установки нового. Механизм работает так:

  1. Создаётся временное соединение-«игла» (needle) с параметрами нового запроса
  2. Выполняется поиск подходящего соединения в кэше соединений
  3. Если найдено: переиспользуется существующее соединение, **игла уничтожается**
  4. Если не найдено: игла становится реальным соединением



Обработчик протокола SMB хранит состояние в двух местах:

  * `smbc` (состояние соединения) в `meta_hash` соединения
  * `req` (состояние запроса) в `meta` easy handle



Ошибка: `req->path` устанавливается так, чтобы указывать _внутрь_ `smbc->share` во время настройки иглы. Когда игла уничтожается при переиспользовании, `smbc->share` освобождается, но `req->path` по-прежнему указывает туда.

## Ошибка

В `smb_parse_url_path()` код разбирает путь SMB URL и разделяет его на имя общего ресурса (share) и путь к файлу:

root@kitploit:~
    
    
    // lib/smb.c, smb_parse_url_path() line 431:
    smbc->share = curlx_strdup((*path == '/' || *path == '\\') ? path + 1 : path);
    // ...
    *slash++ = 0;
    req->path = slash;   // <--- points into smbc->share on the NEEDLE
    

Для URL `smb://server/share1/file1.txt` создаётся:

  * `smbc->share` = `"share1\0file1.txt"` (выделено в куче, принадлежит игле)
  * `req->path` = указатель на `"file1.txt"` (внутри `smbc->share`)



Когда срабатывает переиспользование соединения:

root@kitploit:~
    
    
    // lib/url.c, url_find_or_create_conn() line 3619:
    out:
      if(needle)
        Curl_conn_free(data, needle);  // Destroys needle -> frees smbc->share
    

`Curl_conn_free()` вызывает `Curl_hash_destroy(&conn->meta_hash)`, который запускает `smb_conn_dtor()`, освобождая `smbc->share`. Но `req->path` (на easy handle, который переживает соединение) всё ещё указывает в теперь уже освобождённую память.

Когда SMB-запрос продолжает выполнение:

root@kitploit:~
    
    
    // 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()` копирует это содержимое в SMB-пакет, отправляемый на сервер.

Сценарий атаки: SSRF, при котором атакующий контролирует SMB-сервер. Приложение-жертва выполняет два SMB-запроса к серверу атакующего. Второй запрос сливает содержимое кучи как «имя файла» в запросе SMB OPEN.

### Отказ в обслуживании

Если освобождённая память была возвращена операционной системе (не отображена), `strlen()` вызывает SIGSEGV/нарушение прав доступа.

### Вторичная ошибка: неверный общий ресурс

Даже без UAF переиспользование SMB-соединений семантически сломано. `smb_send_tree_connect()` использует `smbc->share` из _переиспользуемого_ соединения (старого общего ресурса), а не из нового запроса. TREE_CONNECT уходит к совершенно не тому общему ресурсу.

* * *

## Воспроизведение

### Через curl CLI

root@kitploit:~
    
    
    # Two SMB URLs to the same server, different shares/files:
    curl smb://192.168.1.100/share1/file1.txt -o /dev/null \
         smb://192.168.1.100/share2/file2.txt -o /dev/null
    

### Через libcurl (multi-handle)

root@kitploit:~
    
    
    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);
    
    // When e2 runs after e1 completes and reuses the connection: UAF
    

### С AddressSanitizer

Соберите curl с флагом `-fsanitize=address` и выполните команды выше:

root@kitploit:~
    
    
    ==PID==ERROR: AddressSanitizer: heap-use-after-free on address 0x...
    READ of size 1 at 0x... thread T0
        #0 strlen
        #1 smb_send_open lib/smb.c:750
        #2 smb_request_state lib/smb.c:1163
        ...
    
    freed by thread T0 here:
        #0 free
        #1 smb_conn_dtor lib/smb.c:388
        #2 Curl_hash_destroy
        #3 Curl_conn_free lib/url.c:557
    

Полный скрипт воспроизведения см. в poc/REPRODUCE_UAF.sh.

* * *

## Исправление

Основная проблема в том, что `req->path` заимствует указатель на память, принадлежащую `smbc` иглы. Исправление заставляет `req->path` владеть собственной копией:

root@kitploit:~
    
    
    --- a/lib/smb.c
    +++ b/lib/smb.c
    @@ -378,7 +378,7 @@ static void smb_easy_dtor(void *key, size_t klen, void *entry)
       (void)key;
       (void)klen;
    +  curlx_free(req->path);
       curlx_free(req);
     }
    
    @@ -428,7 +428,10 @@ static CURLcode smb_parse_url_path(struct Curl_easy *data,
       /* Parse the path for the file path converting any forward slashes into
          backslashes */
       *slash++ = 0;
    -  req->path = slash;
    +  req->path = curlx_strdup(slash);
    +  if(!req->path) {
    +    Curl_safefree(smbc->share);
    +    return CURLE_OUT_OF_MEMORY;
    +  }
    

Stefan Eissing реализовал официальное исправление в коммите `e090be9f73a7a71459ef678c`.

* * *

## Осведомлённость разработчиков

Разработчики были частично осведомлены о риске висячего указателя. В `smb_easy_dtor()` есть такой комментарий:

root@kitploit:~
    
    
    /* `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 handle. Был упущен сценарий **уничтожения иглы при переиспользовании соединения** , который и является фактическим триггером.

* * *

## Хронология

Дата| Событие  
---|---  
  
* * *

## Подверженные конфигурации

  * SMB должен быть включён: `!defined(CURL_DISABLE_SMB)`
  * NTLM core должен быть доступен: `defined(USE_CURL_NTLM_CORE)`
  * `sizeof(curl_off_t) > 4` (64-битный off_t, стандарт на большинстве платформ)



SMB включён по умолчанию в сборках curl, поддерживающих NTLM.

* * *

## Ресурсы

  * Коммит с исправлением: `e090be9f73a7a71459ef678c`
  * Официальное уведомление: curl.se/docs/CVE-2026-3805.html
  * Отчёт HackerOne: #3591944
  * Коммит, внёсший уязвимость: `777c5209df`



* * *

_CVE-2026-3805 — исправлено в curl 8.19.0. Затронуты версии: 8.13.0 – 8.18.0._

_Daniel Wade —GitHub — some-email@example.com_