Poważna luka w Erlang/OTP SSH

16 kwietnia została zgłoszona podatność (CVE-2025-32433, oceniona jako krytyczna – skala 10/10) w bibliotece stanowiącej implementację serwera SSH dla aplikacji pisanych w języku Erlang. Niepoprawna obsługa protokołu SSH umożliwia atakującemu wykonanie dowolnego polecenia (RCE, Remote Code Execution) bez konieczności wcześniejszej autoryzacji. Na szczęście opisywany problem dotyczy wyłącznie serwerów SSH wystawianych przez aplikacje pisane w Erlang […]

Kwi 29, 2025 - 12:59
 0
Poważna luka w Erlang/OTP SSH

16 kwietnia została zgłoszona podatność (CVE-2025-32433, oceniona jako krytyczna – skala 10/10) w bibliotece stanowiącej implementację serwera SSH dla aplikacji pisanych w języku Erlang. Niepoprawna obsługa protokołu SSH umożliwia atakującemu wykonanie dowolnego polecenia (RCE, Remote Code Execution) bez konieczności wcześniejszej autoryzacji. Na szczęście opisywany problem dotyczy wyłącznie serwerów SSH wystawianych przez aplikacje pisane w Erlang – powszechnie wykorzystywany serwer OpenSSH nie jest podatny. Biorąc jednak pod uwagę, że Erlang znalazł zastosowanie w obszarach telekomunikacji czy ogólnie urządzeń sieciowych, to zdecydowanie należy zweryfikować, czy podatność występuje w zarządzanej przez nas infrastrukturze. Wykrywana jest przykładowo przez skaner bezpieczeństwa Nessus.

W celu usunięcia podatności należy podnieść wersję OTP zgodnie z informacją podaną w opisie luki.

Informacje o podatnych i poprawionych wersjach.
Informacje o podatnych i poprawionych wersjach.

W przypadku podatnych urządzeń (takich jak niektóre starsze modele routerów Cisco) rozwiązaniem powinna być aktualizacja oprogramowania (firmware). Jeśli jednak nie ma takiej możliwości, bo przykładowo dana seria utraciła już wsparcie techniczne, to najlepszym wyjściem będzie ograniczenie dostępu SSH do zaufanej puli adresów. Warto od razu zaznaczyć, że tak wrażliwa usługa jak SSH nie powinna być dostępna publicznie. W zasadzie jedynym wyjątkiem mogą być usługi hostingowe, ale i w tych przypadkach można zastosować przykładowo ograniczenie dostępu do adresów IP wskazujących na konkretne państwo.

Praktyczne użycie podatności jest bardzo łatwe, co potwierdza fakt, że z przygotowaniem działającego „PoC” poradził sobie ChatGPT. Serwer SSH działający w ramach wirtualnej maszyny Erlang (BEAM) możemy uruchomić w kilku krokach. Użytkownik „aplikacyjny” musi mieć uprawnienia sudo, ponieważ serwer będzie nasłuchiwał na porcie 22, domyślnym dla SSH. Uruchomimy najpierw wersję podatną (27.3.2), a po udanym wykonaniu ataku najnowszą obecnie wersję 27.3.3.

Do kompilacji i instalacji różnych wersji Erlang można użyć narzędzia kerl. Binarkę należy zapisać w dowolnym katalogu znajdującym się w zmiennej $PATH i nadać uprawnienie do wykonania.

				
					curl -O https://raw.githubusercontent.com/kerl/kerl/master/kerl
chmod 700 kerl
				
			

Po wykonaniu bez podawania parametrów powinny zostać wypisane dostępne funkcje.

Wylistowanie dostępnych opcji kerl.
Wylistowanie dostępnych opcji kerl.

