Feb 18, 2026·7 min read·5 visits
A critical heap overflow in curl's SOCKS5h negotiation. If you use `socks5h://` and connect to a malicious URL via a slow proxy, a long hostname can overflow the heap buffer. Update to curl 8.4.0 immediately.
CVE-2023-38545 is a high-severity heap-based buffer overflow in curl's SOCKS5 proxy handshake implementation. It affects libcurl versions 7.69.0 through 8.3.0. The vulnerability is triggered when curl is configured to use a SOCKS5 proxy with remote hostname resolution (`socks5h://`) and encounters a slow handshake delay. This race-like condition in the non-blocking state machine allows a hostname up to 65,535 bytes to be copied into a much smaller heap buffer (default 16kB in libcurl), leading to heap corruption, Denial of Service (DoS), or potential Remote Code Execution (RCE).
Let's be honest: we all trust curl blindly. It is the duct tape of the internet. It runs in your car, your fridge, your server, and probably your smart toaster. When Daniel Stenberg ships code, the world assumes it's rock solid. But in October 2023, the security community held its collective breath as the curl project announced a "high severity" issue that felt suspiciously like the 90s calling to ask for its buffer overflow back.
This isn't your garden-variety input validation error. CVE-2023-38545 is a nasty heap-based buffer overflow hiding deep within the SOCKS5 proxy handshake state machine. Specifically, it targets the socks5h:// scheme—the mode where you tell the proxy to resolve the hostname remotely because you don't trust your local DNS (or you're using Tor).
Here is the kicker: the vulnerability relies on latency. It is a state machine desync that only triggers when the network is slow enough to force curl to pause and resume its handshake. It is a bug that lives in the cracks of asynchronous I/O, waiting for a lag spike to turn a harmless connection into a remote shell.
To understand this bug, you have to appreciate the complexity of non-blocking I/O. Back in version 7.69.0, curl refactored its SOCKS handshake to be fully non-blocking. This means instead of halting execution while waiting for a packet, curl saves its state, returns control to the application, and resumes later when data arrives. State machines are notoriously hard to get right because you have to remember everything perfectly between pauses.
Here is the logic trap: The SOCKS5 protocol limits hostnames to 255 bytes. Curl knows this. The developers added logic saying, "If the hostname is longer than 255 bytes, we can't send it to the proxy. Instead, let's resolve it locally to an IP and send that."
Sound logic, right? The problem is where this check lived. The decision to switch to local resolution happened inside the state machine. If the handshake was fast, it worked fine. But if the handshake was slow—meaning curl had to return and re-enter the function—the local variable tracking this decision wasn't persisted correctly, or rather, the code path taken upon resumption bypassed the check entirely.
When the state machine resumed, it suffered a bout of amnesia. It forgot it was supposed to switch to local resolution for the massive hostname. Instead, it proceeded down the "remote resolve" path, happily attempting to stuff a hostname up to 65,535 bytes into a buffer that was never designed to hold it.
The vulnerability lies in lib/socks.c. Curl reuses its generic download buffer for the SOCKS handshake. In libcurl, this buffer defaults to a measly 16kB (16,384 bytes). In the CLI tool, it's usually 100kB. But the SOCKS5 spec allows a hostname length (in the URL) of up to 65k.
The vulnerable logic looked something like this (simplified):
// Vulnerable logic in lib/socks.c
if(!socks5_resolve_local && hostname_len > 255) {
// We are SUPPOSED to switch to local resolve here
// because the name is too long for SOCKS5.
socks5_resolve_local = TRUE;
}
// ... later down the code path ...
if(!socks5_resolve_local) {
// If we are here, we copy the hostname to the buffer.
// BUG: If state machine re-entered, we might reach here
// with a huge hostname_len but socks5_resolve_local still FALSE.
memcpy(buffer, hostname, hostname_len);
}The fix was brutal and effective. Instead of trying to be clever and switch modes on the fly, the patch simply says: "If the hostname is too long for SOCKS5, kill the connection."
// Patched logic in commit fb4415d8aee6c15af8ef7b4c6a6d65d6c8274831
if(!socks5_resolve_local && hostname_len > 255) {
failf(data, "SOCKS5: the destination hostname is too long to be "
"resolved remotely by the proxy.");
return CURLPX_LONG_HOSTNAME;
}By returning a hard error (CURLPX_LONG_HOSTNAME), they eliminated the possibility of the code falling through to the memcpy. It's a classic example of security over usability: fail safe, don't fail open.
Exploiting this is a work of art. You can't just throw a long string at the server; you have to choreograph a network dance. Here is how a researcher triggers the heap overflow:
export ALL_PROXY=socks5h://localhost:9050).Location header points to a URL with a massive hostname: http://AAAAAAAA...[20,000 bytes].../.Hello bytes... slowly. Byte... pause... byte.This delay forces curl's state machine to yield CURL_SOCKET_BAD and return control to the main loop. When it resumes, the logic flaw kicks in. Curl skips the "switch to local" logic and blindly memcpys the 20,000 'A's into the 16kB heap buffer. Boom. You have just overwritten the heap metadata and whatever structures lay adjacent to the download buffer.
So, is the internet burning? Yes and no.
For the curl command-line tool: The default buffer is 100kB. You would need a hostname longer than 100kB to overflow it. Since the maximum URL length is usually capped around 65k, the CLI tool is mostly safe from a pure overflow unless the user deliberately set a very low rate limit (e.g., --limit-rate 10k), which shrinks the buffer size dynamically.
For libcurl applications: This is where the fire is. The default buffer is 16kB. A 20kB hostname is trivial to generate. If you have an application (like a scraper, a bot, or a microservice) that uses libcurl with socks5h, it is highly vulnerable.
Successful exploitation means Remote Code Execution (RCE). The attacker controls the data being written (the hostname) and the length. With modern heap grooming (Heap Feng Shui), an attacker can overwrite function pointers or C++ vtables on the heap to hijack control flow. At the very least, this is a guaranteed Denial of Service (DoS) for any affected service.
There is no fancy workaround here that doesn't involve crippling your functionality.
The Solution: Upgrade to curl 8.4.0 or later. Do it now.
If you absolutely cannot patch (and I judge you for it), you can mitigate this by:
socks5:// instead of socks5h://. This forces local DNS resolution, which bypasses the vulnerable code path entirely. However, this leaks DNS queries to your local network, which might defeat the purpose of using a proxy (especially for Tor users).libcurl in your own code, you can set CURLOPT_BUFFERSIZE to something larger than 65,535 bytes. This ensures the buffer is always large enough to hold the maximum possible hostname, neutralizing the overflow.But seriously, just apt-get update.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
libcurl haxx | 7.69.0 - 8.3.0 | 8.4.0 |
curl haxx | 7.69.0 - 8.3.0 | 8.4.0 |
| Attribute | Detail |
|---|---|
| CWE | CWE-787 (Heap-based Buffer Overflow) |
| Attack Vector | Network (Requires Malicious Server + Proxy) |
| CVSS v3.1 | 9.8 (Critical) |
| Impact | RCE / DoS |
| Buffer Size (libcurl) | 16kB (Default) |
| Max Hostname | 65,535 bytes |
| Exploit Status | Proof-of-Concept Available |
The software writes data past the end, or before the beginning, of the intended buffer on the heap.