Lab 03: OWASP A03 — Injection Attacks

Objective

Attack a live server with real injection vulnerabilities using Kali Linux: bypass authentication with manual SQL injection, dump the full database with sqlmap, execute OS commands via command injection, achieve Remote Code Execution through Server-Side Template Injection (SSTI) using Jinja2's Python runtime — then understand why each works and what the fix is.

Background

Injection is OWASP #3 (2021) and has been in the Top 10 every year since 2003. It occurs when untrusted data is sent to an interpreter as part of a command or query. SQL injection can bypass logins, dump entire databases, and (in some configs) read files or execute OS commands. SSTI is newer — Jinja2's template engine can call Python's os.popen() from inside a {{...}} expression, giving full RCE with a single URL parameter. In 2021, SSTI was used against GitLab (CVE-2021-22205) to achieve unauthenticated RCE on 50,000+ servers.

Architecture

┌─────────────────────┐        Docker Network: lab-a03         ┌─────────────────────┐
│   KALI ATTACKER     │ ─────── HTTP attacks ─────────────▶   │   VICTIM SERVER     │
│  innozverse-kali    │                                         │  innozverse-cybersec│
│  sqlmap, curl,      │ ◀────── responses ───────────────────  │  Flask API :5000    │
│  python3            │                                         │  (SQLi, CMDi, SSTI) │
└─────────────────────┘                                         └─────────────────────┘

Time

45 minutes

Prerequisites

  • Lab 01 completed (two-container setup)

Tools

  • Victim: zchencow/innozverse-cybersec:latest

  • Attacker: zchencow/innozverse-kali:latest (sqlmap, curl, python3)


Lab Instructions

Step 1: Environment Setup — Launch Victim Server

📸 Verified Output:


Step 2: Launch Kali Attacker

Inside Kali:


Step 3: Recon — nmap + gobuster

📸 Verified Output:


Step 4: SQL Injection — Login Bypass

📸 Verified Output:

💡 admin'-- works because -- is the SQL comment character. The query becomes SELECT * FROM users WHERE username='admin' — the password check is completely removed. There is no password needed. The only fix is parameterised queries: db.execute("SELECT * FROM users WHERE username=? AND password=?", (u, p)) — the ? placeholder means the input is always treated as data, never as SQL syntax.


Step 5: SQL Injection — UNION Data Dump (Manual)

📸 Verified Output:


Step 6: sqlmap — Automated Full Database Dump

📸 Verified Output:


Step 7: Command Injection — OS Shell Access

📸 Verified Output:

💡 ;id works because the shell processes ; as a command separator. The server runs ping -c 1 ;id which executes two commands: ping -c 1 (fails — no host), then id (succeeds). Semicolons, &&, ||, backticks, $(), and | are all command separators. The fix: never pass user input to shell=True. Use subprocess.run(['ping', '-c', '1', host], shell=False) with an explicit list — the shell is never invoked.


Step 8: SSTI — Server-Side Template Injection Detection

📸 Verified Output:


Step 9: SSTI — Remote Code Execution

📸 Verified Output:

💡 SSTI gives full RCE because Jinja2 templates have access to Python's entire runtime. request.application.__globals__ reaches Flask's global namespace, from which we can import os and call any system command. The fix: never pass user input into render_template_string(). Use render_template('file.html', name=name) — template files are static; only the variable name is substituted, not evaluated as a Jinja2 expression.


Step 10: Cleanup

On your host:


Remediation

Vulnerability
Broken Code
Fix

SQLi (login)

f"...WHERE username='{u}'"

db.execute("...WHERE username=?", (u,))

SQLi (search)

f"...LIKE '%{q}%'"

db.execute("...LIKE ?", (f'%{q}%',))

Command injection

subprocess.check_output(f'ping {host}', shell=True)

subprocess.run(['ping','-c','1', host], shell=False)

SSTI

render_template_string(f'Hello {name}!')

render_template('hello.html', name=name)

Summary

Attack
Tool
Result

SQLi login bypass

curl

Logged in as admin with wrong password

SQLi UNION dump

curl + python3

Dumped all usernames and passwords

SQLi full dump

sqlmap

Automatic — all tables dumped

Command injection

curl

uid=0(root) — full OS shell

SSTI detection

curl

{{7*7}}49 confirms Jinja2

SSTI RCE

python3

os.popen("id") — remote code execution as root

Further Reading

Last updated