Sqlmap: [FEATURE REQUEST] Server Filesystem Enumeration using SQL injection

Created on 2 Feb 2018  Â·  14Comments  Â·  Source: sqlmapproject/sqlmap

Tools

_SQLMAP_ is a powerful tool (with overwhelming amount of features), but there is currently no real functionality to perform servers's file system enumeration via SQL injection (e.g. MySQL's load_file(...)). If you want to scan web folder, then tools like _DirBuster_ already exist and do their job very well, but if you want to enumerate server's internal structure by using SQL injection, then ... there is currently only such a tool which is called _SqlNuke_ and is written in ruby (and not python =( ) and isn't far so mature tool as sqlmap is (ok there is also a Metasploit tool, but it's still not cool enough: https://digi.ninja/metasploit/mysql_file_enum.php).

meme

Why SQLMAP?

Why is sqlmap so perfect to solve this task? Because it has already all the necessary functionality to make an SQL injection in very different ways (cookies & headers, POST & GET requests, payload suffix, prefix, time based, blind, union based, stacked, SQL injection type recognition and payload generation and and and ... - you know how much it is ;)). So why not just add another cool option to contribute to the glory of this great tool!

There are several possible scenarios, where it might be useful

1. File privilege is on, but it's impossible to write file to any world-viewable folder

  1. All world-viewable folders are protected -> still possible to write to "non-public" folders (we can also check if the file exists if we are able to write to some non-public folder from the outside, but if we try to create file with specific name we'll fail because MySQL's into outfile can't overwrite files)
  2. MySQL's into outfile requires _quotes_ or _doublequotes_ (load_file(...) can still work with HEX), which are e.g. blocked.

2. File priv is on: We can read and write files, but we just don't know DocumentRoot's location and need to bruteforce it. Quotes aren't blocked.

  1. "Spammer 4ever"-style: try to write a file with special payload (easier detection in the next step, if it's a right file) (MySQL: into outfile) and then try to read it back (MySQL: load_file(...)). Problem: Some folders might be still protected, or there might be a rare case with file collision (MySQL's into outfile can't overwrite existing files)(file names must be carefully chosen to avoid possible collisions).
    After such "attack" we definitely will need to clean up the server xD so a list with tested paths would be useful...
  2. We know how our script in the DocumentRoot is called (e.g. index.pl), so we will try to bruteforce DocumentRoot (maybe we have some minimal information leak found, where we see relative path like use lib qw(../../../service/lib); and then later in the code use Foo::Engine; (e.g. leaked perl code)) by using bugs (read below) or dictionary-based or pure bruteforce. (consider the case where we can't find DocumentRoot via php bugs (or similar) or google search or in log files or in database itself - we just d-o-n't have it =()

Note

  1. MySQL's load_file() works with HEX-encoded or quoted string and works also with relative paths, e.g.: load_file("../../../../Apache/htdocs/path/file.php") (current working directory on MySQL is @@datadir (default path is /var/lib/mysql))
    (https://websec.wordpress.com/2007/11/17/mysql-table-and-column-names/)
  2. Stacked queries ";" are disabled by default, so I'm considering here the most common case where we can use subqueries (nested queries) only
  3. I use MySQL only as example here

Suggestions

  1. Fixed list with default/common file paths of config files
    /etc/passwd
    /etc/init.d/apache/httpd.conf
    /etc/init.d/apache2/httpd.conf
    ...
    -> https://wiki.apache.org/httpd/DistrosDefaultLayout
    -> nginx, Apache, lighttpd, Apache Tomcat, Jetty, ...
    -> possibility to use own lists
  2. Dictionary based search or pure bruteforce
    {variable_path}{fixed_filename} OR {fixed}{variable}{fixed}
    Example: /var/www/{variable}/htdocs/index.php
    Where {variable} might be from a wordlist or be just bruteforced (only lower-case chars OR only upper-case chars OR only numbers OR upper-case + lower case OR ... etc)
    And don't forget about "ugly" file or folder names. Example /var/www/java_1.6/file.txt, so we need to bruteforce only the java version in this folder name: {fixed}{java_{0..9}[1].{0...9}[1]}{fixed}
  3. Using cool bugs on specific MySQL versions:
    https://securityweekly.com/2013/01/18/mysql-file-system-enumeration/
    -> Instead of enumerating file names we can use this bug to enumerate directories
    -> Windows MySQL 4.1.7, 5.5.29, Linux MySQL 5.5.25, ... (and maybe other versions?)(maybe there are similar bugs on other DBMSs?) - btw is there a CVE for this bug?!

Not so important question

Does SQLMAP use any of known *SQL's bugs which you can find in the CVE-list?
And again, just for example MySQL: https://www.cvedetails.com/vulnerability-list.php?vendor_id=185

Most helpful comment

@stamparm awesome, thank you so much!

does this flag support also Windows paths? Windows accepts paths like:

  • C:\\boot.ini
  • C:/boot.ini
  • C:\Users\Foo\.conda\environments.txt
  • C:/Users/Foo/.conda/environments.txt

All 14 comments

Got my vote for that picture

On Feb 1, 2018, at 7:51 PM, nuke2night notifications@github.com wrote:

Tools

SQLMAP is a powerful tool (with overwhelming amount of features), but there is currently no real functionality to perform servers's file system enumeration via SQL injection (e.g. MySQL's load_file(...)). If you want to scan web folder, then tools like DirBuster already exists and make their job very well, but if you want to enumerate server's internal structure by using SQL injection, then ... there is currently only such a tool which is called SqlNuke, which is written in ruby (and not python =( ) and isn't far so mature tool as sqlmap is.

Why SQLMAP?

Why is sqlmap so perfect to solve this taks? Because it has already all the necessary functionality to make an SQL injection in very different ways (cookies & headers, POST & GET requests, payload suffix, prefix, time based, blind, union based, stacked, SQL injection type reconginition and payload generation and and and ... - you know how much it is ;)). So why not just to add another cool option to contribute to the glory of this great tool!

There are several possible scenarios, where it might be useful

  1. File privilege is on, but it's impossible to write file to any world-viewable folder

All world-viewable folders are protected -> still possible to write "non-public" folders (we can also check if the file exists if we are able to write to some non-public folder from the outside, but if try to create file with specific name we'll fail because MySQL's into outfile can't overwrite files)
MySQL's into outfile requires quotes or doublequotes (load_file(...) can still work with HEX), which are e.g. blocked.

  1. File priv is on: We can read and write files, but we just don't know DocumentRoot's location and need to bruteforce it. Quotes aren't blocked.

"Spammer 4ever"-style: try to write a file with special payload (easier detection in the next step, i it's a right file) (MySQL: into outfile) and then try to read it back (MySQL: load_file(...)). Problem: Some folders might be still protected, or there might be a rare case with file collision (MySQL's into outfile can't overwrite existing files)(file names must be carefully chosen to avoid possible collisions).
After such "attack" we definitely will need to clean up the server xD so a list with tested paths would be useful
We know how our script in the DocumentRoot is called (e.g. index.pl), so we will try to bruteforce directories (maybe we have some minimal information leak found, where see relative path inclusion like use lib qw(../../../service/lib); and then later use Foo::Engine; (e.g. leaked perl code))
Suggestions

Fixed list with default/common file paths of config files
/etc/passwd
/etc/init.d/apache/httpd.conf
/etc/init.d/apache2/httpd.conf
...
-> https://wiki.apache.org/httpd/DistrosDefaultLayout
-> nginx, Apache, lighttpd, Apache Tomcat, Jetty, ...
-> possibility to use own lists
Dictionary based search or pure bruteforce
{variable_path}{fixed_filename} OR {fixed}{variable}{fixed}
Example: /var/www/{variable}/htdocs/index.php
Where {variable} might from a wordlist or be just bruteforced (only lower-case chars OR only upper-case chars OR only numbers OR upper-case + lower case OR ...)
Using cool bugs on specific MySQL versions:
https://securityweekly.com/2013/01/18/mysql-file-system-enumeration/
-> Instead of enumerating file names with can use this bug to enumerate directories
-> Windows MySQL 4.1.7, 5.5.29, Linux MySQL 5.5.25, ... (and maybe other versions?)(maybe there are similar bugs on other DBMSs?)
Note

I use MySQL only as example here.

—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub, or mute the thread.

Im suggesing something like this: --file-enumerate -f list.txt
where list.txt contains
/etc/passwd
/etc/init.d/apache/httpd.conf and etc

I made a perl script to enumerate and download all the files, automating the sqlmap.

At moment there are following possibilities to scan/download (within response) files from server:
--sql-query=QUERY SQL statement to be executed
--sql-shell Prompt for an interactive SQL shell
--sql-file=SQLFILE Execute SQL statements from given file(s)

btw not only enumeration, but also download option would be really cool. Just extract file contents from response & save to file in sqlmap's extra folder _server-files_. For example this might look like this:
.sqlmap/output/example.com/server-files/etc/passwd, where _server-files_ is just a placeholder for _root_ "/".

I needed this small script for an engagement to show a customer How you can read files from the server.
https://github.com/devcoinfet/Sqlmap_file_reader/blob/master/file_read.py

yes, but it is stupid to write own scripts. just let others write them. xD

no. now seriously. sqlmap has everything, but not this feature and i'm tired of writing own dumper scripts. I mean it's not a big deal to write it once or twice for differrent cases. But that's why people started to create more or less generic tools. Just to avoid repetitive work.

Well the script I gave U does exactly what your asking for just tweak it and change the domain to come from sys.argv[1] or such

I literally code all day everyday, So I highly disagree about letting others write our code for us.

2 reasons

1.)Its a learning experience
2.)it helps You understand the vulnerability in better detail

Well the script I gave U does exactly what your asking for just tweak it and change the domain to come from sys.argv[1] or such

I literally code all day everyday, So I highly disagree about letting others write our code for us.

2 reasons

1.)Its a learning experience
2.)it helps You understand the vulnerability in better detail

