CVE-2023-38545: curl SOCKS5 heap overflow via stale local resolve flag
dfbd7006-74e6-43f0-8a46-0439d8e7dcdd
CVE-2023-38545 — heap buffer overflow in curl's SOCKS5 proxy negotiation (lib/socks.c, do_SOCKS5). When using socks5h:// (CURLPROXY_SOCKS5_HOSTNAME) with a hostname longer than 255 bytes, curl is supposed to fall back to local DNS resolution because the SOCKS5 wire format only allots 1 byte for hostname length. The fallback is implemented by setting the function-local variable socks5_resolve_local = TRUE inside the CONNECT_SOCKS_INIT case (lib/socks.c:589-593). do_SOCKS5() runs as a non-blocking state machine and returns to the caller on EAGAIN. The local variable is reinitialized from conn->socks_proxy.proxytype on every call (line 573-574), so on a slow handshake the override is lost between re-entries. When state advances to CONNECT_RESOLVE_REMOTE (line 875), the code executes socksreq[len++] = (char)hostname_len; memcpy(&socksreq[len], sx->hostname, hostname_len); (lines 906-908), copying the full long hostname into data->state.buffer, a heap allocation as small as READBUFFER_MIN=1024 bytes. With an attacker-controlled long hostname (e.g. via HTTP redirect Location:), this overflows the heap chunk with attacker-controlled bytes. Exploitable for memory corruption / RCE depending on heap layout.
Glob lib/socks*.c → lib/socks.c is the primary target.
3. Grep socks5_resolve_local|Curl_SOCKS5|socks5_hostname in lib/socks.c → key hits at lines 573 (initialization), 589 (the >255 fallback override), 783 (CONNECT_REQ_INIT consumer), 883/906-908 (CONNECT_RESOLVE_REMOTE memcpy of hostname into socksreq).
4. Read lines 540-660 to see do_SOCKS5() body and confirmed socks5_resolve_local is a stack-local, recomputed every call from proxytype, with the override only applied in CONNECT_SOCKS_INIT.
5. Read lines 770-870 to follow CONNECT_REQ_INIT → CONNECT_RESOLVE_REMOTE flow and confirm absence of any hostname_len re-check on re-entry.
6. Read lines 875-912 to find the actual overflow primitive: socksreq[len++] = (char)hostname_len (truncates) followed by memcpy(&socksreq[len], sx->hostname, hostname_len) writing the untruncated length.
7. Grep READBUFFER_MIN in lib/urldata.h → 1024-byte minimum for state.buffer, confirms heap chunk can be <hostname size.
8. Re-entry trigger: socks_state_send / socks_state_recv return early with outstanding > 0 (line 624) on partial I/O, causing the caller to drive do_SOCKS5() again with state advanced.