Waldo is one of the easier machines on HackTheBox, and the vulnerabilities that we need to exploit are not necessarily representative of the real world. The way to "user" has an easier form of a common vulnerability, though, and the privilege escalation taught be about a tool I never used before, so I decided to make a Write-Up for this box.
As always, we will start with reconnaissance, a port scan being the most basic of reconnaissance techniques:
Starting Nmap 7.70 ( https://nmap.org ) at 2018-12-19 13:37 CET Nmap scan report for [IP] Host is up (0.11s latency). Not shown: 997 closed ports PORT STATE SERVICE VERSION 22/tcp open ssh OpenSSH 7.5 (protocol 2.0) | ssh-hostkey: | 2048 c4:ff:81:aa:ac:df:66:9e:da:e1:c8:78:00:ab:32:9e (RSA) | 256 b3:e7:54:6a:16:bd:c9:29:1f:4a:8c:cd:4c:01:24:27 (ECDSA) |_ 256 38:64:ac:57:56:44:d5:69:de:74:a8:88:dc:a0:b4:fd (ED25519) 80/tcp open http nginx 1.12.2 |_http-server-header: nginx/1.12.2 | http-title: List Manager |_Requested resource was /list.html |_http-trane-info: Problem with XML parsing of /evox/about
This shows us that both an SSH server and an HTTP server are running on this target. SSH is a pretty secure application, and without any vulnerability in the server itself we will only get further with valid credentials - which we don't have. Let's then progress to check out the HTTP server. Even though I don't recommend it for private use (data collection and all that), I like to use Chromium for hacking activities. Opening the site we are greeted with a very unwelcoming flood of colors (a large image of "Where is Waldo?" and a cheap HTML UI). Pressing F12 opens the developer console, which is great for analyzing HTTP traffic. The "Network" tab has an overview of all HTTP calls that are made. Checking "Preserve log" will keep the information after a refresh a made or a new site is opened - which can be useful to get a hold of the bigger picture.
Refreshing the site, the developer view shows a call to an interesting file - "dirRead.php". Clicking on one of the items makes a call to another file "fileRead.php". Looking at their functionality, we can see that they do what we would expect - read a file (or a directory, respectively). With these, we may be able to crawl through the file system of the target and find delicate information (like a flag - for example). Taking a closer look at the calls in the developer console, we see that there are POST requests made to these files, with the parameter "path" for "dirRead.php" and "file" for "fileRead.php".
To forge HTTP requests, I use curl (To get a feeling for curl, you can right click on an item in the developer console, select "copy as curl" and paste it into your terminal).
Let's start simple:
Let's analyze this: We can look at the current directory, we can look at subdirectories and we can look at the parent directory. But if we try to get even further up in the directory tree, we are stuck. A lot of Web Applications that allow such file and directory accesses work with permissions - but we don't get a 403 or similar response that tells us that what we try is forbidden. Another method is to sanitize the input, e.g. removing ".." from strings to avoid going up a level - that specific example does not apply here, as passing ".." as input delivers the desired output. But developers are human (for now), and humans make mistakes. What we want to achieve here is called "Path Traversal".
After playing around with different inputs a bit, we can make the following observation: What the PHP scripts to is replace "../" with "". It only does one round of replacing, though. Some examples:
A smarter (but still pretty dumb) algorithm would do the replacement over and over again, until no replacement is possible anymore. But this one doesn't. With the last example we can now form paths suitable for our aims:
We can crawl through the directories as we please, and we already found the user flag! Let's grab it, shall we? "fileRead.php" will allow us to do just that:
Hmmm... Turns out, it doesn't work as expected. Some trial & error will help us realize that this is a permission problem. We simply don't have the rights to read that file. Looking at the above output, though, we see that there are some non-standard files within "/home/nobody/.ssh". Let's take a look:
We got ourselves an RSA private key! The nmap scan earlier showed us that an SSH server is running on port 22 of the target, so if we put that key into a file on our machine (let's call it "private.key"), and use the username we just found through the dirRead ("nobody"), maybe...
We have the user flag! And we have SSH access to the target. Let us now continue to make our way to root.
No SUID file to exploit. Further search will also reveal that there is nothing, no cronjob, no misconfiguration, etc. that could be used for privilege escalation. One thing, though, remains: Why is the key called monitor? Maybe a look into the known_hosts file will give us a hint:
It seems that the user "nobody" has opened an SSH connection to the same machine it is on (localhost/127.0.0.1) in the past. With the information we have, we can make a guess:
Our assumptions were correct. The key filename actually revealed the username to us, we now have a user with slightly more privileges than "nobody". Let's take a look around:
"cd" doesn't work. We can not even change directory - we're stuck. But one line of the output might help here: "-rbash: cd: restricted". These restrictions seem to restricted only in the "rbash" shell, the default shell configured for the user "monitor". What if we use a different shell?
And it works! One problem remains, though:
The shell doesn't recognize most of our commands? This is because of how the PATH variable is configure here:
It does not contain any of the common paths for commands. Let's fix that:
We can now use some more commands.
******************************************* * *This is an application to print out common log files * ******************************************** #include "logMonitor.h" void printUsage() { printf("Usage: %s [-aAbdDfhklmsw] [--help]\n", PROGRAMNAME); } int main(int argc, char** argv){ int opt = 0; char filename[26]; { //temporary variables for parsing static struct option long_options[] ={ /* These options don’t set a flag. We distinguish them by their indices. */ {"auth", no_argument, 0, 'a'}, {"alternatives", no_argument, 0, 'A'}, {"btmp", no_argument, 0, 'b'}, {"dpkg", no_argument, 0, 'd'}, {"daemon", no_argument, 0, 'D'}, {"faillog", no_argument, 0, 'f'}, {"help", no_argument, 0, 'h'}, {"kern", no_argument, 0, 'k'}, {"lastlog", no_argument, 0, 'l'}, {"messages", no_argument, 0, 'm'}, {"syslog", no_argument, 0, 's'}, {"wtmp", no_argument, 0, 'w'}, {0,0,0,0} }; //parse the command line arguments int option_index = 0; while((opt = getopt_long (argc, argv, "aAbdDfhklmsw", long_options, &option_index)) != -1 ){ switch (opt) { case 'a' : strncpy(filename, "/var/log/auth.log", sizeof(filename)); printFile(filename); break; case 'A' : strncpy(filename, "/var/log/alternatives.log", sizeof(filename)); printFile(filename); break; case 'b' : strncpy(filename, "/var/log/btmp",sizeof(filename)); printFile(filename); break; case 'd' : strncpy(filename, "/var/log/daemon.log",sizeof(filename)); printFile(filename); break; case 'D' : strncpy(filename, "/var/log/dpkg.log",sizeof(filename)); printFile(filename); break; case 'f' : strncpy(filename, "/var/log/faillog",sizeof(filename)); printFile(filename); break; case 'h' : printUsage(); exit(1); case 'k' : strncpy(filename, "/var/log/kern.log",sizeof(filename)); printFile(filename); break; case 'l' : strncpy(filename, "/var/log/lastlog",sizeof(filename)); printFile(filename); break; case 'm' : strncpy(filename, "/var/log/messages",sizeof(filename)); printFile(filename); break; case 's' : strncpy(filename, "/var/log/syslog",sizeof(filename)); printFile(filename); break; case 'w' : strncpy(filename, "/var/log/wtmp",sizeof(filename)); printFile(filename); break; default: printUsage(); exit(EXIT_FAILURE); } } } return 1; }
The program "logMonitor-0.1" is a SUID script, but looking at the source code we can see that there is now way of a buffer overflow or similar, no way of getting it to do things it wasn't meant to do. So let's look around a bit more:
The SUID search gives us no results we can work with - nothing out of the ordinary here. I was stuck at this point a long time, until a friend told me about a program I never used before, but seems to be very common: getcap. It can help to find executables that run with elevated permissions.
Running it like this:
reveals two things: the "logMonitor-0.1" we have seen before and something called "tac".
Bingo! This is a program with root rights that can read files. So, running it like this:
gives us the root flag and thus concludes this Write-Up!