// 2 ZERO-DAY · 2 CVE · 4 EXPLOIT IN THE LAST 24H

Web Application Weaknesses: Mapping DVWA Attack Categories

Lab Wintermute's DVWA server at 192.0.2.30 is where the abstract concepts from earlier pages become tangible. After building our Python foundation and standing up the isolated network, we now examine how deliberately vulnerable code teaches defensive thinking. DVWA—check the latest release on digininja's GitHub repository—provides four security levels that function as a teaching scaffold: Low (no protection), Medium (naive defenses), High (more sophisticated barriers), and Impossible (what secure implementation actually looks like). I treat these as progressive lenses, not difficulty settings. A student who rushes to exploit Low without studying Impossible learns to break things, not to build or defend them.

SQL Injection: When Input Becomes Code

The vulnerability that refuses to die. SQL injection (Structured Query Language injection, where attacker-controlled input alters database queries) remains common because developers continue concatenating strings instead of separating code from data.

In DVWA's Low security level, the "User ID" field on the SQL Injection page accepts raw input directly into a query. Conceptually: the application builds SELECT first_name, last_name FROM users WHERE user_id = '$id' by dropping your input into that $id slot. Submit 1 and you get a name. Submit 1' OR '1'='1 and the query becomes ...WHERE user_id = '1' OR '1'='1'—always true, returning all rows. The database cannot distinguish between code and data because the developer failed to distinguish them first.

Here's where our Python work pays off. From the Kali workstation at 192.0.2.10, we automate detection with requests, building on the syntax patterns established in Pages 1-2:

#!/usr/bin/env python3
# sqli_probe.py — conceptual probe for DVWA SQLi at Low security
# Run only against authorized lab targets. Snapshot before testing. import requests TARGET = "http://192.0.2.30/dvwa/vulnerabilities/sqli/"
# DVWA requires authentication; session cookie from browser login
COOKIES = {"PHPSESSID": "your_session_here", "security": "low"} # Probe: classic tautology test that should return extra rows
# if input is concatenated unsafely into the query
payload = {"id": "1' OR '1'='1", "Submit": "Submit"} r = requests.get(TARGET, params=payload, cookies=COOKIES)
print(f"Status: {r.status_code}")
print(f"Length: {len(r.text)}")
# illustrative output — verify on your target
# Length: 3847 (vs ~1200 for benign '1' query suggests data leak)

Why this matters: The length difference is your first signal. A benign query returns one record; the tautology returns multiple. This is reconnaissance, not exploitation—we map the vulnerability's existence without extracting sensitive data. The ethical boundary lives in what you do with confirmed access, not in the confirmation itself.

The Medium level adds mysql_real_escape_string() or similar, which students often bypass with numeric inputs where quotes aren't needed: 1 OR 1=1. High level may use preg_replace to strip keywords—a brittle approach that fails against encoded variants. The Impossible level demonstrates parameterized queries (prepared statements with variable binding), the recommended primary defense against SQL injection. The developer defines all SQL logic first: SELECT first_name, last_name FROM users WHERE user_id = ?, then binds the parameter separately. The database treats input exclusively as data, never as executable code. Stored procedures can help but require equally safe implementation; they are not automatically protective.

Cross-Site Scripting: Trusting the Browser Too Much

XSS (Cross-Site Scripting, where attacker scripts execute in a victim's browser within the target site's context) exploits the browser's inability to distinguish legitimate page code from injected markup. DVWA demonstrates two variants.

Reflected XSS: malicious input appears in the immediate response. In Low security, the "Name" field on XSS (Reflected) echoes your input directly into the page. Submit <script>alert('xss')</script> and the browser executes it. The script "reflects" off the server once. Dangerous for phishing: an attacker crafts a link with the payload encoded, sends it to a victim, and harvests session cookies if the site lacks protective headers.

Stored XSS: the payload persists in the database and executes for every visitor. DVWA's XSS (Stored) page—a guestbook-style input—accepts the same script tag. Now the attack survives the original session, affecting administrators who later review entries. Session hijacking risk emerges because browsers send cookies automatically; a script reading document.cookie and exfiltrating to an attacker-controlled server compromises authentication tokens.

Defense layers matter here. Input validation helps but context-sensitive encoding is essential—JavaScript context requires different escaping than HTML. The Content-Security-Policy response header, delivered to the browser to enforce a CSP, sets directives such as default-src and img-src to control valid sources for resources. A basic deployment: Content-Security-Policy: default-src 'self'; script-src 'self'. This prevents inline script execution entirely, neutralizing both reflected and stored variants. The Content-Security-Policy-Report-Only variant supports monitoring violations without blocking—useful during deployment testing, with report-to preferred over the deprecated report-uri for violation reporting.

