VYPR
Unrated severityNVD Advisory· Published May 25, 2026

SOCKS5 TLS upgrade ignores caller timeout in hackney

CVE-2026-47071

Description

Uncontrolled Resource Consumption vulnerability in benoitc hackney allows Flooding. The SOCKS5 transport in src/hackney_socks5.erl correctly applies the caller-supplied timeout to the SOCKS5 negotiation phase, but then upgrades the connection to TLS using the two-argument form ssl:connect/2, which defaults to an infinite timeout. The Timeout value is in scope at the call site but is not forwarded. A hostile SOCKS5 proxy that completes the SOCKS5 handshake normally and then goes silent (or sends a partial TLS ServerHello and stalls) will cause the connecting process to block indefinitely, regardless of the connect_timeout or recv_timeout options supplied by the caller.

This issue affects hackney: from 0.10.0 before 4.0.1.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Hackney SOCKS5 TLS upgrade uses infinite timeout, allowing a hostile proxy to block connections indefinitely and cause denial of service.

Vulnerability

An uncontrolled resource consumption (CWE-400) vulnerability exists in the SOCKS5 transport of the Erlang HTTP client library hackney. In src/hackney_socks5.erl, after a successful SOCKS5 negotiation, the connection is upgraded to TLS using the two-argument form ssl:connect/2, which defaults to an infinite timeout. The caller-supplied Timeout value is in scope at the call site but is not forwarded. This affects all hackney versions from 0.10.0 up to (but not including) 4.0.1 [1][2][4].

Exploitation

An attacker needs to control or impersonate a SOCKS5 proxy that the victim uses. The hostile proxy completes the SOCKS5 handshake normally and then either remains silent (never sending a TLS ServerHello) or sends a partial ServerHello and stalls. While the SOCKS5 negotiation phase respects the caller's connect_timeout or recv_timeout, the subsequent ssl:connect/2 call has no timeout, so the connecting Erlang process blocks indefinitely, even if the caller set explicit short timeouts [2][4].

Impact

A successful exploit causes a denial of service by consuming an Erlang process and socket indefinitely for each such connection. The attacker can exhaust system resources by establishing many such blocked connections. This affects any HTTPS request routed through the attacker's SOCKS5 proxy, and the connect_timeout and recv_timeout options provide no protection during the TLS upgrade phase. The CVSS v4.0 score is 8.2 (HIGH) [2][4].

Mitigation

The fix is implemented in hackney version 4.0.1, released with commit 5ccdab7. The patch modifies src/hackney_socks5.erl to forward the caller's Timeout to ssl:connect/3 for the post-SOCKS5 TLS upgrade, and also to the proxy TLS handshake in connect_to_proxy/6. Users should upgrade to 4.0.1 or later. No workaround is available for versions that cannot be upgraded [1][3][4].

AI Insight generated on May 25, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected products

2
  • Benoitc/Hackneyinferred2 versions
    >=0.10.0,<4.0.1+ 1 more
    • (no CPE)range: >=0.10.0,<4.0.1
    • (no CPE)range: >=0.10.0, <4.0.1

Patches

1
5ccdab725c56

fix(security): forward timeout to proxy TLS upgrades (GHSA-gp9c)