you missed the point. this is not a thread for philosophical discussions. here is a feature request for a popular tool, that i personally miss, that's it. you wonna write your scripts & tools i won't stop you from doing it.

--common-files      Check existence of common files

p.s. checks for these files

@stamparm awesome, thank you so much!

does this flag support also Windows paths? Windows accepts paths like:

  • C:\\boot.ini
  • C:/boot.ini
  • C:\Users\Foo\.conda\environments.txt
  • C:/Users/Foo/.conda/environments.txt

It works indeed on windows by using paths like "\windows\system32\drivers\etc\hosts" (without "C:\" or "C:/" or any other drive prefix)

@edgarolijar

A) well, I left the issue deliberately opened for further discussion (like your comment). Currently paths are sent in unmodified form as put inside the common-files.txt. I'll also introduce the additional question whether user wants to provide its own filepaths (via his local file).
B) from my own experience, DBMSes on Windows should accept both / and \ formats. If anybody will notice different, they are free to "nag" and I'll do additional logic :)

currently it is possible just to edit the file "sqlmap/data/txt/common-files.txt" and just to add own stuff there. maybe it would be cleaner just to add another flag like "--custom-files "?
(but what should then happen if both flags are being selected?)

another question is - if the backend escapes quotes, then load_file(...) will not be able to read any files (e.g. load_file("/etc/passwd")) - will the flag --hex turn the file path string "/etc/passwd" into hex?
load_file(0x2f6574632f706173737764) can still work with hex encoded paths (... into outfile can't ...).

@edgarolijar

A) with the latest revision you'll be asked (like in other --common-... switches) whether you want to use default common files file or you want to provide your own
B) checked common filepaths are now automatically converted to slash format (e.g. C:\foobar -> C:/foobar)
C) --hex doesn't convert file paths to hexadecimal format, only the retrieved value
D) there won't be any more changes to the current switch/option command line logic regarding the --common-files. It is perfectly inline with the rest of similar --common-.. switches

Was this page helpful?
0 / 5 - 0 ratings

Related issues

serdaresen06 picture serdaresen06  Â·  4Comments

mekoki88 picture mekoki88  Â·  6Comments

JonhSilver picture JonhSilver  Â·  4Comments

vitural picture vitural  Â·  7Comments

paktiger picture paktiger  Â·  3Comments