Share
## https://sploitus.com/exploit?id=6B0F6C20-E0C4-5A6C-9C4B-A875D999DBC1
# CVE-2026-3805: Use-After-Free in curl SMB Connection Reuse

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 | Medium |


## TL;DR

`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.


---


## Background: curl Connection Reuse

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:

1. Create a temporary "needle" connection with the new request's parameters
2. Search the connection cache for a matching connection
3. If found: reuse the existing connection, **destroy the needle**
4. If not found: the needle becomes the actual connection

The SMB protocol handler stores state in two places:
- `smbc` (connection state) on the connection's `meta_hash`
- `req` (request state) on the easy handle's `meta`

The 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.


## The Bug

In `smb_parse_url_path()`, the code parses the SMB URL path and splits it into
share name and file path:

```c
// lib/smb.c, smb_parse_url_path() line 431:
smbc->share = curlx_strdup((*path == '/' || *path == '\\') ? path + 1 : path);
// ...
*slash++ = 0;
req->path = slash;   // 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:

```c
// 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:

```c
// 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
```


## Impact

### Information Disclosure

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.

### Denial of Service

If the freed memory has been returned to the OS (unmapped), `strlen()` triggers
a SIGSEGV/access violation.

### Secondary Bug: Wrong Share

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.


---


## Reproduction

### Via curl CLI

```bash
# 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
```

### Via libcurl (multi-handle)

```c
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
```

### With AddressSanitizer

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](poc/REPRODUCE_UAF.sh) for the full reproduction script.


---


## The Fix

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:

```diff
--- 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`.


---


## Developer Awareness

The developers were partially aware of the dangling pointer risk. This comment
exists in `smb_easy_dtor()`:

```c
/* `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.


---


## Timeline

| Date | Event |
|------|-------|
| 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 |


---


## Affected Configuration

- SMB must be enabled: `!defined(CURL_DISABLE_SMB)`
- NTLM core must be available: `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.


---


## Resources

- Fix commit: [`e090be9f73a7a71459ef678c`](https://github.com/curl/curl/commit/e090be9f73a7a71459ef678c)
- Official advisory: [curl.se/docs/CVE-2026-3805.html](https://curl.se/docs/CVE-2026-3805.html)
- HackerOne report: [#3591944](https://hackerone.com/reports/3591944)
- Introducing commit: [`777c5209df`](https://github.com/curl/curl/commit/777c5209df)


---


*CVE-2026-3805 - Fixed in curl 8.19.0. Affected: 8.13.0 through 8.18.0.*

*Daniel Wade - [GitHub](https://github.com/Rat5ak) - danjwade95@gmail.com*