
CVE-2026-3805: Use-After-Free dans la réutilisation de connexion SMB de curl - divulgation d'informations de tas
J'ai découvert une vulnérabilité use-after-free dans le gestionnaire de protocole SMB de libcurl. Lorsqu'un second transfert SMB réutilise une connexion existante au même serveur, le chemin du fichier pour la nouvelle requête (req->path) est un pointeur pendant vers de la mémoire tas libérée. Cette mémoire est lue via strlen() et copiée dans un paquet SMB sortant, divulguant le contenu du tas au serveur ou provoquant un crash.
Correctif en une ligne : faire en sorte que req->path possède sa propre copie plutôt que d'emprunter à smbc->share de l'aiguille.
| CVE | CVE-2026-3805 |
| Classe de bug | Use-After-Free (CWE-416) |
| Cause racine | req->path pointe vers smbc->share de l'aiguille, libéré lors de la réutilisation de connexion |
| Introduit | 777c5209df (2025-04-30) - curl 8.13.0 |
| Corrigé | e090be9f73a7a71459ef678c - curl 8.19.0 (11 mars 2026) |
| Affecté | curl 8.13.0 à 8.18.0 |
| Impact | Divulgation d'informations du tas au serveur, crash |
| Sévérité | 7.5 - ÉLEVÉE - CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H |
smb_setup_connection() s'exécute sur une connexion "needle" temporaire utilisée pour la recherche dans le cache. Elle définit req->path pour qu'il pointe à l'intérieur de smbc->share (mémoire tas appartenant à l'aiguille). Lorsque le cache de connexion trouve une connexion réutilisable, l'aiguille est détruite - libérant smbc->share - mais req->path survit sur le easy handle, maintenant pendant.
Quand smb_send_open() construit la requête SMB OPEN, elle appelle strlen(req->path) et copie le résultat dans le paquet sortant. Quelle que soit la donnée dans cette région tas libérée, elle part vers le serveur. Si l'attaquant contrôle le serveur (SSRF, ou utilisateur se connectant à un SMB malveillant), il reçoit le contenu du tas divulgué comme "nom de fichier" dans la requête SMB NT_CREATE_ANDX.
libcurl réutilise agressivement les connexions pour la performance. Lorsque vous faites plusieurs requêtes vers le même hôte, curl vérifie si une connexion existante peut être réutilisée plutôt que d'en établir une nouvelle. Le mécanisme fonctionne ainsi :
Le gestionnaire de protocole SMB stocke l'état à deux endroits :
smbc (état de connexion) sur le meta_hash de la connexionreq (état de requête) sur le meta du easy handleLe bug : req->path est défini pour pointer à l'intérieur de smbc->share lors de la configuration de l'aiguille. Lorsque l'aiguille est détruite lors de la réutilisation, smbc->share est libéré, mais req->path pointe toujours là.
Dans smb_parse_url_path(), le code analyse le chemin URL SMB et le divise en nom de partage et chemin de fichier :
// 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
Pour l'URL smb://server/share1/file1.txt, cela crée :
smbc->share = "share1\0file1.txt" (alloué sur le tas, appartient à l'aiguille)req->path = pointeur vers "file1.txt" (à l'intérieur de smbc->share)Quand la réutilisation de connexion se déclenche :
// 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() appelle Curl_hash_destroy(&conn->meta_hash) qui invoque smb_conn_dtor(), libérant smbc->share. Mais req->path (sur le easy handle, qui survit) pointe toujours vers la mémoire maintenant libérée.
Quand la requête SMB se déroule :
// 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 mémoire tas libérée peut avoir été réallouée et contenir des données sensibles. strlen(req->path) scanne vers l'avant jusqu'à rencontrer un octet nul, et curlx_strcopy() copie ce contenu dans le paquet SMB envoyé au serveur.
Scénario d'attaque : SSRF où l'attaquant contrôle le serveur SMB. L'application victime effectue deux requêtes SMB vers le serveur de l'attaquant. La seconde requête divulgue le contenu du tas comme "nom de fichier" dans la requête SMB OPEN.
Si la mémoire libérée a été rendue au système d'exploitation (non mappée), strlen() déclenche un SIGSEGV / violation d'accès.
Même sans l'UAF, la réutilisation de connexion SMB est sémantiquement cassée. smb_send_tree_connect() utilise smbc->share de la connexion réutilisée (l'ancien partage), pas le partage de la nouvelle requête. Le TREE_CONNECT va vers le mauvais partage complètement.
# 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
Construisez curl avec -fsanitize=address et exécutez ce qui précède :
==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
Voir poc/REPRODUCE_UAF.sh pour le script de reproduction complet.
Le problème fondamental est que req->path emprunte un pointeur vers la mémoire appartenant à smbc de l'aiguille. Le correctif fait en sorte que req->path possède sa propre copie :
--- 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 a implémenté le correctif officiel dans e090be9f73a7a71459ef678c.
Les développeurs étaient partiellement conscients du risque de pointeur pendant. Ce commentaire existe dans 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. */
Mais cela ne considère que le scénario où la connexion est détruite avant le easy handle. Il manque le scénario de destruction de l'aiguille lors de la réutilisation de connexion, qui est le déclencheur réel.
| Date | Événement |
|---|
!defined(CURL_DISABLE_SMB)defined(USE_CURL_NTLM_CORE)sizeof(curl_off_t) > 4 (off_t 64 bits, standard sur la plupart des plateformes)SMB est activé par défaut dans les builds curl qui ont le support NTLM.
e090be9f73a7a71459ef678c777c5209dfCVE-2026-3805 - Corrigé dans curl 8.19.0. Affecté : 8.13.0 à 8.18.0.
Daniel Wade - GitHub - [email protected]
| 2025-04-30 | 777c5209df refactorise SMB pour utiliser le hachage meta, introduisant le bug |
| 2026-03-07 | Je trouve le bug lors d'un audit de sécurité |
| 2026-03-08 | Signalé à curl via HackerOne (#3591944) |
| 2026-03-08 | curl contacte distros@openwall |
| 2026-03-11 | curl 8.19.0 publié avec le correctif, CVE-2026-3805 publié |