evo-erlang-ssh-cve-patch
Overview
CVE-2025-32433 is a critical (CVSS 10.0) pre-authentication Remote Code Execution vulnerability in Erlang/OTP SSH server. The server fails to validate authentication state before processing SSH connection protocol messages (message IDs >= 80), allowing attackers to execute arbitrary commands without credentials.
Vulnerability Details
- Root Cause:
ssh_connection.erl'shandle_msg/4function processes channel messages (SSH_MSG_CHANNEL_OPEN, SSH_MSG_CHANNEL_REQUEST) without checking if#ssh{authenticated}is true - Attack Vector: After completing key exchange, attacker sends SSH_MSG_CHANNEL_OPEN (ID 90) + SSH_MSG_CHANNEL_REQUEST (ID 98) with exec type, bypassing authentication entirely
- Affected Versions: All Erlang/OTP versions before 27.3.3, 26.2.5.11, 25.3.2.20
The Fix
Add a pattern-matching clause in ssh_connection.erl that catches ANY message when authenticated = false on the server side, returning a disconnect error. This clause must be placed BEFORE any channel message handlers but is naturally placed after any disconnect handler.
Exact Erlang Code to Insert
erlanghandle_msg(_Msg, Connection, server, #ssh{authenticated = false}) -> %% RFC 4252 Section 6: %% Message numbers of 80 and higher are reserved for protocols running %% after this authentication protocol, so receiving one of them before %% authentication is complete is an error, to which the server MUST %% respond by disconnecting. {disconnect, {?SSH_DISCONNECT_PROTOCOL_ERROR, "Unauthenticated connection protocol message"}, Connection};
Insertion Point
Insert this clause right before the first handle_msg(#ssh_msg_channel_open_confirmation{... clause, which is the first channel message handler in the function.
Usage
pythonimport sys sys.path.insert(0, '/app/environment/skills/evo-erlang-ssh-cve-patch/scripts') from utils import find_ssh_connection_erl, check_if_already_patched, apply_cve_2025_32433_patch, validate_patch_applied # Find the file file_path = find_ssh_connection_erl('/app/workspace/otp_src_27.3.2') # Check if already patched if not check_if_already_patched(file_path): apply_cve_2025_32433_patch(file_path) # Validate assert validate_patch_applied(file_path), "Patch validation failed!"
Key Technical Details
- The macro
?SSH_DISCONNECT_PROTOCOL_ERRORis defined inssh_transport.hrl(value 2) - The
#ssh{}record withauthenticated = falsefield is inssh.hrl - Both headers are already included by
ssh_connection.erl - Erlang evaluates function clauses top-to-bottom, so this catch-all must come before specific channel handlers
- The
serveratom in the 3rd argument ensures this only affects server-side connections (clients are unaffected)


