Skip to content

Back

cursed_stale_policy

Required Knowledge

Info

Familiarity with Content Security Policy (CSP) concepts. OWASP - Testing for Content Security Policy

Content Security Policy (CSP) — the practical mental model

CSP is a browser-enforced ruleset sent by the server that restricts what a page is allowed to load/execute (scripts, styles, images, frames, network requests). It’s mainly used to reduce the impact of injection bugs like XSS.

CSP is “allowlist rules,” not a bug fix

  • CSP doesn’t remove vulnerabilities; it limits what an attacker’s injected content can do (defense in depth).

The two pieces you read every time

Directives (what type of thing is being controlled)

Examples:

  • default-src (fallback policy)
  • script-src (JavaScript execution rules)
  • style-src, img-src, connect-src, frame-src, object-src, etc.

Source expressions (where it’s allowed to come from)

Common ones:

  • 'self' = same origin
  • https://cdn.example.com = specific origins
  • data: / blob: = special schemes (often risky if used loosely)
  • 'unsafe-inline' = allows inline scripts/handlers (usually weak)
  • 'nonce-…' = allow inline scripts only if they include the right nonce
  • 'sha256-…' = allow inline scripts only if the content matches a hash

“Strict CSP”: nonces, hashes, and strict-dynamic

Modern “strict CSP” usually means:

'strict-dynamic' is a common add-on in strict policies that changes how trust is propagated from nonce/hash-trusted scripts.

Important gotcha (very challenge-relevant):

Enforce vs Report-Only (great for debugging)

  • Content-Security-Policy = enforced (blocks violations)
  • Content-Security-Policy-Report-Only = doesn’t block; it only reports what would’ve been blocked

Reporting (how defenders see violations)

Learn to “read” a CSP in 30 seconds (HTB-friendly)

  • Find the CSP in DevTools → Network → the document request → Response Headers
  • Identify the script gate:

    • Is there a script-src? If not, does default-src apply?
    • Does it include 'unsafe-inline'?
    • Does it include a 'nonce-…' or 'sha256-…'?
    • Is there 'strict-dynamic'?
  • When something fails, check the Console: browsers usually log CSP violations and name the directive that blocked it

Tiny examples (recognize patterns fast)

Weak-ish (inline allowed):

Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.example.com 'unsafe-inline';

Stricter (nonce-based “strict CSP” style):

Content-Security-Policy: default-src 'self'; script-src 'nonce-r4nd0m' 'strict-dynamic'; object-src 'none'; base-uri 'none';

If you paste the exact CSP header from your HTB challenge, I’ll translate it into “what’s allowed / what’s blocked / what misconfigs to look for” without handing you a straight solution. (¬‿¬)

Solution

Here’s what that “solution” is really saying, step by step, in CSP terms — and why it works.

What the CSP nonce is supposed to do

  • A CSP nonce is meant to be a one-time token that the server generates fresh per response.
  • The policy says (conceptually): “Inline scripts are forbidden… except scripts that have this nonce.”
  • So the browser only runs inline <script> tags that match the nonce in the script-src directive.

What went wrong: the nonce is static

  • the nonce in script-src 'nonce-…' does not change between page loads.
  • That breaks the security model: if the attacker can learn the nonce once (by loading the page), they can reuse it forever.

Why this matters:

  • The nonce is basically acting like a password that's printed on every page load.
  • If it never rotates, it’s not a one-time token; it’s just a constant allow-key.

How that enables script execution

If you can inject HTML (or otherwise get your content placed into the page), you can write:

  • <script nonce="STATIC_NONCE"> … </script>

Because the nonce matches the CSP header, the browser treats your injected script as “approved” and executes it even though inline scripts would normally be blocked.

So the bypass is:

  • CSP tries to stop inline JS
  • Nonce is intended to permit only trusted inline JS
  • Static nonce lets anyone mint “trusted” scripts

Why the trigger XSS button matters

Challenges like this often include a bot (admin viewer) and a workflow like:

  • You submit some content (comment/message/name/etc.)
  • “Trigger XSS” makes the bot visit/render that content

So clicking “Trigger XSS” is basically:

  • “Make the privileged bot load the page where my injected <script nonce=...> exists”

If the injection is stored or reflected in a place the bot visits, the bot’s browser executes the script.

What /callback is doing in that description

That line means:

  • The injected script is written to send a request to an endpoint like /callback.
  • This is usually used as a “proof” channel:
    • Either the bot hits /callback with some data
    • Or /callback returns something sensitive when called by the bot (because the bot has the right cookies/session)

So the bot processes our script, makes a request to /callback implies the payload caused a fetch/navigation/image load/etc. that reaches /callback under the bot’s context.

How that leads to the flag page

Patterns:

  • /callback returns content only visible to an admin/bot session, so when the bot requests it, the response includes the flag.
  • Or /callback is part of a chain that causes the bot to load an internal/admin-only page and then reveal it somewhere the attacker can observe.

The xss payload used (from frontend/src/modules/violationHandler.js)

if (xssInputElement) {
        xssInputElement.value = [
            "<script>",
            "   fetch('/callback', {",
            "       method: 'POST',",
            "       headers: { 'Content-Type': 'application/json' },",
            "       body: JSON.stringify({ cookies: document.cookie })",
            "   });",
            "</script>"
        ].join("\n");

        window.codeMirrorEditors.xssEditor = initializeCodeMirrorEditor(xssInputElement, {
            mode: 'javascript',
        });
    }

Reality check

Static/weak nonces + nonce-based CSP bypass chains

“CSP exists but is applied inconsistently” (coverage gaps)

“CSP allowlists can be a trap” (trusted domains become the weakness)

CSP reporting can leak info (report endpoints as a side-channel)

Older-but-useful CSP bypass thinking

Research writeups that feel like “bug bounty meta” (great for HTB CSP puzzles)