
CVE-2026-3805: Use-After-Free nel riutilizzo della connessione SMB di curl - divulgazione di informazioni dall'heap
Ho trovato una vulnerabilità use-after-free nel gestore del protocollo SMB di libcurl. Quando
una seconda operazione SMB riutilizza una connessione esistente allo stesso server, il
percorso del file per la nuova richiesta (req->path) è un puntatore pendente verso memoria
heap liberata. Questa memoria viene letta tramite strlen() e copiata in un pacchetto SMB
in uscita, facendo trapelare i contenuti dell'heap verso il server o causando un crash.
Correzione in una riga: far sì che req->path possieda una propria copia invece di prendere in
prestito dal smbc->share dell'ago.
| CVE | CVE-2026-3805 |
| Classe di bug | Use-After-Free (CWE-416) |
| Causa principale | req->path punta al smbc->share dell'ago, liberato al riutilizzo della connessione |
| Introdotta | 777c5209df (2025-04-30) - curl 8.13.0 |
| Corretta | e090be9f73a7a71459ef678c - curl 8.19.0 (11 marzo 2026) |
| Versioni interessate | curl dalla 8.13.0 alla 8.18.0 |
| Impatto | Divulgazione di informazioni heap al server, crash |
| Gravità | 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() viene eseguita su una connessione temporanea "ago" (needle) usata per
la ricerca nella cache. Imposta req->path in modo che punti dentro smbc->share (memoria
heap di proprietà dell'ago). Quando la cache delle connessioni trova una connessione
riutilizzabile, l'ago viene distrutto - liberando smbc->share - ma req->path sopravvive
sull'easy handle, ora pendente.
Quando smb_send_open() costruisce la richiesta SMB OPEN, chiama strlen(req->path)
e copia il risultato nel pacchetto in uscita. Qualunque dato si trovi in quella regione
heap liberata finisce sul server. Se l'attaccante controlla il server (SSRF, o utente
che si connette a un SMB malevolo), riceve i contenuti heap trapelati come "nome file"
nella richiesta SMB NT_CREATE_ANDX.
libcurl riutilizza in modo aggressivo le connessioni per le prestazioni. Quando si effettuano più richieste allo stesso host, curl verifica se una connessione esistente può essere riutilizzata piuttosto che stabilirne una nuova. Il meccanismo funziona così:
Il gestore del protocollo SMB memorizza lo stato in due punti:
smbc (stato della connessione) sul meta_hash della connessionereq (stato della richiesta) sul meta dell'easy handleIl bug: req->path viene impostato per puntare dentro smbc->share durante la configurazione
dell'ago. Quando l'ago viene distrutto al riutilizzo, smbc->share viene liberato, ma
req->path punta ancora lì.
In smb_parse_url_path(), il codice analizza il percorso dell'URL SMB e lo divide in
nome della condivisione e percorso del file:
// 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
Per l'URL smb://server/share1/file1.txt, questo crea:
smbc->share = "share1\0file1.txt" (allocato nell'heap, di proprietà dell'ago)req->path = puntatore a "file1.txt" (dentro smbc->share)Quando si attiva il riutilizzo della connessione:
// 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() chiama Curl_hash_destroy(&conn->meta_hash) che invoca
smb_conn_dtor(), liberando smbc->share. Ma req->path (sull'easy handle,
che sopravvive) punta ancora alla memoria ora liberata.
Quando la richiesta SMB procede:
// 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
La memoria heap liberata potrebbe essere stata riallocata e contenere dati sensibili.
strlen(req->path) scansiona in avanti fino a trovare un byte nullo, e
curlx_strcopy() copia quel contenuto nel pacchetto SMB inviato al server.
Scenario di attacco: SSRF in cui l'attaccante controlla il server SMB. L'applicazione vittima effettua due richieste SMB al server dell'attaccante. La seconda richiesta fa trapelare i contenuti dell'heap come "nome file" nella richiesta SMB OPEN.
Se la memoria liberata è stata restituita al sistema operativo (non mappata), strlen()
provoca un SIGSEGV/violazione di accesso.
Anche senza la UAF, il riutilizzo delle connessioni SMB è semanticamente rotto.
smb_send_tree_connect() usa smbc->share della connessione riutilizzata (la
vecchia condivisione), non la condivisione della nuova richiesta. La TREE_CONNECT va
alla condivisione completamente sbagliata.
# 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
Compila curl con -fsanitize=address ed esegui quanto sopra:
==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
Vedi poc/REPRODUCE_UAF.sh per lo script di riproduzione completo.
Il problema fondamentale è che req->path prende in prestito un puntatore a memoria
di proprietà del smbc dell'ago. La correzione fa sì che req->path possieda una
propria copia:
--- 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 ha implementato la correzione ufficiale in e090be9f73a7a71459ef678c.
Gli sviluppatori erano parzialmente consapevoli del rischio di puntatore pendente. Questo commento
è presente in 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. */
Ma questo considera solo lo scenario in cui la connessione viene distrutta prima dell'easy handle. Manca lo scenario della distruzione dell'ago durante il riutilizzo della connessione, che è il fattore scatenante effettivo.
| Data |
|---|
!defined(CURL_DISABLE_SMB)defined(USE_CURL_NTLM_CORE)sizeof(curl_off_t) > 4 (off_t a 64 bit, standard sulla maggior parte delle piattaforme)SMB è abilitato per impostazione predefinita nelle build di curl che supportano NTLM.
e090be9f73a7a71459ef678c777c5209dfCVE-2026-3805 - Corretta in curl 8.19.0. Interessate: dalla 8.13.0 alla 8.18.0.
Daniel Wade - GitHub - [email protected]
| Evento |
|---|
| 2025-04-30 | 777c5209df rifattorizza SMB per usare il meta hash, introducendo il bug |
| 2026-03-07 | Ho trovato il bug durante un audit di sicurezza |
| 2026-03-08 | Segnalato a curl tramite HackerOne (#3591944) |
| 2026-03-08 | curl contatta distros@openwall |
| 2026-03-11 | curl 8.19.0 rilasciata con la correzione, pubblicata CVE-2026-3805 |