every *2shell, one place
← all bugs

Erlang/OTP SSH Pre-Auth RCE

Product
Erlang/OTP OTP ssh application
CVE
CVE-2025-32433
Disclosed
2025-04-16
CVSS
10
Auth
pre-auth
CISA KEV
yes
Affected
Erlang/OTP before OTP-27.3.3, OTP-26.2.5.11 and OTP-25.3.2.20 when the built-in SSH server is used (many Cisco, Ericsson and IoT products embed it).
Fixed
OTP-27.3.3, OTP-26.2.5.11, OTP-25.3.2.20.

Summary

The Erlang/OTP SSH server would process SSH connection-protocol messages, including 'exec' channel requests, before the client had authenticated. Anyone who can reach the port can run arbitrary commands, and a PoC was public within a day.

Root cause

The ssh_connection state machine did not reject connection-protocol messages (message numbers >= 80, e.g. SSH_MSG_CHANNEL_OPEN and SSH_MSG_CHANNEL_REQUEST 'exec') received before user authentication had completed. The exec request is evaluated as Erlang code by the daemon, typically running as root.

Impact

Unauthenticated remote code execution with the privileges of the SSH daemon (often root) on any system embedding the Erlang SSH server.

Public PoC

# No key exchange is completed and no userauth is sent:
# banner -> KEXINIT -> CHANNEL_OPEN -> CHANNEL_REQUEST 'exec'
s.sendall(b"SSH-2.0-OpenSSH_8.9\r\n")
banner = s.recv(1024)
s.sendall(pad_packet(build_kexinit()))

s.sendall(pad_packet(build_channel_open()))   # SSH_MSG_CHANNEL_OPEN 'session'

# 'exec' payload is Erlang code, evaluated pre-auth
chan_req = build_channel_request(
    command='file:write_file("/lab.txt", <<"pwned">>).'
)
s.sendall(pad_packet(chan_req))

Source: ProDefense (Platform Security) - CVE-2025-32433 PoC

Detection

Banner check for 'SSH-2.0-Erlang/x.y' on exposed ports; nuclei template CVE-2025-32433. IDS rules flag SSH_MSG_CHANNEL_OPEN/REQUEST before USERAUTH_SUCCESS.

Mitigation

Upgrade OTP; otherwise disable the SSH server or restrict access via firewall.

References