CVE-2026-70367: Stunnel: ssrf bypass in stunnel socks proxy via ipv4-mapped ipv6 loopback and unspecified addresses allows access to loopback-only services
A Server-Side Request Forgery (SSRF) bypass vulnerability exists in “stunnel” 5.79 and lower when configured in SOCKS proxy mode. This flaw allows a client to bypass intended localhost restrictions by using IPv4-mapped IPv6 addresses (e.g., “::ffff:127.0.0.1”) or unspecified addresses ("0.0.0.0", "::"), enabling access to loopback-only services on the "stunnel" host that should not be network-reachable.
Other sources
AIONLYREPORT package: stunnel-5.72-8.el10 ------ Summary: SSRF Bypass in SOCKS Proxy via 0.0.0.0 and IPv4-mapped IPv6 Addresses: stunnel configured with protocol = socks rejects only 127.0.0.0/8 and ::1, allowing a client to bypass the localhost filter via ::ffff:127.0.0.1 and, on some platforms, 0.0.0.0 or :: to reach loopback-only services on the stunnel host. Requirements to exploit: The package must be deployed in SOCKS server mode (protocol = socks), the attacker must be able to reach that listener, and a service of interest must be bound to loopback on the stunnel host. Some deployments may additionally gate access with network policy or TLS client authentication. The strongest reproduced bypass uses ::ffff:127.0.0.1; the 0.0.0.0 and :: variants are additional edge cases and may be OS-dependent. Component affected: stunnel-5.72-8.el10, src/protocol.c, validateconnectaddr() in the SOCKS server request path Version affected: stunnel-5.72-8.el10 when configured with protocol = socks Patch available: no released package fix established; proposed patch included below Version fixed: unknown Upstream coordination: Not notified. CVSS: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:L/A:L - 8.1 (HIGH) AV:N - A reachable protocol = socks listener can be attacked over the network with a crafted SOCKS request. AC:L - The bypass only requires a crafted destination address and no race or unusual precondition once the listener is reachable. PR:N - The SOCKS server path accepts the 0x00 no-authentication method; no prior privileges on the host are required. UI:N - No user interaction is required. S:U - The issue is an access-control failure in stunnel's own destination validation and is scored within that service boundary. C:H - Successful exploitation can expose loopback-only administrative, metadata, or data-bearing services that were intentionally not network-reachable. I:L - Some local services may accept state-changing requests once the loopback-only boundary is bypassed, but that impact is deployment-dependent. A:L - Unintended requests to local services can also create limited service disruption or load. Impact: Important. When stunnel is deployed as a reachable SOCKS server, a client that can reach the listener can bypass the intended localhost restriction and access services that should only be reachable from the local host. Under Red Hat's guidance this is closer to Important than Moderate because it can expose protected resources to a remote client of the vulnerable service and may enable limited integrity or availability impact as a follow-on. It is not Critical because it does not by itself provide system compromise or code execution, and it is limited to the supported but non-default protocol = socks configuration. Embargo: no Reason: The issue is limited to deployments that intentionally enable protocol = socks, and operators can immediately reduce exposure by disabling or tightly restricting the SOCKS listener. It is serious for affected deployments, but it is not a default-path or wormable compromise. Acknowledgement: Aisle Research Vulnerability Details: The directly observed flaw is an incomplete destination filter in validateconnectaddr(). The function rejects IPv4 loopback (127.0.0.0/8) and exact IPv6 loopback (::1), but it does not reject IPv4-mapped IPv6 loopback such as ::ffff:127.0.0.1, and it also leaves the unspecified addresses 0.0.0.0 and :: unchecked. c NOEXPORT int validateconnectaddr(CLI c) { #ifdef USEIPv6 const unsigned char ipv6loopback[16]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}; #endif unsigned i; for(i=0; i<c->connectaddr.num; ++i) { SOCKADDRUNION addr=&c->connectaddr.addr[i]; #ifdef USEIPv6 if(addr->sa.safamily==AFINET6) { if(!memcmp(&addr->in6.sin6addr, ipv6loopback, 16)) { slog(LOGERR, "SOCKS connection to the IPv6 loopback rejected"); return 0; } / TODO: implement more checks / } else #endif if(addr->sa.safamily==AFINET) { if((ntohl(addr->in.sinaddr.saddr)&0xff000000)==0x7f000000) { slog(LOGERR, "SOCKS connection to the IPv4 loopback rejected"); return 0; } / TODO: implement more checks / } else { slog(LOGERR, "Unsupported address type 0x%02x", addr->sa.safamily); return 0; } } return 1; } The same validation helper is used before the normal outbound connect path (connectremote() -> sconnect() -> connect()). As a result, a client can supply ::ffff:127.0.0.1 and reach a service bound only to loopback on the stunnel host. The 0.0.0.0 and :: variants should be treated more cautiously because their behavior is OS-dependent, but they remain unfiltered edge cases. The strongest reproduced case is the IPv4-mapped IPv6 loopback path on IPv6-enabled builds. The downstream impact depends on what services are bound to loopback and whether those services apply their own authentication. Steps to reproduce: 1. Configure stunnel SOCKS server: ini [socksserver] protocol = socks accept = 127.0.0.1:9080 cert = stunnel.pem key = stunnel.key 2. Start a localhost-only target service on the same host: bash nc -lv 127.0.0.1 18080 3. Open a TLS session to stunnel and perform SOCKS5 handshake: bash openssl sclient -quiet -connect 127.0.0.1:9080 4. Send method negotiation bytes 05 01 00. 5. Send a connect request to ::ffff:127.0.0.1:18080: 05 01 00 04 00000000000000000000ffff7f000001 46a0 6. Observe stunnel replies success (REP=0x00) and data reaches the localhost listener. 7. Repeat with 0.0.0.0 / :: as additional edge cases; these may be OS-dependent, while IPv4-mapped IPv6 loopback is the strongest reproducible bypass case. Mitigation: If SOCKS proxying is not required, disable protocol = socks. Otherwise do not expose the SOCKS listener to untrusted clients, keep it bound to loopback or a trusted management network where possible, and enforce any available client-authentication or network ACL controls until a code fix is shipped. Proposed Fix: A minimal fix is to reject IPv4 and IPv6 unspecified addresses and to detect IPv4-mapped IPv6 loopback before returning success from validateconnectaddr(). diff diff --git a/src/protocol.c b/src/protocol.c @@ NOEXPORT int validateconnectaddr(CLI c) { #ifdef USEIPv6 const unsigned char ipv6loopback[16]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}; + const unsigned char ipv6any[16]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; #endif @@ #ifdef USEIPv6 if(addr->sa.safamily==AFINET6) { if(!memcmp(&addr->in6.sin6addr, ipv6loopback, 16)) { slog(LOGERR, "SOCKS connection to the IPv6 loopback rejected"); return 0; } + if(!memcmp(&addr->in6.sin6addr, ipv6any, 16)) { + slog(LOGERR, + "SOCKS connection to the IPv6 unspecified address rejected"); + return 0; + } + if(!memcmp(addr->in6.sin6addr.s6addr, + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff", 12) && + ((addr->in6.sin6addr.s6addr[12] & 0xff)==0x7f)) { + slog(LOGERR, + "SOCKS connection to the IPv4-mapped IPv6 loopback rejected"); + return 0; + } / TODO: implement more checks / } else #endif if(addr->sa.safamily==AFINET) { + if(ntohl(addr->in.sinaddr.saddr)==0x00000000) { + slog(LOGERR, + "SOCKS connection to the IPv4 unspecified address rejected"); + return 0; + } if((ntohl(addr->in.sinaddr.saddr)&0xff000000)==0x7f000000) { slog(LOGERR, "SOCKS connection to the IPv4 loopback rejected"); return 0; } ------ This report was generated using AI technology. Always review AI-generated content prior to use
— Red Hat
Affected Software
Remediation
Recommended actions to resolve this vulnerability, in priority order.
- Configuration
If SOCKS proxying is not required, disable the SOCKS proxy configuration in stunnel (i.e., do not configure `protocol = socks`).
stunnel protocol = disable or do not use protocol = socks - Configuration
Apply the proposed minimal code behavior in `validate_connect_addr()` to reject IPv4/IPv6 unspecified addresses and IPv4-mapped IPv6 loopback before returning success (the report notes the flaw leaves `0.0.0.0` and `::` unchecked and rejects only certain other cases).
stunnel (src/protocol.c) validate_connect_addr() destination address checks = reject IPv4 unspecified (0.0.0.0), IPv6 unspecified (::), and IPv4-mapped IPv6 loopback (::ffff:127.0.0.1) - Compensating control
Do not expose the stunnel SOCKS listener to untrusted clients; instead bind it to localhost only (e.g., `accept = 127.0.0.1:9080`) or a trusted management network, and gate access with network policy / ACLs until a code fix is available.
- Compensating control
If you must keep a reachable SOCKS listener, enforce any available client-authentication or network ACL controls in front of it; keep it disabled/tightly restricted until the SOCKS destination validation is corrected.
Event History
Frequently Asked Questions
What is the severity of CVE-2026-70367?
The severity of CVE-2026-70367 is rated as medium with a score of 5.4.
How do I fix CVE-2026-70367?
To fix CVE-2026-70367, update to stunnel version 5.80 or later, which addresses the SSRF bypass vulnerability.
What type of vulnerability is CVE-2026-70367?
CVE-2026-70367 is classified as a Server-Side Request Forgery (SSRF) bypass vulnerability.
What does CVE-2026-70367 allow an attacker to do?
CVE-2026-70367 allows an attacker to bypass localhost restrictions and access loopback-only services using specific addresses.
Which versions of stunnel are affected by CVE-2026-70367?
Versions of stunnel prior to 5.80 are affected by CVE-2026-70367.