CVE-2026-47071: hackney SOCKS5 TLS Upgrade Infinite Timeout
When hackney routes an HTTPS request through a SOCKS5 proxy, the TLS handshake after the tunnel is established has no timeout, so a malicious or compromised proxy can stall the handshake forever and p
The problem
In `src/hackney_socks5.erl`, after a successful SOCKS5 negotiation, the code calls `ssl:connect/2` to upgrade the tunnel to TLS. The two-argument form of that function defaults its timeout to `infinity`.
The caller-supplied `Timeout` value is already in scope and is correctly used for the SOCKS5 recv calls earlier in the same function. It is simply not forwarded to the TLS step. Any `connect_timeout` or `recv_timeout` option the application sets is silently ignored during the TLS handshake, giving a false sense of safety.
Proof of concept
%% Vulnerable call in src/hackney_socks5.erl (before patch)
%% Timeout is in scope but NOT passed -- defaults to infinity
ssl:connect(Socket, SSLOpts)
%% ---- Trigger from the caller side ----
%% Route an HTTPS request through a SOCKS5 proxy that completes
%% the SOCKS5 greeting and CONNECT reply normally, then goes silent
%% (never sends a TLS ServerHello). The configured timeouts are ignored.
hackney:request(
get,
"https://target.example.com",
[],
<<>>,
[
{proxy, {socks5, "attacker-proxy.example.com", 1080}},
{connect_timeout, 2000},
{recv_timeout, 2000}
]
)
%% => calling process blocks indefinitely past both timeoutsThe root cause is a missing argument: `ssl:connect/2` is the OTP form that accepts only a socket and options and hard-codes the timeout to `infinity`. The patched version at commit 5ccdab7 changes the call to `ssl:connect(Socket, SSLOpts, Timeout)`, forwarding the timeout that was already threaded through the rest of the function.
No certificate forgery is required. The attacker only needs to control or intercept the SOCKS5 proxy, complete the SOCKS5 handshake normally, and then stall or drip the TLS ServerHello. Each stalled connection holds an Erlang process and a file descriptor for an unbounded duration, enabling denial of service through resource exhaustion (CWE-400).
The fix
Upgrade to hackney 4.0.1 or later. The patch commit (5ccdab725c561a6f03d05a51f2d0664f98236dae) replaces `ssl:connect(Socket, SSLOpts)` with `ssl:connect(Socket, SSLOpts, Timeout)` in `src/hackney_socks5.erl`. In rebar.config: `{hackney, "4.0.1"}`. In mix.exs: `{:hackney, "~> 4.0.1"}`.