GODSON'S BLOG
← Back to Writeups

March 2023 - Intigriti's XSS Challenge - Author Writeup

Due to time constraints I never delivered a full write-up for my own March 2023 Intigriti challenge, so here are links to the community write-ups plus a short summary of the intended path-traversal and cache-based CSP bypass.

💡 Challenge URL: https://challenge-0323.intigriti.io/

Due to time constraints, I was not able to deliver the write-up for my challenge when it ended, but plenty of people decided to share how they solved it. So, I highly recommend checking their write-ups for a more detailed walkthrough:

Summary

Goal: Steal the admin’s flag (stored in a note) despite strict CSP and sanitization.

Recon & Source-Code Review

  • Notes are created via the /note/:id?id=<id> endpoint; content is sanitized with DOMPurify.
  • A special /debug/<author-ID>/<note-ID> endpoint returns raw note content as text/html, but only when the custom header mode: read is set — otherwise it returns 404.

Client-Side Path Traversal to Reach the Debug Endpoint

  • The frontend fetches /note/<id> (with id coming from URL parameters) and always includes the mode: read header.
  • Using directory traversal in the id parameter (e.g. ?id=../debug/<author-ID>/<note-ID>) forces the browser to request the debug path with the correct header, even though the UI makes that impossible normally.

CSP Bypass via 404-Based Script Injection

  • A strict CSP blocks inline scripts and untrusted external sources.
  • But the custom 404 page reflects paths unsanitized, with the ability to inject <script src=...> tags into the error message via path-encoding tricks — inserting a crafted path ending in /...;alert(...) causes script execution despite the CSP.

Exploitation via Browser Cache (bfcache / disk cache)

(inspired by ArkArk’s writeup)

  • The debug response is fetched as text/html, which can be stored in the browser’s disk cache.
  • Navigating (e.g. via window.open) to the path-traversal note endpoint, then calling history.back(), loads the previously fetched debug response without sanitization while the execution context persists — triggering the injected script.

Final Attack Chain

  1. Submit a note with the payload, or trigger stored content indirectly via path traversal.
  2. Trigger a fetch to /debug/... through the traversal path (with forward history), storing the raw HTML response in cache.
  3. Navigate back to the cached view, where the CSP-bypass path injection executes JS — revealing the flag.
On This Page