⚠️ Authorized, defensive use only.

# Verify CSP presence on a target (lab only)
curl -I http://192.0.2.30/dvwa/ 2>/dev/null | grep -i content-security
# illustrative output — verify on your target
# Content-Security-Policy: default-src 'self'

In plain terms: CSP tells the browser "only run scripts from these approved addresses," so even if an attacker injects script tags, the browser refuses to execute them.

Command Injection: The OS Boundary Collapse

Command injection occurs when application input reaches the operating system shell. DVWA's Command Injection page at Low security passes the "IP address" field directly to ping. Enter 192.0.2.1 and the server runs ping -c 4 192.0.2.1. Enter 192.0.2.1; cat /etc/passwd and the semicolon terminates the ping command, starting a second. The application intended network diagnostics; the attacker gained arbitrary command execution.

The mechanism is concatenation again—input glued to code. Medium level filters && and ; but often misses pipe | or backticks. High level uses more extensive filtering, still vulnerable to creative bypasses. Impossible level uses ip_check() with strict validation or, better, avoids shell invocation entirely—calling PHP's internal network functions or Python's subprocess with argument lists that prevent shell interpretation.

Why this matters: The semicolon is not magical. The shell interprets it as command termination because the application passed user input to a shell interpreter. The fix is not "filter semicolons" but "never pass user input to a shell." Allowlisting—permitting only expected characters (digits, dots for IP addresses)—provides defense in depth when architectural separation isn't immediately achievable.

File Inclusion: When Paths Wander

File inclusion vulnerabilities let attackers manipulate file paths to access unintended resources. DVWA's File Inclusion pages demonstrate this through page parameters like ?page=include.php. At Low security, substituting ?page=/etc/passwd succeeds if the application concatenates or directly uses the parameter without path restriction.

Path traversal (directory traversal, using ../ sequences to escape intended directories) extends this: ?page=../../../etc/passwd walks up the filesystem. The mechanics depend on insufficient path canonicalization—the server resolves relative paths against attacker input rather than enforcing a base directory.

Medium and High levels add naive filters: stripping ../, which ....// bypasses; or requiring file:// prefixes, which doesn't constrain location. Impossible level uses strict path canonicalization (resolving to absolute paths and verifying they remain within an allowed base directory) or, more robustly, mapping allowed files through an explicit allowlist rather than dynamic path construction.

Brute Force Authentication: Patience as Attack

DVWA's Brute Force page presents a login form vulnerable to automated guessing at Low security. The pedagogical value lies in understanding rate-based attacks and their countermeasures, not in harvesting credentials.

From our Kali workstation, we might integrate Hydra for systematic testing against the authorized lab target:

# Hydra against DVWA login — LAB AUTHORIZED TARGET ONLY
# -l single username, -P password list, http-post-form module
# syntax: path:postdata:success/failure condition string hydra -l admin -P /usr/share/wordlists/rockyou.txt \ 192.0.2.30 http-post-form \ "/dvwa/vulnerabilities/brute/:username=^USER^&password=^PASS^&Login=Login:Username and/or password incorrect."

⚠️ Authorized, defensive use only.

The Medium level adds a sleep(2) delay—naive, bypassable with patience. High level may introduce CAPTCHA or track attempts per session. Impossible level implements account lockout after defined failures and, critically, multi-factor authentication (MFA, requiring a second verification factor beyond password). Even with correct credentials, the attacker lacks the time-based or hardware-generated second factor.

The defensive progression matters more than the attack: lockout prevents automated guessing; MFA prevents credential reuse entirely. In production, implement both—lockout with care to avoid denial-of-service against legitimate users, MFA with recovery mechanisms that don't become alternate attack paths.

Documenting for the Learning Report

Lab Wintermute's engagement goal is comprehension, not compromise. For each DVWA category at 192.0.2.30, your report should capture: the security level tested, the conceptual vulnerability mechanism, your Python or tool-based detection approach, the defensive control demonstrated at Impossible level, and why that control succeeds where weaker variants fail. Snapshot rollback between categories ensures clean state; network isolation at the VirtualBox NAT layer ensures no accidental external reach.

This page's techniques—network reconnaissance with Python, web vulnerability mapping, defensive control analysis—feed directly into the consolidated simulation covered later. The patterns you trace on DVWA's deliberately broken code are the same patterns you'll recognize in production code reviews, bug bounty programs, and incident response. The difference is authorization, scope, and intent.

Further reading