
Набор инструментов для обнаружения и эксплуатации CVE-2026-39987 — предварительно аутентифицированного RCE в блокнотах Marimo. Включает сканер на Python и NSE-скрипт Nmap для выявления уязвимых экземпляров через проверку WebSocket-конечных точек.
Уязвимость предварительно аутентифицированного удалённого выполнения кода в Marimo — открытом Python-ноутбуке для науки о данных и AI/ML. WebSocket-конечная точка терминала (/terminal/ws) полностью пропускает проверку аутентификации, в то время как соседняя конечная точка ноутбука (/ws) корректно её применяет. Неаутентифицированный злоумышленник может подключиться к /terminal/ws и получить полноценную интерактивную PTY-оболочку на хост-системе без каких-либо учётных данных.
Эксплуатировалась в дикой природе в течение 10 часов после раскрытия. Злоумышленники похитили учётные данные AWS менее чем за 3 минуты.
Затрагивает Marimo <= 0.20.4. Исправлено в Marimo 0.23.0.
| Поле | Детали |
|---|---|
| ID CVE | 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), строкам подключения к базам данных, ключам API для AI-сервисов (OpenAI, Anthropic и т. д.) и внутренним сетям. В отличие от традиционных веб-приложений, ноутбуки предназначены для выполнения произвольного кода. Это их основная цель.
Такое сочетание делает любой обход аутентификации в среде ноутбуков особенно разрушительным.``` 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) корректно вызывает validate_auth() для проверки личности пользователя перед принятием WebSocket-соединений. Это ожидаемое поведение безопасности.
Конечная точка терминала (/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: Отсутствие аутентификации для критической функции**. Самый опасный endpoint на сервере (тот, который предоставляет интерактивную оболочку) вообще не имеет аутентификации.
### Атака: от консультации до ключей AWS за 3 минуты