
Marimo(一个用于数据科学和 AI/ML 的开源 Python 笔记本)中存在一个未认证远程代码执行漏洞。终端 WebSocket 端点(/terminal/ws)完全跳过了身份验证检查,而相邻的笔记本端点(/ws)则正确执行了该检查。未认证的攻击者可以连接到 /terminal/ws,在零凭据的情况下获得主机系统上的完整交互式 PTY shell。
该漏洞在披露后 10 小时内即被在野利用。攻击者在不到 3 分钟内窃取了 AWS 凭据。
影响 Marimo <= 0.20.4。已在 Marimo 0.23.0 中修复。
| 字段 | 详情 |
|---|---|
| CVE ID | CVE-2026-39987 |
| 厂商 | Marimo Project |
| 产品 | Marimo(Python 笔记本) |
| 受影响版本 | <= 0.20.4 |
| CVSS v3.1 | 9.3(严重) |
| CWE | CWE-306 — 关键功能缺少身份验证 |
| 攻击向量 | 网络 |
| 身份验证 | 无需认证 |
| 用户交互 | 无 |
| 利用成熟度 | 已在野外被积极利用 |
| 利用时间 | 披露后约 10 小时 |
| 修复版本 | Marimo 0.23.0 |
Marimo 是一个开源的反应式 Python 笔记本,旨在作为 Jupyter 的现代替代品。它专为数据科学、AI/ML 实验和交互式数据分析而构建。其关键特性包括自动依赖跟踪、可复现执行,以及相比传统笔记本更简洁的开发者体验。
Marimo 正在 Python 和 AI/ML 社区中迅速获得关注,尤其是那些希望获得比 Jupyter 更结构化笔记本工作流的从业者。
与所有笔记本环境一样,Marimo 实例通常可以访问敏感资源:云凭据(AWS、GCP、Azure)、数据库连接字符串、AI 服务的 API 密钥(OpenAI、Anthropic 等)以及内部网络访问权限。与传统 Web 应用不同,笔记本设计用于执行任意代码。这正是它们的核心用途。
这种组合使得笔记本环境中任何身份验证绕过都极具破坏性。``` Typical Marimo Deployment:
┌──────────────┐ ┌────────────────────────────────┐ │ │ HTTP │ Marimo Server │ │ Browser │────────>│ │ │ (User) │ │ ┌──────────────────────────┐ │ │ │<────────│ │ /ws (Notebook) │ │ └──────────────┘ WS │ │ ✅ validate_auth() │ │ │ └──────────────────────────┘ │ │ │ │ ┌──────────────────────────┐ │ │ │ /terminal/ws │ │ │ │ ❌ NO AUTH CHECK │ │ │ └──────────────────────────┘ │ │ │ │ ┌──────────────────────────┐ │ │ │ Python Environment │ │ │ │ .env files │ │ │ │ AWS credentials │ │ │ │ API keys │ │ │ └──────────────────────────┘ │ └────────────────────────────────┘
---
## 漏洞深度剖析
### 两个 WebSocket 端点
Marimo 的服务器为不同功能实现了多个 WebSocket 端点。两个主要端点之间的关键区别在于是否存在身份验证检查:```
Authentication Flow Comparison:
/ws (Notebook WebSocket):
┌─────────┐ ┌───────────────┐ ┌──────────┐ ┌───────────┐
│ Connect │───>│ validate_auth │───>│ Accept │───>│ Notebook │
└─────────┘ └───────┬───────┘ └──────────┘ └───────────┘
│
❌ Reject if
not authenticated
/terminal/ws (Terminal WebSocket):
┌─────────┐ ┌───────────────┐ ┌──────────┐ ┌───────────┐
│ Connect │───>│ Check mode & │───>│ Accept │───>│ PTY Shell │
└─────────┘ │ platform only │ └──────────┘ └───────────┘
└───────────────┘
⚠️ No auth check!
Anyone gets a shell!
笔记本端点(/ws)在接收 WebSocket 连接之前会正确调用 validate_auth() 来验证用户身份。这是预期的安全行为。
终端端点(/terminal/ws)仅检查服务器是否处于运行模式以及平台是否支持终端功能。它从不调用 validate_auth()。在通过这些基本检查后,它会接受连接并创建一个完整的 PTY(伪终端)会话。```python
async def websocket_connect(self, message): await self.validate_auth() # ✅ Checks authentication await self.accept() # ... notebook communication
async def websocket_connect(self, message): if not self.is_running_mode(): # Only checks mode await self.close() return if not self.is_platform_supported(): # Only checks platform await self.close() return await self.accept() # ❌ No auth! Anyone gets a shell # ... PTY shell creation
这是 **CWE-306:关键功能缺少身份验证**。服务器上最危险的端点(提供交互式 shell 的端点)完全没有身份验证。
### 攻击:3 分钟内获取 AWS 密钥
利用时间线展示了现代威胁行为者的行动速度有多快:```
┌──────────────────────────────────────────────────────────────┐
│ CVE-2026-39987 Timeline │
├──────────────────────────────────────────────────────────────┤
│ │
│ T+0h Advisory published │
│ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ │
│ T+9h First exploit built from advisory │
│ T+10h Exploitation in the wild confirmed │
│ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ │
│ T+10h 0m Attacker connects to /terminal/ws │
│ T+10h 1m Full PTY shell obtained │
│ T+10h 2m .env file located and read │
│ T+10h 3m AWS keys exfiltrated │
│ Total attack time: ~3 minutes │
└──────────────────────────────────────────────────────────────┘
攻击本身简单得令人发指:``` Step 1: Attacker opens WebSocket connection to /terminal/ws (No authentication needed, no special tools required)
Step 2: Server creates a PTY (pseudo-terminal) session Attacker now has an interactive shell
Step 3: Attacker runs commands: $ cat .env AWS_ACCESS_KEY_ID=AKIA... AWS_SECRET_ACCESS_KEY=... DATABASE_URL=postgres://... OPENAI_API_KEY=sk-...
Step 4: Credentials exfiltrated Attacker now has cloud access, database access, and API keys for AI services
Total time: under 3 minutes Authentication required: none Tools required: any WebSocket client
无需开发漏洞利用。无需 shellcode。无需内存破坏。只需一个 WebSocket 客户端和一个缺失的认证检查。
---
## 影响分析
**对 Marimo 主机的直接影响:**
- 以 Marimo 进程的权限获得完整交互式 shell
- 可访问该进程可读取的所有文件(源代码、数据、凭据)
- 可访问包含 API 密钥和机密信息的环境变量
- 能够在主机系统上执行任意命令
**凭据泄露(主要攻击目标):**
- 来自 `.env` 文件或环境变量的 AWS 访问密钥和秘密密钥
- GCP/Azure 服务账户凭据
- 包含密码的数据库连接字符串
- OpenAI、Anthropic 及其他 AI 服务的 API 密钥
- SSH 密钥及其他认证材料
**下游影响(通过窃取的凭据):**
- 未经授权访问云基础设施(EC2、S3、Lambda 等)
- 从云存储和数据库窃取数据
- 资源滥用(加密货币挖矿、AI API 额度盗用)
- 横向移动进入云网络和本地网络
**风险放大因素:**
- 笔记本环境的设计目的就是执行任意代码(这正是其用途)
- 数据科学环境通常为训练任务配置了广泛的云访问权限
- 许多 Marimo 实例为协作和远程工作而暴露在互联网上
- 在研究/实验环境中,安全加固往往被事后才考虑
---
## 受影响版本
| 版本 | 状态 |
|---------|--------|
| Marimo 0.23.0+ | **已修复** |
| Marimo 0.20.5 至 0.22.x | **可能受影响**(介于公告范围与修复版本之间) |
| Marimo <= 0.20.4 | **受影响**(已确认范围) |
---
## 更宏观的视角:AI/ML 工具链遭受攻击
CVE-2026-39987 并非孤立事件。它是 2026 年 4 月出现的明显趋势的一部分:
| CVE | 产品 | 类型 | 状态 |
|:---|:---|:---|:---|
| **CVE-2026-39987** | Marimo | 预认证 RCE(WebSocket) | 10 小时内被利用 |
| **CVE-2026-33017** | Langflow | RCE | CISA KEV(3 月 26 日) |
| **CVE-2026-5059** | aws-mcp-server | 命令注入 RCE | 公开公告 |
| **TorchGeo** | TorchGeo | eval() RCE | 公开公告 |