CVE-2023-38545
SOCKS5 heap buffer overflow
Project curl Security Advisory, October 11 2023 - Permalink
VULNERABILITY
This flaw makes curl overflow a heap based buffer in the SOCKS5 proxy handshake.
When curl is asked to pass along the hostname to the SOCKS5 proxy to allow that to resolve the address instead of it getting done by curl itself, the maximum length that hostname can be is 255 bytes.
If the hostname is detected to be longer than 255 bytes, curl switches to local name resolving and instead passes on the resolved address only to the proxy. Due to a bug, the local variable that means "let the host resolve the name" could get the wrong value during a slow SOCKS5 handshake, and contrary to the intention, copy the too long hostname to the target buffer instead of copying just the resolved address there.
TERMINOLOGY
The curl library is known as libcurl and the command line tool that uses the library is known as the curl tool. Either or both may be referred to as just curl. The distinctive names are used in this document when necessary.
INFO
The hostname comes from the URL that curl has been told to operate with.
The target buffer is the heap-based download buffer in libcurl that
is reused for SOCKS negotiation before the transfer has started. The
size of the buffer is 16kB by default, but can be set to different sizes
by the application. The curl tool sets it to 102400 bytes by default -
but it sets the buffer size to a smaller size if
--limit-rate
is set lower than 102400 bytes per second.
libcurl provides the CURLOPT_BUFFERSIZE
option to change
the size of the download buffer.
libcurl accepts hostnames up to 65535 bytes in the URL.
If the used hostname is longer than the target buffer, there is a
memcpy()
that overwrites the buffer into the heap. The URL
parser and possibly an IDN library (if curl is built with one) have to
accept the hostname, which somewhat limits the set of available byte
sequences that can be used in the copy.
For an overflow to happen it needs a slow enough SOCKS5 handshake to trigger the local variable bug, and the client using a hostname longer than the download buffer. Perhaps with a malicious HTTPS server doing a redirect to an especially crafted URL.
Typical server latency is likely "slow" enough to trigger this bug without an attacker needing to influence it by DoS or SOCKS server control.
An overflow is only possible in applications that do not set
CURLOPT_BUFFERSIZE
or set it smaller than 65541. Since the
curl tool sets CURLOPT_BUFFERSIZE
to 100kB by default it is
not vulnerable unless rate limiting was set by the user to a rate
smaller than 65541 bytes/second.
The options that cause SOCKS5 with remote hostname to be used in libcurl:
CURLOPT_PROXYTYPE
set to typeCURLPROXY_SOCKS5_HOSTNAME
, or:CURLOPT_PROXY
orCURLOPT_PRE_PROXY
set to use the schemesocks5h://
- One of the proxy environment variables can be set to use the
socks5h://
scheme. For examplehttp_proxy
,HTTPS_PROXY
orALL_PROXY
.
The options that cause SOCKS5 with remote hostname to be used in the curl tool:
--socks5-hostname
, or:--proxy
or--preproxy
set to use the schemesocks5h://
- Environment variables as described in the libcurl section.
This bug was introduced when the SOCKS5 handshake code was converted from a blocking function into a non-blocking state machine.
The analysis in this section is specific to curl version 8. Some older versions of curl version 7 have less restriction on hostname length and/or a smaller SOCKS negotiation buffer size that cannot be overridden by CURLOPT_BUFFERSIZE.
The Common Vulnerabilities and Exposures (CVE) project has assigned the name CVE-2023-38545 to this issue.
CWE-122: Heap-based Buffer Overflow
Severity: High
HackerOne: https://hackerone.com/reports/2187833
ADDITIONAL INFO
Since the posting of this advisory, security researcher RyotaK has notified that even if the buffer size is large enough to prevent heap overflow an attacker can still use the integer overflow of hostname length in conjunction with a crafted hostname longer than 255 characters to make the handshake complete successfully.
In this scenario the crafted hostname contains a destination hostname, port and arbitrary data. The SOCKS server successfully connects to that host, sends the arbitrary data and then curl sends the "expected" request data on that same connection. The impact is limited because curl does not allow control characters and null in the hostname.
AFFECTED VERSIONS
- Affected versions: libcurl 7.69.0 to and including 8.3.0
- Not affected versions: libcurl < 7.69.0 and >= 8.4.0
- Introduced-in: https://github.com/curl/curl/commit/4a4b63daaa
libcurl is used by many applications, but not always advertised as such!
SOLUTION
Starting in curl 8.4.0, curl no longer switches to local resolve mode if the name is too long but is instead rightfully returning an error.
Patch collection for older versions
RECOMMENDATIONS
A - Upgrade curl to version 8.4.0
B - Apply the patch to your local version
C - Do not use CURLPROXY_SOCKS5_HOSTNAME
proxies with
curl
D - Do not set a proxy environment variable to socks5h://
TIMELINE
This issue was reported to the curl project on September 30, 2023. We contacted distros@openwall on October 3, 2023.
libcurl 8.4.0 was released on October 11 2023, coordinated with the publication of this advisory.
CREDITS
- Reported-by: Jay Satiro
- Patched-by: Jay Satiro
Thanks a lot!