
CVE-2026-3805: Uso após liberação (Use-After-Free) na reutilização de conexão SMB do curl - divulgação de informações do heap
Encontrei uma vulnerabilidade de use-after-free no manipulador de protocolo SMB do libcurl. Quando uma segunda transferência SMB reutiliza uma conexão existente para o mesmo servidor, o caminho do arquivo para a nova solicitação (req->path) é um ponteiro pendente para memória heap liberada. Essa memória é lida via strlen() e copiada para um pacote SMB de saída, vazando o conteúdo da heap para o servidor ou causando uma falha.
Correção em uma linha: faça req->path possuir sua própria cópia em vez de tomar emprestado de smbc->share da needle.
| CVE | CVE-2026-3805 |
| Classe do bug | Use-After-Free (CWE-416) |
| Causa raiz | req->path aponta para smbc->share da needle, liberado na reutilização de conexão |
| Introduzido | 777c5209df (2025-04-30) - curl 8.13.0 |
| Corrigido | e090be9f73a7a71459ef678c - curl 8.19.0 (Mar 11, 2026) |
| Afetados | curl 8.13.0 até 8.18.0 |
| Impacto | Vazamento de informações da heap para o servidor, falha |
| Severidade | 7.5 - ALTA - CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H |
smb_setup_connection() é executada em uma conexão temporária "needle" usada para busca no cache. Ela define req->path para apontar para dentro de smbc->share (memória heap de propriedade da needle). Quando o cache de conexões encontra uma conexão reutilizável, a needle é destruída - liberando smbc->share - mas req->path permanece no easy handle, agora pendente.
Quando smb_send_open() monta a solicitação SMB OPEN, ela chama strlen(req->path) e copia o resultado para o pacote de saída. Quaisquer dados nessa região de heap liberada vão para o servidor. Se o atacante controlar o servidor (SSRF, ou usuário se conectando a um SMB malicioso), ele recebe o conteúdo vazado da heap como o "nome do arquivo" na solicitação SMB NT_CREATE_ANDX.
O libcurl reutiliza conexões agressivamente por desempenho. Quando você faz várias solicitações ao mesmo host, o curl verifica se uma conexão existente pode ser reutilizada em vez de estabelecer uma nova. O mecanismo funciona assim:
O manipulador de protocolo SMB armazena estado em dois lugares:
smbc (estado da conexão) no meta_hash da conexãoreq (estado da solicitação) no meta do easy handleO bug: req->path é definido para apontar para dentro de smbc->share durante a configuração da needle. Quando a needle é destruída na reutilização, smbc->share é liberado, mas req->path ainda aponta para lá.
Em smb_parse_url_path(), o código analisa o caminho da URL SMB e o divide em nome do compartilhamento e caminho do arquivo:
// 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
Para a URL smb://server/share1/file1.txt, isso cria:
smbc->share = "share1\0file1.txt" (alocado na heap, de propriedade da needle)req->path = ponteiro para "file1.txt" (dentro de smbc->share)Quando a reutilização de conexão é acionada:
// 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() chama Curl_hash_destroy(&conn->meta_hash), que invoca smb_conn_dtor(), liberando smbc->share. Mas req->path (no easy handle, que sobrevive) ainda aponta para a memória agora liberada.
Quando a solicitação SMB prossegue:
// 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
A memória heap liberada pode ter sido realocada e conter dados sensíveis. strlen(req->path) varre para frente até encontrar um byte nulo, e curlx_strcopy() copia esse conteúdo para o pacote SMB enviado ao servidor.
Cenário de ataque: SSRF em que o atacante controla o servidor SMB. O aplicativo vítima faz duas solicitações SMB ao servidor do atacante. A segunda solicitação vaza o conteúdo da heap como o "nome do arquivo" na solicitação SMB OPEN.
Se a memória liberada foi devolvida ao SO (desmapeada), strlen() aciona uma violação de acesso/SIGSEGV.
Mesmo sem o UAF, a reutilização de conexão SMB está semanticamente quebrada. smb_send_tree_connect() usa smbc->share da conexão reutilizada (o compartilhamento antigo), não o compartilhamento da nova solicitação. O TREE_CONNECT vai para o compartilhamento errado.
# 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
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
Compile o curl com -fsanitize=address e execute o exemplo acima:
==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
Veja poc/REPRODUCE_UAF.sh para o script de reprodução completo.
O problema fundamental é que req->path toma emprestado um ponteiro para memória de propriedade do smbc da needle. A correção faz req->path possuir sua própria cópia:
--- 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 implementou a correção oficial em e090be9f73a7a71459ef678c.
Os desenvolvedores estavam parcialmente cientes do risco de ponteiro pendente. Este comentário existe em 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. */
Mas isso considera apenas o cenário em que a conexão é destruída antes do easy handle. Ele não considerou o cenário de destruição da needle durante a reutilização de conexão, que é o gatilho real.
| Data | Evento |
|---|
!defined(CURL_DISABLE_SMB)defined(USE_CURL_NTLM_CORE)sizeof(curl_off_t) > 4 (off_t de 64 bits, padrão na maioria das plataformas)O SMB é habilitado por padrão em builds do curl que possuem suporte a NTLM.
e090be9f73a7a71459ef678c777c5209dfCVE-2026-3805 - Corrigido no curl 8.19.0. Afetados: 8.13.0 a 8.18.0.
Daniel Wade - GitHub - [email protected]
| 2025-04-30 | 777c5209df refatora o SMB para usar meta hash, introduzindo o bug |
| 2026-03-07 | Encontro o bug durante auditoria de segurança |
| 2026-03-08 | Relatado ao curl via HackerOne (#3591944) |
| 2026-03-08 | curl contata distros@openwall |
| 2026-03-11 | curl 8.19.0 lançado com a correção, CVE-2026-3805 publicado |