If you find a SQL injection vulnerability on a web application, what can you do with it? The answer is that it may be possible to leverage it to gain LocalSystem privileges on the Windows machine that hosts the MySQL instance. How does this work? Well, MySQL, as a fully featured Relational Database Management System (RDBMS) offers lots of useful functionality like “select into dumpfile”. This allows the web application developer to create files on the file system at runtime, typically containing extracted database table blobs, clobs, or other data which should not be reformatted as part of the extract. MySQL also supports user defined functions (UDFs), allowing the application developer to define commonly used functionality within a single interface on the database. MySQL UDFs can be created using either standard SQL statements, or using a DLL file hosted on the filesystem. MySQL also allows the programmer to manipulate binary values directly using the “0x” prefix. Put all of this together, and it is possible to create a suitable DLL using C, C++, or some other language, and use MySQL to select the binary contents of the DLL into a dumpfile on the filesystem, and then create a UDF linking to the DLL.
We created a DLL to execute a command on the operating system, and return its output. For ease of implementation, we simply caught the standard output stream, and ignored standard error. This was not an issue since it can be handled in the command passed to the function at runtime.
The SQL to create the DLL on the filesystem is the following (making sure we’re on the right database first). Note that this has been truncated for display purposes, but the full SQL to create the DLL on the filesystem is contained in this attachment create-dll.
use mysql;
select 0x4d5a900003 <snipped here for brevity>
into dumpfile 'c:\\Program Files (x86)\\MySQL\\MySQL Server 4.1\\bin\\msvcrt.dll';
Some things to watch out for here are that the box hosting the MySQL instance is actually a Windows box, since MySQL also runs on Linux. The MySQL path must also be correctly specified. The correct path can be determined by querying MySQL global variables that include path details, such as @@character_sets_dir, @@slow_query_log_file, @@plugin_dir, etc. In this case, we created a file called “msvcrt.dll” in the MySQL bin directory. Note that the “into dumpfile” functionality does not allow existing files on the filesystem to be over-written, so it may be a good idea to build up a random file name for each file creation attempt, in order to minimise the probability of using a stale or incorrect DLL file. Depending on the exact MySQL version, when creating the function, MySQL will only load the DLL file from specific locations. As of version 5.0.67, MySQL will only load DLL files from the directory specified in the MySQL @@plugin_dir global variable. Before version 5.0.67, in our experience, the best place to put the DLL file is directly in the MySQL bin directory.
Once the DLL file has been created, lets query the list of functions that already exist.
select * from func;
If the function already exists, then it must be dropped before it can be re-created.
drop function do_command;
Next comes the creation of the function:
create function do_command returns string soname 'msvcrt.dll';
If the function was successfully created, then the following SQL statement will allow us to execute a command on the operating system of the machine which hosts the MySQL instance. Since, in Windows, the MySQL service runs as LocalSystem by default, this will give you escalated privileges on the machine, as well as the ability to see the full results of the command. Any errors that occur when the command is executed can be caught using the “2>&1″ idiom, which works in Windows 7, at least.
mysqL> select do_command("whoami 2>&1");
+----------------------------------------+
| do_command("whoami 2>&1") |
+----------------------------------------+
| nt authority\system |
+----------------------------------------+
1 row in set (0.09 sec)
mysql>
Note that this basic idea is based on that used by raptor at http://www.securiteam.com/exploits/6G00P1PC0U.html, but with a different implementation, to allow for the output of the command to be returned.