This is a write-up on how i solved Luke from HacktheBox.

Hack the Box is an online platform where you practice your penetration testing skills.

As always, I try to explain how I understood the concepts here from the machine because I want to really understand how things work. So please, if I misunderstood a concept, please let me know.

About the box

Luke is rated medium difficulty, and it’s OS is FreeBSD. I think it’s passable as an easy box as it’s fairly short and simple. When I solved this, I forgot that this was a medium difficulty box, but realized that it will be quite difficult if you don’t enumerate properly. Also, for me, the box’s required approach is new compared to the active machines at the time this was released but as all HTB machines, there is something to learn from here.

#TLDR

Initial foothold: Through enumeration, found details for credentials to authenticate to get a JWT.

Root: Using the generated JWT to list more credentials from port 3000 and accessing Ajenti Server Admin Panel, reading root.txt and user.txt

#Initial Foothold

As usual, I run an nmap scan, saving it to my Nnmap directory:

nmap -sV -sC -oA nmap/initial 10.10.10.137

The output is:

Nmap scan report for 10.10.10.137

Host is up (0.27s latency).

Not shown: 991 closed ports

PORT STATE SERVICE VERSION

21/tcp open ftp vsftpd 3.0.3+ (ext.1)

| ftp-anon: Anonymous FTP login allowed (FTP code 230)

|_drwxr-xr-x 2 0 0 512 Apr 14 12:35 webapp

| ftp-syst:

| STAT:

| FTP server status:

| Connected to 10.10.13.22

| Logged in as ftp

| TYPE: ASCII

| No session upload bandwidth limit

| No session download bandwidth limit

| Session timeout in seconds is 300

| Control connection is plain text

| Data connections will be plain text

| At session startup, client count was 3

| vsFTPd 3.0.3+ (ext.1) - secure, fast, stable

|_End of status

22/tcp open ssh?

80/tcp open http Apache httpd 2.4.38 ((FreeBSD) PHP/7.3.3)

| http-methods:

|_ Potentially risky methods: TRACE

|_http-server-header: Apache/2.4.38 (FreeBSD) PHP/7.3.3

|_http-title: Luke

1310/tcp filtered husky

3000/tcp open http Node.js Express framework

|_http-title: Site doesn't have a title (application/json; charset=utf-8).

3003/tcp filtered cgms

8000/tcp open http Ajenti http control panel

|_http-title: Ajenti

19315/tcp filtered keyshadow

45100/tcp filtered unknown

Open services are FTP, SSH(?), http, Node.js and Ajenti. I first check FTP.

FTP

I found a file called for_Chihiro.txt. I download it and read its contents:

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

It seems it’s a note from Derry to Chihiro, implying that he/she can access the sources of the website, but the access will be deleted soon. I check HTTP next.

HTTP

The landing page gives us a website template. After playing with it, I found nothing interesting in the website. Since there is no sign or warning not to brute force, I ran dirsearch.

Dirsearch is a python tool that brute forces directories and files in a website. You can find it here, since if I remember correctly, it is not preinstalled in Kali.

https://github.com/maurosoria/dirsearch

Checking the paths found:

config.php

$dbHost = 'localhost'; $dbUsername = 'root'; $dbPassword = 'Zk6heYCyv6ZE9Xcg'; $db = "login"; $conn = new mysqli($dbHost, $dbUsername, $dbPassword,$db) or die("Connect failed: %s

". $conn -> error);

It seems it’s made to look like an error which was probably intended. It gives us info of possible db credentials.

/login.php

This gives us a login page.

Trying the credentials from config.php:

/management

There seems to be a management page. The credentials from the config.php doesn’t work on this also.

Port 8000(Ajenti)

The last open service is Ajenti. The credentials doesn’t work on this also.

Port 3000(Node.js)

Visiting port 3000, I find this:

It seems that it requires of us an Auth token. I remember that when I first solved this box, it took me time to understand how to interact with Node.js. I run dirsearch.py on port 3000 to enumerate further.

I find /users/admin path. It is important to note that the credentials from config.php has a username of root, and not admin. Since it requires us JWT to authenticate, I then searched on how to do it.

Authenticating

For us to access the json data, we first need to authenticate by getting a valid JWT(json web tokens). You can learn more about how it works here: https://jwt.io/. We can use cURL to get a valid JWT from our data using the syntax below(many ways to do this afaik). I use -s to not show the progress bar, -X request method, and -H for headers, and — data for the payload:

I get a “Forbidden” as the response. It seems that the credentials are not valid or we don’t have privilege to access this. After thinking some time and looking at what I have, I remember that there is a /users/admin path. Deducting that an admin user is valid, I tried changing root to admin:

I then get a “success:true”. So the valid credentials are admin:Zk6heYCyv6ZE9Xcg.

Using jq

I then used a tool called jq. It is a tool to make json outputs much more “viewable” and “manageable”. Read more about how you can use it here:

Using jq, the output will look like this:

curl -s -X POST -H 'Accept: application/json' -H 'Content-Type: application/json' --data '{"username":"admin","password":"Zk6heYCyv6ZE9Xcg"}' http://10.10.10.137:3000/login | jq

{

"success": true,

"message": "Authentication successful!",

"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiaWF0IjoxNTY4NDUxNDkwLCJleHAiOjE1Njg1Mzc4OTB9.pTGePScCFa4gFXNj9Gjfc9RqZfLwMBKtGY_HyfuULa0"

}

Filtering the value “token”:

curl -s -X POST -H 'Accept: application/json' -H 'Content-Type: application/json' --data '{"username":"admin","password":"Zk6heYCyv6ZE9Xcg"}' http://10.10.10.137:3000/login | jq '.token'

"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiaWF0IjoxNTY4NDUxNTQzLCJleHAiOjE1Njg1Mzc5NDN9.PQlVYZfOkIRGnBMnpb6jDeV5F9g86ttccgrApZoC_xQ"

You can then create a bash script to simplify and store it in a variable, but it’s not that important in this case.

Here’s how it looks from the terminal: