Hack Your First Web App with DVWA
Exploit SQL injection, XSS, and command injection on a local vulnerable web app.
Introduction
30 Second Summary
Every time you type something into a website, your input travels through layers of code that decide what to do with it. When that code trusts your input without checking it, attackers can make the application do things it was never designed to do.
In this project, you will spin up a deliberately vulnerable web application, intercept its traffic with a professional pentesting tool, and exploit three of the most common vulnerabilities found in real-world security audits.
What You'll Build
A complete web application pentesting workflow covering traffic interception, vulnerability identification, and exploitation of SQL Injection, Reflected XSS, and OS Command Injection.
By the end of this project, you'll have:
- A local pentesting lab running DVWA in Docker, serving intentionally vulnerable web pages.
- Proof-of-concept exploits for three vulnerability classes using Burp Suite as your interception proxy.
- Extracted database credentials (usernames and MD5 password hashes) via UNION-based SQL injection.
- Secret Mission: Bypass the Medium-level security filters on Command Injection by reading source code and crafting a payload that uses an unblocked shell metacharacter.
Are there any prerequisites?
You should have a basic understanding of how websites work (URLs, forms, HTTP requests). You also need Docker Desktop and Burp Suite Community Edition installed on your machine.
Before We Start
Before We Start...
Launch Your Pentesting Lab
Before you can hack a web application, you need a safe target to practice on. Running attacks against real websites without permission is illegal. That is where DVWA (Damn Vulnerable Web Application) comes in.
DVWA is a deliberately broken web application that gives you permission to attack it. In this step, you will spin it up in Docker, configure its security level, and connect Burp Suite so you can see every request your browser sends.
In this step, get ready to:
- Start DVWA in a Docker container and access it in your browser.
- Initialize the DVWA database and set the security level to Low.
- Configure Burp Suite to intercept traffic from DVWA.
Start DVWA in Docker
DVWA runs as a container, which means you can start and stop the entire vulnerable web app with a single command. The container bundles PHP, MySQL, and Apache together so there is nothing else to install.
- Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
- Type Terminal (macOS) or PowerShell (Windows) and press Enter to open it.
- Start the DVWA container by running this command:
docker run --rm -d --name dvwa -p 4280:80 vulnerables/web-dvwa
What does this command do?
- docker run downloads the image (if needed) and starts a new container.
- --rm automatically deletes the container when you stop it, keeping things clean.
- -d runs the container in the background so your terminal stays free.
- -p 4280:80 maps port 4280 on your machine to port 80 inside the container.
Why port 4280?
Burp Suite uses port 8080 by default for its proxy. Port 80 can require admin privileges on Windows. Port 4280 avoids conflicts with both.
- Wait about 30 seconds for the container to finish initializing.
- Open your web browser and navigate to http://localhost:4280.
You should see the DVWA login page with a dark background and a red logo.
Don't see the login page?
- Make sure Docker Desktop is running. Check your system tray (Windows) or menu bar (macOS) for the Docker whale icon.
- If you see "port already in use", another process is on port 4280. Stop it or change the port number in the command (e.g., -p 4281:80).
- If the page loads slowly or shows an error, wait another 30 seconds. The MySQL database inside the container takes time to start.
Help me troubleshoot my DVWA container not starting
Initialize the DVWA database and set security level
DVWA needs its database tables created before you can use the vulnerability modules. You also need to set the security level to Low, which disables all input filtering so you can focus on understanding how each attack works.
- On the DVWA login page, enter admin as the username.
- Enter password as the password.
- Click Login.
You should land on the DVWA setup page.
- Scroll down and click Create / Reset Database at the bottom of the page.
The page refreshes and shows a success message. DVWA logs you out after resetting the database.
- Log in again with username admin and password password.
- Click DVWA Security in the left sidebar menu.
- In the Security Level dropdown, select Low.
- Click Submit.
The page confirms the security level is now set to Low.
What does Low security mean?
DVWA has four security levels: Low, Medium, High, and Impossible. At Low, the application applies zero input filtering or validation. This lets you see exactly how each vulnerability works without any defenses getting in the way.
Later in the Secret Mission, you will raise the level to Medium and learn how to bypass basic filters.
Configure Burp Suite and verify interception
Burp Suite sits between your browser and DVWA, capturing every HTTP request and response. This lets you inspect, modify, and replay traffic. You will use Burp's built-in browser, which is already configured to route through the proxy.
Why use Burp's built-in browser?
Configuring your main browser to use Burp as a proxy requires installing a CA certificate and changing system proxy settings. Burp's built-in Chromium browser comes pre-configured, so you can start intercepting immediately with no extra setup.
- Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
- Type Burp Suite and press Enter to open it.
Burp Suite may take a moment to load. You will see a project selection screen.
- Select Temporary project and click Next.
- Select Use Burp defaults and click Start Burp.
Burp Suite opens to its dashboard. Now you need to launch the built-in browser.
- Click the Proxy tab at the top of the Burp window.
- Click Open browser to launch Burp's built-in Chromium browser.
A Chromium browser window appears. All traffic from this browser passes through Burp's proxy automatically.
- In Burp's browser, navigate to http://localhost:4280.
- Log in with username admin and password password.
- Back in Burp Suite, click the Proxy tab, then click HTTP history.
You should see a list of HTTP requests to localhost:4280. These are the requests your browser made to DVWA. You can click any row to inspect the full request and response.
What is HTTP history?
HTTP history is Burp's log of every request and response that passed through its proxy. Each row shows the method (GET or POST), the URL, the status code, and the response size. This is your primary tool for understanding what happens when you interact with a web application.
HTTP history is empty?
- Make sure you are browsing DVWA in Burp's built-in browser, not your regular browser. Only Burp's browser routes through the proxy by default.
- Check that you navigated to http://localhost:4280 (not https). DVWA runs on plain HTTP.
- Try clicking a few links inside DVWA and check HTTP history again. Some versions of Burp filter display results by default.
Help me troubleshoot Burp Suite not capturing DVWA traffic
Your pentesting lab is live. You have DVWA running at Low security with Burp Suite capturing every request. Next up, you will use Burp's Repeater to craft SQL injection payloads and extract real data from the database.
SQL Injection - Extract Data with Burp Repeater
Your pentesting lab is running and traffic is flowing through Burp Suite. Now it is time to use that interception power to find and exploit a real vulnerability.
SQL Injection is one of the most dangerous vulnerabilities on the web. It happens when a web application takes user input and plugs it directly into a database query without any filtering. In this step, you will use Burp's Repeater tool to craft malicious queries that trick DVWA's database into handing over usernames and password hashes.
In this step, get ready to:
- Intercept a normal SQL Injection request and send it to Burp Repeater.
- Confirm the application is vulnerable by injecting a payload that returns all users.
- Extract usernames and password hashes from the database using UNION injection.
Intercept and examine a normal request
Before injecting anything malicious, you need to understand what a normal request looks like. This gives you a baseline to compare against when your payloads change the application's behavior.
- In Burp's built-in browser, click SQL Injection in the DVWA menu on the left.
- Enter 1 in the User ID field and click Submit.
You should see information for a user with ID 1 displayed on the page (First name and Surname).
- Switch to Burp Suite and click the Proxy tab, then click HTTP history.
- Find the GET request to /vulnerabilities/sqli/ in the list. It will have id=1 in the URL parameters.
- Right-click that request and select Send to Repeater.
- Click the Repeater tab at the top of Burp Suite.
You should see the full GET request in the left panel. Notice the id=1 parameter in the URL. This is the injection point you will target.
What is Burp Repeater?
Repeater lets you manually modify and resend individual HTTP requests as many times as you want. You can tweak a single parameter, hit Send, and instantly see how the server responds.
This makes it perfect for testing injection payloads. Instead of retyping in the browser every time, you edit the raw request directly and watch the response change.
Don't see the request in HTTP history?
Make sure you submitted the form in Burp's built-in browser, not your regular browser. Only traffic from Burp's browser flows through the proxy.
If the history list is long, use the filter bar at the top and search for sqli to narrow it down.
Help me find my SQL Injection request in Burp's HTTP history.
Test for SQL injection and dump all users
A classic way to test for SQL injection is to inject a condition that is always true. If the application returns more data than it should, you have confirmed the vulnerability.
- In the Repeater tab, find the id=1 parameter in the request URL.
- Change the value of id from 1 to this payload:
' OR '1'='1
- Click Send.
Examine the response in the right panel. Instead of one user, you should see multiple users returned (admin, Gordon, Hack, Pablo, Bob).
Why does this payload work?
At the Low security level, DVWA's PHP code builds the database query like this:
SELECT first_name, last_name FROM users WHERE user_id = '$id';
Your input gets plugged directly into $id with no filtering. When you enter ' OR '1'='1, the query becomes:
SELECT first_name, last_name FROM users WHERE user_id = '' OR '1'='1';
Since '1'='1' is always true, the database returns every row in the table.
Only seeing one user in the response?
Check that you replaced the entire value after id=. The parameter should read id=' OR '1'='1 with no leftover 1 in front of the single quote.
Also confirm that DVWA is still set to Low security. If it was changed to Medium or High, the payload may be blocked.
Help me debug my SQL injection payload in Burp Repeater.
Use UNION injection to extract credentials
Confirming a vulnerability exists is step one. Now you will escalate the attack by pulling data from a completely different table. The UNION SQL keyword lets you append a second query's results to the first query's output.
- In Burp Repeater, change the id parameter to this UNION injection payload:
' UNION SELECT user, password FROM users #
- Click Send.
The response now contains usernames and their corresponding MD5 password hashes from the database. You have just extracted the entire users table.
What does this payload do?
- The leading ' closes the original query's string.
- UNION SELECT user, password FROM users appends a second query that pulls the user and password columns from the users table.
- The # character comments out everything after your payload, preventing the original query's trailing syntax from causing an error.
- Look at the response and find the admin user's password hash. It will be a 32-character string of letters and numbers (an MD5 hash).
- To verify the hash, open a new tab in your regular browser and search for "MD5 decrypt" on any online tool.
- Paste the admin hash. It will decode to password, confirming you have extracted real credentials.
Why is this so dangerous?
In a real application, this same technique could expose customer emails, credit card data, or admin credentials. The attacker does not need direct access to the database server. They only need one unprotected input field.
MD5 hashes are especially weak. Many can be reversed instantly using precomputed lookup tables (rainbow tables). Stronger hashing algorithms like bcrypt make this reversal computationally impractical.
Seeing an error instead of credentials?
Make sure there is a space between users and # at the end of the payload. Without the space, the comment character may not be recognized.
Double-check that you typed user and password (not username or passwd). These are the exact column names in DVWA's database.
Help me fix my UNION SQL injection payload.
You just went from a simple form field to extracting every credential in the database. Next up, you will exploit a completely different vulnerability class: injecting JavaScript into a web page to steal session cookies.
Reflected XSS - Inject JavaScript into the Page
In the last step, you proved that SQL injection can leak an entire database. That attack targeted the server-side query logic. Now you are going to flip the target.
Reflected Cross-Site Scripting (XSS) attacks the browser. Instead of manipulating a database query, you inject JavaScript that the server echoes straight into the page HTML. If the application does not sanitize your input, the browser executes it as real code.
In this step, get ready to:
- Identify a reflection point where user input appears in the page.
- Inject a JavaScript payload that triggers an alert box.
- Escalate to a simulated cookie theft that demonstrates session hijacking.
Identify the reflection point
Before injecting anything malicious, you need to find where user input is reflected into the page. If the application echoes your input back without sanitizing it, that is your entry point.
- In Burp's browser, click XSS (Reflected) in the DVWA menu on the left.
- You should see a page with a What's your name? input field.
- Type hacker into the field and click Submit.
The page now displays "Hello hacker". Your input went in through the form and came right back out in the HTML.
- Switch to Burp Suite and click the Proxy tab, then HTTP history.
- Find the GET request to /vulnerabilities/xss_r/.
- Look at the URL parameters. You should see name=hacker in the request.
What is a reflection point?
A reflection point is anywhere user-supplied input appears in the server's response without being encoded or escaped. The server takes the value from the name GET parameter and pastes it directly into the HTML with code like echo '<pre>Hello ' . $_GET['name'] . '</pre>'.
If the server does not escape special characters like < and >, the browser interprets your input as HTML or JavaScript instead of plain text.
Inject a basic script payload
You have confirmed that the application reflects your input. Now test whether the browser will execute JavaScript injected through that same input field.
- In Burp's browser, go back to the XSS (Reflected) page.
- In the What's your name? field, enter this payload:
<script>alert(1)</script>
- Click Submit.
A JavaScript alert box pops up displaying "1". You just proved the application is vulnerable to XSS.
Why does this work?
The server takes your input and inserts it directly into the HTML response. Instead of the page rendering Hello <script>alert(1)</script> as text, the browser sees an actual <script> tag and executes the JavaScript inside it.
The DVWA Low security level applies zero filtering to the input. It also sets X-XSS-Protection to 0, disabling the browser's built-in XSS filter so you can see the vulnerability clearly.
No alert box appeared?
Make sure you are using Burp's built-in Chromium browser, not your main browser. Burp's browser has XSS protection disabled by default for testing.
Confirm your DVWA security level is still set to Low. Check the DVWA Security page in the left menu.
If you typed the payload correctly and still see no alert, help me debug why my XSS payload is not triggering an alert in DVWA at Low security.
Escalate to cookie theft simulation
An alert box proves the concept, but real attackers use XSS to steal session cookies. You will simulate this by making the alert display your actual session cookie.
- In the What's your name? field, enter this payload:
<script>alert(document.cookie)</script>
- Click Submit.
The alert now shows your PHPSESSID cookie and your security level. This is the session token that keeps you logged in as admin.
How would a real attacker use this?
In a real attack, the attacker replaces alert() with code that sends the cookie to their server. With your session cookie, they can impersonate you without needing your password.
This is called "reflected" XSS because the payload lives entirely in the URL. Look at the name parameter in Burp's HTTP history. The attacker would craft a malicious URL and trick a victim into clicking it.
- Switch to Burp Suite and open the Proxy tab, then HTTP history.
- Find the latest GET request to /vulnerabilities/xss_r/.
- Look at the full URL. You should see the entire <script>alert(document.cookie)</script> payload visible in the name parameter.
This confirms the attack vector. Anyone who clicks a link containing that URL would have their cookies stolen.
Reflected vs Stored XSS
Reflected XSS requires tricking a victim into clicking a crafted link. The payload only fires for that one request. Stored XSS is more dangerous because the attacker saves the payload in the database (like a comment or profile field), and it fires for every user who views that page.
DVWA has a separate Stored XSS module you can explore after this project.
You have now exploited two vulnerability classes. SQL injection leaked the database, and XSS proved you can execute arbitrary JavaScript in a victim's browser. Next up, you will go after the operating system itself with command injection.
Command Injection - Execute System Commands
You've demonstrated that XSS can steal session cookies from unsuspecting users.
Now you'll exploit the most dangerous vulnerability class on the list: OS Command Injection. This happens when a web application passes user input directly to the operating system's command shell. If you can inject shell commands, you have the same access as the web server process itself.
In this step, get ready to:
- Inject operating system commands through a web form.
- Read the vulnerable source code to understand why the attack works.
- Compare the broken code to a secure implementation.
Exploit command injection at Low security
DVWA has a "Ping a device" feature that takes an IP address and runs the ping command on the server. If the application doesn't sanitize your input, you can sneak additional commands onto the end.
- In Burp's browser, click Command Injection in the DVWA left-hand menu.
- Enter 127.0.0.1 in the IP address field.
- Click Submit.
You should see normal ping output showing packets sent and received from 127.0.0.1. This confirms the feature works as intended.
- Now inject a second command by entering this payload in the IP address field:
127.0.0.1 | whoami
- Click Submit.
The page now shows both the ping output AND the result of whoami. You should see www-data at the bottom, which is the user account running the web server.
What does the pipe character do?
The | (pipe) character tells the server's shell to run a second command after the first one. The server sees ping 127.0.0.1 | whoami and executes both commands.
From the application's perspective, it just ran ping on your input. From the attacker's perspective, you just ran an arbitrary command on the server.
Not seeing www-data in the output?
Make sure you include a space before and after the | character. The full payload is 127.0.0.1 | whoami with spaces around the pipe.
Also confirm your DVWA security level is still set to Low. Check the bottom-left corner of the page where it displays the current level.
Help me troubleshoot why my command injection payload isn't working in DVWA
Read the vulnerable source code
DVWA lets you view the actual PHP source code behind each vulnerability. This is how real penetration testers analyze applications during a white-box assessment.
- Click View Source at the bottom-right of the Command Injection page.
You should see the PHP code that handles your input. The critical line is:
// The application passes your input directly to the shell
$cmd = shell_exec( 'ping -c 4 ' . $target );
Why is this code vulnerable?
The application takes your input ($target) and concatenates it directly into a shell command with zero filtering. Whatever you type goes straight to the operating system.
This means any shell metacharacter you include will be interpreted by the OS. The pipe, semicolon, ampersand, backtick, and many other characters can all chain additional commands.
- Close the source code popup.
- Try reading a system file by entering this payload in the IP address field:
127.0.0.1 | cat /etc/passwd
- Click Submit.
The page now displays the contents of the server's /etc/passwd file, which lists every user account on the system. You just read a file you were never meant to access.
Seeing only ping output with no file contents?
Double-check that you typed cat (not Cat or CAT). Linux commands are case-sensitive.
Help me figure out why my cat /etc/passwd command injection isn't showing file contents
Understand why this is dangerous
Reading files is just the beginning. Let's see exactly what level of access a command injection gives an attacker.
- Enter the following payload in the IP address field:
127.0.0.1 | id
- Click Submit.
The output shows the user ID, group ID, and groups of the process running the web application. This tells an attacker exactly what permissions they have on the system.
What could a real attacker do with this?
With command injection access, an attacker could read configuration files containing database passwords. They could download malware onto the server. They could establish a reverse shell for persistent access.
They could also pivot to other systems on the network. This single vulnerability can lead to full infrastructure compromise.
Now let's look at how a developer should fix this vulnerability.
- Click DVWA Security in the left-hand menu.
- Change the security level to Impossible using the dropdown.
- Click Submit.
- Navigate back to Command Injection in the left-hand menu.
- Click View Source at the bottom-right of the page.
The Impossible level source code uses strict input validation with a regex that only allows IP address characters (digits and dots). It splits the input by dots, checks each segment is a number between 0 and 255, and rejects anything else.
Why whitelisting beats blacklisting
The Low level has no filtering at all. A blacklist approach (like Medium level) tries to remove dangerous characters, but attackers just use characters that aren't blocked.
The Impossible level uses a whitelist. It defines exactly what valid input looks like (a properly formatted IP address) and rejects everything else. This is the correct security pattern because you can't bypass a check for "is this a valid IP" with shell metacharacters.
- Close the source code popup.
- Navigate back to DVWA Security in the left-hand menu.
- Change the security level back to Low and click Submit.
You've now exploited three of the most common vulnerability classes in web applications. You've seen how SQL injection leaks databases, how XSS steals sessions, and how command injection gives operating system access. Ready to level up? The Secret Mission challenges you to bypass a security filter using source code analysis.
Secret mission
Bypass the Medium Security Filter
The Medium security level adds a blacklist that strips certain characters from your input. Can you read the source code, identify what the developer forgot to block, and craft a payload that slips through?
Clean Up Your Resources
Clean Up Your Resources
This project runs entirely locally using Docker, so there are no ongoing cloud costs. Decide whether to keep your lab running, stop it for now, or remove everything entirely.
Resources you used:
- DVWA Docker container (named dvwa, running on port 4280).
- vulnerables/web-dvwa Docker image (cached locally).
- Burp Suite Community Edition (installed application).
- Docker Desktop (installed application).
Keep everything running
No action needed. Choose this if you want to keep practising with DVWA right now.
- Your DVWA container continues running at http://localhost:4280.
- Burp Suite stays open with your HTTP history intact.
- The container uses minimal resources while idle.
Pause - I'll come back to this later
Stop the running container to free up memory. The Docker image stays cached so you can restart quickly next time.
- Stop the DVWA container by running this command in your terminal:
docker stop dvwa
Because the container was started with the --rm flag, stopping it automatically removes the container. The image remains cached locally.
- To restart the lab later, run the same command from Step 1:
docker run --rm -d --name dvwa -p 4280:80 vulnerables/web-dvwa
- Close Burp Suite. Your HTTP history from this session will not be saved (Community Edition does not persist temporary projects).
Delete - I don't want to use this again
Remove all project resources completely. This frees up disk space used by the Docker image.
- Stop the DVWA container:
docker stop dvwa
- Remove the Docker image from your machine:
docker rmi vulnerables/web-dvwa
- Close Burp Suite.
- If you no longer need Burp Suite or Docker Desktop, uninstall them through your system settings.
Nice Work!
Nice Work!
You just completed your first structured web application penetration test. You went from spinning up a vulnerable target to extracting real credentials from its database.
Here's what you learned:
- Set up a local pentesting lab with DVWA running in Docker, and configured Burp Suite as an intercepting proxy to inspect and modify HTTP traffic.
- Exploited SQL Injection to extract usernames and MD5 password hashes from the database using UNION-based injection in Burp Repeater.
- Demonstrated Reflected XSS by injecting JavaScript that accessed session cookies, and achieved OS Command Injection to execute arbitrary system commands on the server.
- Bypassed a security filter by reading the application source code and identifying unblocked shell metacharacters.
Ready to quiz yourself?