
CVE-2026-3805: Uso después de liberación en la reutilización de conexión SMB de curl - divulgación de información del heap
Encontré una vulnerabilidad de use-after-free en el manejador de protocolo SMB de libcurl. Cuando una segunda transferencia SMB reutiliza una conexión existente al mismo servidor, la ruta del archivo para la nueva solicitud (req->path) es un puntero colgante a memoria del heap liberada. Esta memoria se lee mediante strlen() y se copia en un paquete SMB saliente, filtrando contenido del heap al servidor o provocando un bloqueo.
Corrección en una línea: hacer que req->path tenga su propia copia en lugar de tomar prestado del smbc->share de la needle.
| CVE | CVE-2026-3805 |
| Clase de bug | Use-After-Free (CWE-416) |
| Causa raíz | req->path apunta dentro del smbc->share de la needle, liberado al reutilizar la conexión |
| Introducido | 777c5209df (2025-04-30) - curl 8.13.0 |
| Corregido | e090be9f73a7a71459ef678c - curl 8.19.0 (11 mar 2026) |
| Afecta | curl 8.13.0 hasta 8.18.0 |
| Impacto | Divulgación de información del heap al servidor, bloqueo |
| Severidad | 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() se ejecuta sobre una conexión temporal «needle» utilizada para la búsqueda en la caché. Establece req->path para que apunte dentro de smbc->share (memoria del heap propiedad de la needle). Cuando la caché de conexiones encuentra una conexión reutilizable, la needle se destruye - liberando smbc->share - pero req->path permanece en el easy handle, ahora colgante.
Cuando smb_send_open() construye la solicitud SMB OPEN, llama a strlen(req->path) y copia el resultado en el paquete saliente. Cualquier dato que haya en esa región del heap liberada va al servidor. Si el atacante controla el servidor (SSRF, o un usuario que se conecta a un SMB malicioso), recibe el contenido filtrado del heap como «filename» en la solicitud SMB NT_CREATE_ANDX.
libcurl reutiliza agresivamente las conexiones por rendimiento. Cuando haces múltiples solicitudes al mismo host, curl comprueba si una conexión existente puede reutilizarse en lugar de establecer una nueva. El mecanismo funciona así:
El manejador de protocolo SMB almacena estado en dos lugares:
smbc (estado de la conexión) en el meta_hash de la conexiónreq (estado de la solicitud) en el meta del easy handleEl bug: req->path se establece para que apunte dentro de smbc->share durante la configuración de la needle. Cuando la needle se destruye en la reutilización, smbc->share se libera, pero req->path sigue apuntando allí.
En smb_parse_url_path(), el código analiza la ruta URL SMB y la divide en nombre de recurso compartido y ruta de archivo:
// 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 la URL smb://server/share1/file1.txt, esto crea:
smbc->share = "share1\0file1.txt" (asignado en el heap, propiedad de la needle)req->path = puntero a "file1.txt" (dentro de smbc->share)Cuando se activa la reutilización de conexión:
// 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() llama a Curl_hash_destroy(&conn->meta_hash), que invoca smb_conn_dtor(), liberando smbc->share. Pero req->path (en el easy handle, que sobrevive) sigue apuntando a la memoria ahora liberada.
Cuando la solicitud SMB continúa:
// 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 del heap liberada puede haber sido reasignada y contener datos sensibles.
strlen(req->path) escanea hacia adelante hasta encontrar un byte nulo, y curlx_strcopy() copia ese contenido en el paquete SMB enviado al servidor.
Escenario de ataque: SSRF donde el atacante controla el servidor SMB. La aplicación víctima hace dos solicitudes SMB al servidor del atacante. La segunda solicitud filtra contenido del heap como «filename» en la solicitud SMB OPEN.
Si la memoria liberada ha sido devuelta al sistema operativo (no asignada), strlen() provoca un SIGSEGV/violación de acceso.
Incluso sin el UAF, la reutilización de conexiones SMB está semánticamente rota.
smb_send_tree_connect() usa smbc->share de la conexión reutilizada (el recurso compartido antiguo), no el recurso compartido de la nueva solicitud. El TREE_CONNECT va al recurso compartido completamente equivocado.
# 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 y ejecuta lo anterior:
==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
Véase poc/REPRODUCE_UAF.sh para el script de reproducción completo.
El problema fundamental es que req->path toma prestado un puntero hacia memoria propiedad del smbc de la needle. La corrección hace que req->path tenga su propia 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 implementó la corrección oficial en e090be9f73a7a71459ef678c.
Los desarrolladores eran parcialmente conscientes del riesgo del puntero colgante. Este comentario existe en 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. */
Pero esto solo considera el escenario en el que la conexión se destruye antes que el easy handle. No tuvo en cuenta el escenario de destrucción de la needle durante la reutilización de la conexión, que es el desencadenante real.
| Fecha |
|---|
!defined(CURL_DISABLE_SMB)defined(USE_CURL_NTLM_CORE)sizeof(curl_off_t) > 4 (off_t de 64 bits, estándar en la mayoría de plataformas)SMB está habilitado por defecto en las compilaciones de curl que tienen soporte NTLM.
e090be9f73a7a71459ef678c777c5209dfCVE-2026-3805 - Corregido en curl 8.19.0. Afectadas: 8.13.0 hasta 8.18.0.
Daniel Wade - GitHub - [email protected]
| Evento |
|---|
| 2025-04-30 | 777c5209df refactoriza SMB para usar meta hash, introduciendo el bug |
| 2026-03-07 | Encontré el bug durante una auditoría de seguridad |
| 2026-03-08 | Reportado a curl vía HackerOne (#3591944) |
| 2026-03-08 | curl contacta a distros@openwall |
| 2026-03-11 | curl 8.19.0 publicado con la corrección, CVE-2026-3805 publicada |