Overview
Welcome to the second HackTheBox walkthrough on this blog! "Luke" has been recently retired, so I'll go ahead and share how I went about in owning the machine. This machine had somewhat of a CTF feeling and was a fun learning experience nevertheless.
Enumeration & Recon
First things first, let's do some basic scanning with Nmap to see what ports are open.
![]() |
| nmap -Pn -A -oN luke.nmap.out 10.10.10.137 |
![]() |
| nmap output, continued |
We have many open ports to explore - 21 (FTP), 22 (possibly SSH), 80 (HTTP), 3000 (Node.js), and 8000 (Ajenti). Since we have some HTTP ports open, let's go ahead and fire up nikto on port 80.
![]() |
| nikto -h 10.10.10.137 | tee luke.nikto.out |
We see that the config.php and login.php files are available - we'll get to that shortly. Let's run dirb first on port 80.
![]() |
| dirb http://10.10.10.137 |
![]() |
| dirb results for port 80 |
Keep a note of /management - we'll definitely check that out later. Now we can start poking around with what we have so far - let's check out those config.php and login.php files, as well as the main page on port 80.
![]() |
| Main webpage |
![]() |
| /config.php file |
Looks like the config.php file gave us some credentials to try out later:
$dbHost = 'localhost'; $dbUsername = 'root'; $dbPassword = 'Zk6heYCyv6ZE9Xcg'; $db = "login"; $conn = new mysqli($dbHost, $dbUsername, $dbPassword,$db) or die("Connect failed: %s\n". $conn -> error);What about login.php?
We found a login portal! Unfortunately, the root credentials we just found on config.php don't work on this login page. We'll table this for now, and maybe we'll end up coming back to it later.
dirb also showed us that /management is open on port 80, so let's head over there:
Too bad the credentials we found don't work here either. But, we have two login portals to try out later on!
Let's keep enumerating and check out port 21, which allows anonymous FTP login.
![]() |
| Finding available files from FTP |
We found a file called for_Chihiro.txt. Let's download it and see what it says!
![]() |
| for_Chihiro.txt content |
Well that's not too helpful. But at least it tells us that we might find things by poking around on the web ports. We already found some interesting php files on port 80, so let's see what port 8000 has to offer.
![]() |
| Ajenti login portal |
Our root credentials don't work on this Ajenti login portal, unfortunately. But, we now have a third login portal to try with any new credentials we might find. This Ajenti portal is especially interesting, since Ajenti provides remote server management - getting access to this would certainly be useful.
Next, let's look at port 3000, which appears to run a web application through Node.js.
![]() |
| visiting port 3000 |
It looks like Luke is using JSON Web Token (JWT) authentication for Node.js. Maybe we can still find some information by bruteforcing port 3000. I went ahead and ran gobuster and dirbuster:
![]() |
| gobuster dir -e -u http://10.10.10.137:3000 -w /usr/share/wordlists/dirb/common.txt |
![]() |
| Setting up dirbuster |
![]() |
| dirbuster results |
We have /login and /users/ available - let's see what these endpoints look like.
Looks like we need to authenticate before we can check out /users, and /login looks like the authentication point where we can obtain our auth token. We can do this by sending a curl request to the /login API - our request should have the username and password for the account whose authentication token we want to use.
Obtaining Authentication Token and Credentials
Let's try using our root credentials from config.php to get an authentication token:
curl -s -X POST -H 'Accept: application/json' -H 'Content-Type: application/json' --data '{"password":"Zk6heYCyv6ZE9Xcg", "username":"root", "rememberMe":false}' http://10.10.10.137:3000/login
| Failure |
Well, that didn't work. But what if we try the same password with a different well-known username - maybe "admin"?
curl -s -X POST -H 'Accept: application/json' -H 'Content-Type: application/json' --data '{"password":"Zk6heYCyv6ZE9Xcg", "username":"admin", "rememberMe":false}' http://10.10.10.137:3000/login
![]() |
| Success! |
Great, we got the authentication token for the admin account:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiaWF0IjoxNTY4NDY0NDU1LCJleHAiOjE1Njg1NTA4NTV9.nY-552O77MKj-xmdtY69IHz2fyczqbT40o1U5-px4jI
By sending this authentication token in subsequent curl requests to the Node.js framework on 10.10.10.137:3000, we can make authenticated requests that we couldn't make before. For starters, we can start enumerating 10.10.10.137:3000/users as the authenticated admin account.
curl -s -H 'Accept: application/json' -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiaWF0IjoxNTY4NDY0NDU1LCJleHAiOjE1Njg1NTA4NTV9.nY-552O77MKj-xmdtY69IHz2fyczqbT40o1U5-px4jI' http://10.10.10.137:3000/users | jq
![]() |
| User accounts found |
The response gave us user account names, IDs, and roles. "Admin" is there, as expected (that's our account), along with Derry (the web admin), Yuri (Beta Tester), and Dory (Supporter).
Remember how dirbuster found /users/admin ? It seems we can request more information on individual user accounts via the API by requesting /users/<username>. Let's do that for /users/admin:
curl -s -H 'Accept: application/json' -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiaWF0IjoxNTY4NDY0NDU1LCJleHAiOjE1Njg1NTA4NTV9.nY-552O77MKj-xmdtY69IHz2fyczqbT40o1U5-px4jI" http://10.10.10.137:3000/users/admin | jq
That's the admin password we just used, so it seems that the /users API provides user account passwords! Let's get passwords on all the accounts we just found:
curl -s -H 'Accept: application/json' -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiaWF0IjoxNTY4NDY0NDU1LCJleHAiOjE1Njg1NTA4NTV9.nY-552O77MKj-xmdtY69IHz2fyczqbT40o1U5-px4jI" http://10.10.10.137:3000/users | jq -r '.[] | .name' | while read line; do curl -s -H 'Accept: application/json' -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiaWF0IjoxNTY4NDY0NDU1LCJleHAiOjE1Njg1NTA4NTV9.nY-552O77MKj-xmdtY69IHz2fyczqbT40o1U5-px4jI" http://10.10.10.137:3000/users/$line | jq ; done
![]() |
| Enumerating user account passwords |
Success! We now have passwords for the user accounts we found. Let's try testing these credentials in the login portals we found previously on 10.10.10.137:80/login.php, 10.10.10.137:80/management, and 10.10.10.137:8000. Derry was labeled as a web admin, so let's start with their account (password is "rZ86wwLvx7jUxtch").
We can't log in on port 8000 or on /login.php, but we successfully log in as Derry through /management!
![]() |
| Logging in as Derry |
Let's take a look at the config.json file, which appears to contain configuration information for Ajenti:
It looks like we found configuration information for the "root" Ajenti user. Let's see if there's a password in there.
![]() |
| More credentials found |
We found a password ("KpMasng6S5EtTy9Z") for the "root" Ajenti account, which means we can now try logging in on port 8000, which holds the Ajenti login portal.
![]() |
| Successful Ajenti login as root |
Success! With this remote administration tool, we can easily read the user.txt and root.txt flags.
Owning User and Root
On the left, we see the "File Manager' option. Select that, and browse to the /root directory.
Owning User and Root
On the left, we see the "File Manager' option. Select that, and browse to the /root directory.
Let's open root.txt!
![]() |
| root.txt found |
The "Edit" button will open root.txt in Notepad, and we get the flag!
![]() |
| Getting the root flag. |
This was certainly interesting, since we were able to get the root flag before the user flag. Getting the user flag is equally as trivial - we just go back to the File Manager, browse to /home/derry, and open user.txt just like we did with root.txt.
![]() |
| Finding /home/derry/user.txt |
![]() |
| Grabbing the user flag |
Challenge complete!


































No comments:
Post a Comment