Skip to main content
Background Image

Mr Robot CTF - TryHackMe

·1356 words·7 mins
Brady McLaughlin
Author
Brady McLaughlin

This is a write-up for the “Mr Robot CTF” lab, a “medium” room on TryHackMe (I think this is of similar difficulty to some of the “easy” rooms, at least today). This is a free room located at https://tryhackme.com/room/mrrobot. I am documenting the process I used to find all information in this writeup WITHOUT including any flags, in the spirit of the game. However, following this process exactly should result in a full compromise of the target system.


Recon, Scanning, and Enumeration
#

My first step was to ping the box to ensure that it was alive and ready for enumeration:

Next, I ran a quick nmap scan to enumerate the service versions and run some basic nmap scripts against found ports:

We see that we have SSH, which will help us if we find some credentials to use, and an Apache web server running on ports 80 and 443.

First, let’s check out the website:

The webpage is very dynamic, and even hosts a pseudo-shell in which we can run the displayed “commands.” These pages aren’t very useful for us, though, so after looking around to enjoy the atmosphere, we can move on.

Since all we have to work with is the web service, the most obvious next step is to scan for directories with a tool like gobuster:

Here, there is a bit of a misdirect - the most interesting looking endpoint is the “/wp-login” page, which indicates a WordPress installation, but we actually need to look at “/robots” first:

This is a robots.txt file, commonly used to tell web crawlers which pages to avoid. There are two of interest here, the first of which is the first key:

The second file is a dictionary file. We can download it to our machine with our preferred tool (I’m using wget here) and examine the file. It looks to be a list of potential usernames or passwords (or both), just as we’d expect:

If we inspect this file, there are a ton of lines. Most of these are duplicates, so let’s filter this out with sort -u to make sure we’re only getting unique entries:

Initial Access
#

Our file is now considerably smaller (but still over 9,000) and will thus be easier to pass through tools. Next, let’s turn our attention to the “/wp-login” endpoint:

If we try something basic (I used admin:admin here), we can see that the login page unnecessarily discloses whether or not the username we have entered is valid:

We can use this to enumerate valid users. I am going to use hydra, but there are various tools that could be used for this purpose. For the http-post-form module, we’ll want to capture a web request with a proxy like Burp Suite:

We can use the POST data at the bottom to craft our hydra syntax. For the http-post-form module, we’ll use the format endpoint:POST data:failed response. We’ll substitute the username and password with variables as shown, and tell hydra to run through our dictionary file in place of the username, since the password doesn’t matter just yet:

Now, if we enter this username with an arbitrary test password, we can see that we get a different message:

We can use this for stage two of our brute-force attack. Now that our username is known, we can switch from -L to -l, and our -p will become -P since we’re going to put the dictionary file here now. The only other thing we’ll have to adjust is our error message:

Now that we have a valid password, we can log into the WordPress login endpoint:

Since we have access to the “/wp-admin” endpoint now, we can make some modifications to the site in order to take over the web service. There are a variety of methods, one of which is to upload a reverse shell plugin like this one that we can use to run code on the underlying operating system:

Don’t forget to activate!

Now, when we navigate to the plugin, we have a spot to put our tun0 IP address (which you can find by running ip a) and a port of our choosing. It doesn’t really matter in this case, so we’ll just use the default, 4444. We’ll need to set up a listener as well:

Now, when we hit the “Connect” button, we’ll catch a shell in our listener:

Upgrading our shell
#

Since the shell we get from this plugin is very unstable, I’m going to open a second listener and send myself a second reverse shell from within, giving me more control over the interface we’re running in:

Now that we are no longer dependent upon the WordPress plugin, we can upgrade our python3 reverse shell to a full PTY with the following commands:

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

Ctrl + Z (to background the shell, taking us back to our host machine's shell)

stty raw -echo;fg (on the host shell - gives us a raw terminal with no input or output processing, then foregrounds the reverse shell from the victim machine)

export TERM=xterm (sets the TERM variable so that we can use commands like clear in the reverse shell)

Lateral Movement
#

Now that we have a stable shell, let’s look around the file system for more information. A good place to start is the “/home” directory:

Looking in the “/home/robot” directory, we can see that key 2 is located here, but only the “robot” user has the necessary permissions to read the file:

We can, however, read the “password.raw-md5” file with our current permission set:

We could crack this hash with a tool like Crackstation, but we wouldn’t want to put a client’s hashes online. Let’s instead crack this offline with hashcat. Since MD5 doesn’t have any headers, we’ll have to find the hash mode:

This hash will easily crack, almost immediately, as we can see here:

Assuming this is a valid system credential, we may be able to log in with SSH:

Now that we are operating as the user of the second key file, we can read it:

Privilege Escalation
#

Now that we are the “robot” user, we can see if there are any privilege escalation opportunities available to us. After striking out on sudo -l, we can upload LinPEAS for some “fire-and-forget” privilege escalation checks.

One way to transfer files to the victim machine is to host the file via HTTP (we could use SCP in this case, but since we won’t always have SSH access, this way is more common):

Now we just need to download the file to a writable directory. “/tmp” almost always works for this:

Note that we don’t have the permissions necessary to execute this script by default, so we’ll need to add them ourselves, after which we can run the file:

For me, LinPEAS is crashing before running the entire script, so I pivoted to running each check individually with ./linpeas.sh -o <check>. The one that returned the most interesting information was ./linpeas.sh -o interesting_perms_files:

Checking GTFOBins, there does seem to be a known privilege escalation vector for nmap with SUID permissions:

However, this appears to be a file write path:

Since a SUID binary will run as the owner of the file, let’s see if we can use this shell method to run nmap in interactive mode, which we should be able to break out of while maintaining our permissions:

Now that we’ve escalated to the “root” user, we should be able to grab the final key!

Bonus path
#

There was another path to gaining the credentials to log into the WordPress page, but testing for valid usernames and passwords will be more viable for developing a methodology for other machines.

On the “/license” endpoint, the page just looks to have text:

If we scroll down, there is more to the message:

The string at the bottom is encoded with base64, meaning that we can decode it:

Again, this is more of a “CTF-y” way to find the credentials, but I thought I’d show it here for the sake of completeness.

Thanks to Leon Johnson for the creation of this challenge!