CVE-2026-48858 Overview
CVE-2026-48858 is a Server-Side Request Forgery (SSRF) vulnerability [CWE-918] in the Erlang/OTP ftp application (ftp_internal module). The ftp_internal:handle_ctrl_result/2 PASV handler extracts the IP address from a server's 227 response and passes it directly to gen_tcp:connect/4 without validating that the address matches the control connection peer. A malicious or compromised FTP server can redirect the client's data channel to an arbitrary internal host and port. This enables SSRF against internal services and cloud metadata endpoints, as well as classic FTP bounce attacks against third-party hosts.
Critical Impact
Attackers controlling an FTP server can read responses from, or send file contents to, arbitrary internal targets reachable by the Erlang FTP client, including cloud metadata services.
Affected Products
- Erlang/OTP inets versions 5.10.4 through 6.5 (OTP 17.4 through 20.3), source path lib/inets/src/ftp/ftp_internal.erl
- Erlang/OTP ftp versions 1.0 and later (OTP 21.0 and later), source path lib/ftp/src/ftp_internal.erl
- OTP from 17.4 before 29.0.2, 28.5.0.2, and 27.3.4.13 (corresponding to ftp before 1.2.6, 1.2.4.1, and 1.2.3.1)
Discovery Timeline
- 2026-06-10 - CVE CVE-2026-48858 published to NVD
- 2026-06-10 - Last updated in NVD database
Technical Details for CVE-2026-48858
Vulnerability Analysis
The vulnerability resides in the PASV (passive mode) response handler within ftp_internal.erl. When the FTP client issues PASV, the server responds with code 227 containing an IP address and port for the data connection. The Erlang client parses this response and immediately calls gen_tcp:connect/4 using the server-supplied IP without cross-checking it against the control connection peer.
The adjacent EPSV handlers in the same module correctly invoke peername(CSock) to derive the data connection IP from the existing control socket. The default PASV path (mode=passive, ipfamily=inet, ftp_extension=false) omits this check entirely. RFC 2577 section 3 explicitly recommends validating the PASV response IP against the control connection peer to prevent exactly this class of attack.
On read operations such as ftp:ls/1,2, ftp:nlist/1,2, and ftp:recv/2,3, the redirected target's response is returned to the caller. On write operations such as ftp:send/2,3 and ftp:append/2,3, file content is delivered to the redirected target.
Root Cause
The root cause is missing input validation on a network-supplied address. The PASV handler trusts the server-controlled IP literal from the 227 response. This violates the secure design pattern already used in the same file's EPSV handlers, which bind the data connection to the verified control peer.
Attack Vector
An attacker operates or compromises an FTP server that an Erlang/OTP client connects to. The attacker crafts a 227 PASV response advertising an arbitrary IP and port — for example, 169.254.169.254:80 (AWS Instance Metadata Service) or an internal database host. The client opens the data connection to that target and either exfiltrates the target's response back to the attacker-controlled session or writes attacker-supplied content to the internal target.
ipfamily = inet,
client = From,
caller = {setup_data_connection, Caller},
+ csock = CSock,
timeout = Timeout,
sockopts_data_passive = SockOpts,
ftp_extension = false} = State) when is_list(Lines) ->
Source: Erlang/OTP commit 2691a80. The patch threads the control socket CSock into the PASV handler so the IP can be validated against the control connection peer instead of trusted from the server response.
Detection Methods for CVE-2026-48858
Indicators of Compromise
- Outbound TCP connections from Erlang/BEAM processes to RFC1918 ranges, link-local 169.254.0.0/16, or loopback shortly after an outbound FTP control session
- Data connection destination IP that differs from the FTP control connection destination IP for the same client process
- FTP client retrievals containing HTTP response headers or cloud metadata JSON payloads instead of expected file content
Detection Strategies
- Inspect FTP control traffic for 227 responses whose advertised IP does not match the server's control-channel IP
- Correlate FTP control and data flows by source process and 5-tuple; flag mismatches between control peer and data peer
- Inventory Erlang/OTP deployments and identify processes loading the ftp or inets applications with vulnerable version strings
Monitoring Recommendations
- Enable egress logging from application hosts that run Erlang services and alert on connections to instance metadata endpoints such as 169.254.169.254
- Monitor BEAM process network activity for short-lived connections to internal RFC1918 addresses initiated immediately after a remote FTP session
- Ingest host telemetry into a centralized data lake to baseline FTP client behavior and detect deviations across the fleet
How to Mitigate CVE-2026-48858
Immediate Actions Required
- Upgrade Erlang/OTP to a fixed release: 29.0.2, 28.5.0.2, or 27.3.4.13 (corresponding to ftp 1.2.6, 1.2.4.1, or 1.2.3.1)
- Audit application code for use of the ftp module and migrate away from it, since the ftp application is deprecated and scheduled for removal in OTP-30
- Restrict outbound network access from hosts running Erlang FTP clients, blocking connections to internal management ranges and cloud metadata endpoints
Patch Information
The fix passes the control socket into the PASV state record and validates the 227 response IP against the control connection peer. See the GitHub Security Advisory GHSA-24cv-hwgr-37fq, the CNA advisory, and commits 2691a80 and 521bcfa.
Workarounds
- Connect only to trusted FTP servers and avoid using the ftp client against attacker-influenced endpoints
- Replace ftp usage with FTPS, SFTP, or HTTPS clients that perform server identity validation
- Apply network egress filters that prevent the application host from reaching internal services or cloud metadata IPs on arbitrary ports
# Verify installed OTP version contains the fix
erl -eval 'io:format("~s~n",[erlang:system_info(otp_release)]), halt().' -noshell
# Example egress filter blocking cloud metadata from an Erlang host (iptables)
iptables -A OUTPUT -d 169.254.169.254 -j REJECT
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

