
CVE-2026-3805: Use-After-Free in curl SMB connection reuse - heap info disclosure
I found a use-after-free vulnerability in libcurl's SMB protocol handler. When
a second SMB transfer reuses an existing connection to the same server, the
file path for the new request (req->path) is a dangling pointer to freed heap
memory. This memory is read via strlen() and copied into an outgoing SMB
packet, leaking heap contents to the server or crashing.
One-line fix: make req->path own its own copy instead of borrowing from the
needle's smbc->share.
| CVE | CVE-2026-3805 |
| Bug class | Use-After-Free (CWE-416) |
| Root cause | req->path points into needle's smbc->share, freed on connection reuse |
| Introduced | 777c5209df (2025-04-30) - curl 8.13.0 |
| Fixed | e090be9f73a7a71459ef678c - curl 8.19.0 (Mar 11, 2026) |
| Affected | curl 8.13.0 through 8.18.0 |
| Impact | Heap information disclosure to server, crash |
| Severity | 7.5 - HIGH - CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H |
smb_setup_connection() runs on a temporary "needle" connection used for
cache lookup. It sets req->path to point inside smbc->share (heap memory
owned by the needle). When the connection cache finds a reusable connection,
the needle is destroyed - freeing smbc->share - but req->path survives on
the easy handle, now dangling.
When smb_send_open() builds the SMB OPEN request, it calls strlen(req->path)
and copies the result into the outgoing packet. Whatever data is in that freed
heap region goes to the server. If the attacker controls the server (SSRF, or
user connecting to malicious SMB), they receive the leaked heap contents as the
"filename" in the SMB NT_CREATE_ANDX request.
libcurl aggressively reuses connections for performance. When you make multiple requests to the same host, curl checks if an existing connection can be reused rather than establishing a new one. The mechanism works like this:
The SMB protocol handler stores state in two places:
smbc (connection state) on the connection's meta_hashreq (request state) on the easy handle's metaThe bug: req->path is set to point inside smbc->share during needle
setup. When the needle is destroyed on reuse, smbc->share is freed, but
req->path still points there.
In smb_parse_url_path(), the code parses the SMB URL path and splits it into
share name and file path:
// 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
For URL smb://server/share1/file1.txt, this creates:
smbc->share = "share1\0file1.txt" (heap allocated, owned by needle)req->path = pointer to "file1.txt" (inside smbc->share)When connection reuse triggers:
// 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() calls Curl_hash_destroy(&conn->meta_hash) which invokes
smb_conn_dtor(), freeing smbc->share. But req->path (on the easy handle,
which survives) still points into the now-freed memory.
When the SMB request proceeds:
// 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
The freed heap memory may have been reallocated and contain sensitive data.
strlen(req->path) scans forward until it hits a null byte, and
curlx_strcopy() copies that content into the SMB packet sent to the server.
Attack scenario: SSRF where the attacker controls the SMB server. The victim application makes two SMB requests to the attacker's server. The second request leaks heap contents as the "filename" in the SMB OPEN request.
If the freed memory has been returned to the OS (unmapped), strlen() triggers
a SIGSEGV/access violation.
Even without the UAF, SMB connection reuse is semantically broken.
smb_send_tree_connect() uses smbc->share from the reused connection (the
old share), not the new request's share. The TREE_CONNECT goes to the wrong
share entirely.
# 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
Build curl with -fsanitize=address and run the above:
==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
See poc/REPRODUCE_UAF.sh for the full reproduction script.
The fundamental issue is that req->path borrows a pointer into memory owned
by the needle's smbc. The fix makes req->path own its own copy:
--- 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 implemented the official fix in e090be9f73a7a71459ef678c.
The developers were partially aware of the dangling pointer risk. This comment
exists 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. */
But this only considers the scenario where the connection is destroyed before the easy handle. It missed the needle destruction during connection reuse scenario, which is the actual trigger.
| Date | Event |
|---|
!defined(CURL_DISABLE_SMB)defined(USE_CURL_NTLM_CORE)sizeof(curl_off_t) > 4 (64-bit off_t, standard on most platforms)SMB is enabled by default in curl builds that have NTLM support.
e090be9f73a7a71459ef678c777c5209dfCVE-2026-3805 - Fixed in curl 8.19.0. Affected: 8.13.0 through 8.18.0.
Daniel Wade - GitHub - [email protected]
| 2025-04-30 | 777c5209df refactors SMB to use meta hash, introducing the bug |
| 2026-03-07 | I find the bug during security audit |
| 2026-03-08 | Reported to curl via HackerOne (#3591944) |
| 2026-03-08 | curl contacts distros@openwall |
| 2026-03-11 | curl 8.19.0 released with fix, CVE-2026-3805 published |