Proces kompilacji Erlang z użyciem kerl ma pewną „wadę”. Biblioteki OTP – w tym potrzebna nam ssh – są pomijane gdy w systemie brakuje odpowiednich zależności. Kerl w żaden sposób nie pozwala na zatrzymanie kompilacji i doinstalowanie wymaganych pakietów. Z tego powodu przed pierwszym użyciem lepiej jest pobrać archiwum z OTP (dostępne wydania są na stronie https://github.com/erlang/otp/releases) i wykonać polecenie configure, które wyraźnie poinformuje o wykrytych problemach.

Sprawdzenie potrzebnych zależności dla aplikacji OTP.
Sprawdzenie potrzebnych zależności dla aplikacji OTP.

Interesuje nas sekcja APPLICATIONS DISABLED. Jak widać, aplikacja ssh wymaga biblioteki OpenSSL, czyli pakietu libssl-dev. Można też rozwiązać braki zależności dla pozostałych aplikacji poprzez pobranie JDK (np. z użyciem SDKMAN) i instalację pakietu unixodbc-dev. Wspomniane w drugiej sekcji wxWidgets możemy pominąć – dotyczy wyłącznie aplikacji ze środowiskiem graficznym. Kolejne sprawdzenie środowiska skryptem configure nie powinno wyświetlić pżadnych informacji o wyłączonych aplikacjach.

Można więc przejść do kompilacji Erlang. Wykonanie kerl list releases zwróci wersje „znane” dla tego narzędzia. Jak widać, 27.3.2 akurat nie znajduje się na liście, ale wystarczy wskazać tę konkretną wersję wraz z adresem repozytorium i kompilacja powinna zadziałać całkowicie poprawnie.

Wersje Erlang dostępne ad hoc w kerl.
Wersje Erlang dostępne ad hoc w kerl.
				
					kerl build git https://github.com/erlang/otp.git OTP-27.3.2 27.3.2
				
			

Można następnie wylistować dostępne „buildy” (zbudowane wersje Erlang) i zainicjować odpowiednie katalogi (struktura jest tworzona automatycznie), z których poziomu będzie możliwe korzystanie ze środowiska. W naszym przypadku istotny jest przede wszystkim katalog bin, gdzie znajdziemy konsolę erl.

Inicjalizacja środowiska Erlang.
Inicjalizacja środowiska Erlang.

Ostatni i najważniejszy etap to uruchomienie aplikacji ssh. Wskazany zostaje port 22 oraz podane hasło do wszystkich kont – aplikacja ssh z Erlang nie korzysta z systemowej bazy haszy zawartej w specjalnym pliku /etc/shadow. Dlatego też trzeba zaznaczyć, że opisywana podatność nie pozwala bezpośrednio na użycie konta systemowego, a jedynie tego, na którego prawach została uruchomiona aplikacja (oczywiście dla podanej konfiguracji jest to konto root). Niemniej w żaden sposób nie zmniejsza to wagi podatności, a wszelkie luki typu RCE należy w każdym wypadku traktować poważnie.

Konsolę Erlang można uruchomić poprzez wykonanie wspomnianego pliku erl z uprawnieniami root. Poniższy kod umożliwi uruchomienie prostego, ale stanowiącego dobry przykład, serwera SSH. Wcześniej zatrzymałem usługę OpenSSH, aby nie wystąpił konflikt portów uniemożliwiający poprawny start aplikacji.

				
					{ok, _} = application:ensure_all_started(ssh).
Port = 22.
ssh:daemon(Port, [{password, "admin"}]).
				
			
Uruchomienie aplikacji ssh.
Uruchomienie aplikacji ssh.

Wykonanie tych trzech linii pozwoliło na uruchomienie pełnego serwera SSH obsługującego także standardowy upload plików. Zgodnie z podanym opisem użytkownik posiada uprawnienia root.

Wykonywanie poleceń poprzez SSH dostarczane przez Erlang/OTP.
Wykonywanie poleceń poprzez SSH dostarczane przez Erlang/OTP.

Uruchomiłem podstawowy skan bezpieczeństwa w narzędziu Nessus i podatność została znaleziona.

Wykrycie podatności przez Nessus.
Wykrycie podatności przez Nessus.

Przygotowany exploit w postaci skryptu Python działa skutecznie. Jego wykonanie przeciwko podatnej usłudze skutkuje utworzeniem pliku /lab.txt o zawartości „pwned”. Nie jest to szczególnie ciekawe z perspektywy omawiania ataku, natomiast wciąż pozwala zrozumieć możliwości wykorzystania podatności RCE. Wspomnę też, że nie znalazłem sposobu uruchomienia innej powłoki (takiej jak Bash) z poziomu Eshell, co pozwoliłoby na bezpośrednie operowanie dobrze znanymi poleceniami Linuxa.

Zmodyfikowałem skrypt, aby zamiast zwykłego pliku tekstowego tworzony był skrypt PHP z funkcją phpinfo(). Jak można sprawdzić, próba ataku przebiegła zgodnie z założeniami.

Wykonanie exploita przeciwko aplikacji ssh Erlang/OTP.
Wykonanie exploita przeciwko aplikacji ssh Erlang/OTP.

Plik rzeczywiście został utworzony w podanej lokalizacji.

Potwierdzenie utworzenia pliku w wyniku działania exploita.
Potwierdzenie utworzenia pliku w wyniku działania exploita.

Tutaj akurat jest to nieszkodliwa funkcja, ale bez większych trudności i zaawansowanej znajomości języka PHP można przygotować skrypt wykonujący polecenia w systemie podane przykładowo jako parametr GET. Dostępne są zresztą gotowe przykłady webshell napisanych w PHP. Dodanie takiego skryptu przez atakującego można traktować jako backdoor, który pozostanie dostępny nawet jeśli podatność RCE uda się załatać.

Wynik funkcji phpinfo().
Wynik funkcji phpinfo().

Jeszcze jeden przykład to skrypt wyświetlający zawartość pliku /etc/passwd. W systemach Linux usługi z reguły działają w ramach dedykowanych kont, co jest w pełni zasadną praktyką, ale ich lista niekoniecznie powinna być dostępna dla wszystkich. Wartość zmiennej command w skrypcie ustawiłem w ten sposób:

				
					command = """file:write_file("/home/www/app/src/net/passwd.php", <<"">>)."""
				
			

Efektu można się było spodziewać.

Wyświetlenie zawartości pliku /etc/passwd przez skrypt PHP.
Wyświetlenie zawartości pliku /etc/passwd przez skrypt PHP.

W celu weryfikacji czy błąd został poprawiony w najnowszej wersji Erlang/OTP wykonałem analogiczną kompilację i instalację z użyciem kerl. Sam skrypt nie kończy się błędem, ale odpowiedni komunikat pojawia się jako „log” aplikacji i polecenie nie jest wykonywane.

Niepoprawne działanie exploita w najnowszej wersji Erlang/OTP.
Niepoprawne działanie exploita w najnowszej wersji Erlang/OTP.

Podatność w aplikacji ssh Erlang/OTP pokazuje również, że warto korzystać ze znanych i sprawdzonych rozwiązań – tutaj jest to serwer OpenSSH – niż konfigurować własne. Wiadomo, że luki bezpieczeństwa mogą znajdować się w każdym oprogramowania, natomiast szanse na ich odkrycie i sprawne przygotowanie poprawki w powszechnie używanych aplikacjach są zdecydowanie większe.