https://github.com/benoitc/hackneyBenoit ChesneauMay 20, 2026via body-scan
2 files changed · +16 10
  • src/hackney_http_connect.erl+11 8 modified
    @@ -56,7 +56,7 @@ connect(ProxyHost, ProxyPort, Opts, Timeout)
       ConnectOpts = hackney_util:filter_options(Opts, AcceptedOpts, BaseOpts),
     
       %% connect to the proxy (TCP or TLS based on proxy_transport)
    -  case connect_to_proxy(ProxyHost, ProxyPort, ProxyTransport, ConnectOpts, Opts) of
    +  case connect_to_proxy(ProxyHost, ProxyPort, ProxyTransport, ConnectOpts, Opts, Timeout) of
         {ok, ProxySocket} ->
           case do_handshake(ProxySocket, ProxyTransport, Host, Port, Opts) of
             ok ->
    @@ -65,8 +65,10 @@ connect(ProxyHost, ProxyPort, Opts, Timeout)
               case Transport of
                 hackney_ssl ->
                   SSLOpts = ssl_opts(Host, Opts),
    -              %% upgrade the tunnel to TLS
    -              case ssl:connect(ProxySocket, SSLOpts) of
    +              %% upgrade the tunnel to TLS. GHSA-gp9c: forward the
    +              %% caller's timeout so a stalled proxy upstream cannot pin
    +              %% the process on the TLS handshake forever.
    +              case ssl:connect(ProxySocket, SSLOpts, Timeout) of
                     {ok, SslSocket} ->
                       {ok, {Transport, SslSocket}};
                     Error ->
    @@ -92,14 +94,15 @@ connect(ProxyHost, ProxyPort, Opts, Timeout)
       end.
     
     %% Connect to proxy using TCP or TLS
    -connect_to_proxy(ProxyHost, ProxyPort, ssl, ConnectOpts, Opts) ->
    +connect_to_proxy(ProxyHost, ProxyPort, ssl, ConnectOpts, Opts, Timeout) ->
       %% HTTPS proxy: connect with TLS
    -  case hackney_happy:connect(ProxyHost, ProxyPort, ConnectOpts) of
    +  case hackney_happy:connect(ProxyHost, ProxyPort, ConnectOpts, Timeout) of
         {ok, TcpSocket} ->
           ProxySslOpts = proplists:get_value(proxy_ssl_options, Opts, []),
           %% Add SNI for the proxy
           SslOpts = [{server_name_indication, ProxyHost} | ProxySslOpts],
    -      case ssl:connect(TcpSocket, SslOpts) of
    +      %% GHSA-gp9c: forward the timeout to the proxy TLS handshake too.
    +      case ssl:connect(TcpSocket, SslOpts, Timeout) of
             {ok, SslSocket} ->
               {ok, SslSocket};
             Error ->
    @@ -109,9 +112,9 @@ connect_to_proxy(ProxyHost, ProxyPort, ssl, ConnectOpts, Opts) ->
         Error ->
           Error
       end;
    -connect_to_proxy(ProxyHost, ProxyPort, tcp, ConnectOpts, _Opts) ->
    +connect_to_proxy(ProxyHost, ProxyPort, tcp, ConnectOpts, _Opts, Timeout) ->
       %% HTTP proxy: plain TCP connection
    -  hackney_happy:connect(ProxyHost, ProxyPort, ConnectOpts).
    +  hackney_happy:connect(ProxyHost, ProxyPort, ConnectOpts, Timeout).
     
     %% Close proxy socket based on transport type
     close_proxy_socket(Socket, ssl) ->
    
  • src/hackney_socks5.erl+5 2 modified
    @@ -61,8 +61,11 @@ connect(Host, Port, Opts, Timeout) when is_list(Host), is_integer(Port),
               case Transport of
                 hackney_ssl ->
                   SSLOpts = ssl_opts(Host, Opts),
    -              %% upgrade the tcp connection
    -              case ssl:connect(Socket, SSLOpts) of
    +              %% upgrade the tcp connection. GHSA-gp9c: forward the
    +              %% caller's timeout; the 2-arg ssl:connect defaults to
    +              %% infinity, so a stalled proxy upstream could pin the
    +              %% process and socket on the TLS handshake forever.
    +              case ssl:connect(Socket, SSLOpts, Timeout) of
                     {ok, SslSocket} ->
                       {ok, {Transport, SslSocket}};
                     Error ->
    

Vulnerability mechanics

Root cause

"The post-SOCKS5 TLS upgrade calls `ssl:connect/2` (infinite timeout) instead of forwarding the caller-supplied timeout, allowing a stalled proxy to block the process indefinitely."

Attack vector

An attacker controls or man-in-the-middles a SOCKS5 proxy used by the victim. The proxy completes the SOCKS5 greeting and CONNECT reply normally, then either stays silent or sends a partial TLS ServerHello and stalls [ref_id=2]. Because the post-SOCKS5 TLS upgrade uses `ssl:connect/2` (infinite timeout), the connecting Erlang process blocks indefinitely, ignoring any `connect_timeout` or `recv_timeout` options the caller set [ref_id=2]. Each stalled request permanently consumes one Erlang process and one socket, enabling a resource-exhaustion denial of service [ref_id=2].

Affected code

The bug is in `src/hackney_socks5.erl` and `src/hackney_http_connect.erl`. In `hackney_socks5.erl` the SOCKS5 negotiation correctly uses the caller-supplied `Timeout`, but the subsequent TLS upgrade calls `ssl:connect(Socket, SSLOpts)` (two-argument form), which defaults to an infinite timeout. The same pattern appears in `hackney_http_connect.erl` where `ssl:connect/2` is used for both the proxy-side TLS handshake and the tunnel TLS upgrade without forwarding the timeout [patch_id=2473707].

What the fix does

The patch changes every `ssl:connect/2` call in the proxy code paths to `ssl:connect/3`, forwarding the caller-supplied `Timeout` argument [patch_id=2473707]. In `src/hackney_socks5.erl` the `Timeout` variable was already in scope but unused; the patch passes it as the third argument to `ssl:connect/3` [patch_id=2473707]. In `src/hackney_http_connect.erl` the `Timeout` parameter is threaded through `connect_to_proxy/6` and used in both the proxy TLS handshake and the tunnel TLS upgrade [patch_id=2473707]. This ensures a stalled proxy cannot pin the process indefinitely — the TLS handshake now respects the caller's timeout.

Preconditions

  • configThe victim application must route an HTTPS request through a SOCKS5 proxy using hackney.
  • networkThe attacker must control or be able to man-in-the-middle the SOCKS5 proxy.
  • authNo authentication is required; the proxy merely needs to complete the SOCKS5 handshake and then stall.

Reproduction

Stand up a SOCKS5 proxy that completes the SOCKS5 greeting and CONNECT reply normally, then goes silent (never sends a TLS ServerHello). Issue an HTTPS request through it via hackney with `connect_timeout` and `recv_timeout` set to a short value (e.g. 2000 ms). Observe the calling process remains blocked well past the configured timeout, consuming a process and socket until killed externally [ref_id=2].

Generated on May 25, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

4

News mentions

0

No linked articles in our index yet.