
Набор инструментов для обнаружения и эксплуатации CVE-2026-39987 — предварительно аутентифицированного RCE в блокнотах Marimo. Включает сканер на Python и NSE-скрипт Nmap для выявления уязвимых экземпляров через проверку WebSocket-конечных точек.
Уязвимость предварительно аутентифицированного удалённого выполнения кода в Marimo — открытом Python-ноутбуке для науки о данных и AI/ML. WebSocket-конечная точка терминала (/terminal/ws) полностью пропускает проверку аутентификации, в то время как соседняя конечная точка ноутбука (/ws) корректно её применяет. Неаутентифицированный злоумышленник может подключиться к /terminal/ws и получить на хост-системе без каких-либо учётных данных.
Эксплуатировалась в дикой природе в течение 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 минуты
Временная шкала эксплуатации показывает, насколько быстро действуют современные угрозы:```
┌──────────────────────────────────────────────────────────────┐
│ 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
Разработка эксплойта не требуется. Никакого шеллкода. Никакого повреждения памяти. Просто WebSocket-клиент и отсутствующая проверка аутентификации.
---
## Анализ воздействия
**Непосредственное воздействие на хост Marimo:**
- Полный интерактивный shell с привилегиями процесса Marimo
- Доступ ко всем файлам, читаемым процессом (исходный код, данные, учётные данные)
- Доступ к переменным окружения, содержащим API-ключи и секреты
- Возможность выполнять произвольные команды на хост-системе
**Раскрытие учётных данных (основная цель атаки):**
- AWS access keys и secret keys из файлов `.env` или переменных окружения
- Учётные данные сервисных аккаунтов GCP/Azure
- Строки подключения к базам данных с паролями
- API-ключи OpenAI, Anthropic и других AI-сервисов
- SSH-ключи и другие материалы аутентификации
**Каскадное воздействие (через похищенные учётные данные):**
- Несанкционированный доступ к облачной инфраструктуре (EC2, S3, Lambda и т. д.)
- Экфильтрация данных из облачных хранилищ и баз данных
- Злоупотребление ресурсами (криптомайнинг, кража кредитов AI API)
- Горизонтальное перемещение в облачные и локальные сети
**Факторы усиления риска:**
- Среды notebook предназначены для выполнения произвольного кода (в этом их назначение)
- Среды data science обычно имеют широкий облачный доступ для задач обучения
- Многие экземпляры Marimo открыты в интернете для совместной работы и удалённой работы
- Усиление безопасности часто является второстепенной задачей в исследовательских/экспериментальных средах
---
## Затронутые версии
| Версия | Статус |
|---------|--------|
| Marimo 0.23.0+ | **Исправлено** |
| Marimo 0.20.5 — 0.22.x | **Вероятно, уязвимо** (между диапазоном из бюллетеня и исправлением) |
| Marimo <= 0.20.4 | **Уязвимо** (подтверждённый диапазон) |
---
## Более широкая картина: атака на инструментарий AI/ML
CVE-2026-39987 — не единичный инцидент. Это часть чёткой закономерности, возникшей в апреле 2026 года:
| CVE | Продукт | Тип | Статус |
|:---|:---|:---|:---|
| **CVE-2026-39987** | Marimo | Pre-Auth RCE (WebSocket) | Эксплуатируется за 10 часов |
| **CVE-2026-33017** | Langflow | RCE | CISA KEV (26 марта) |
| **CVE-2026-5059** | aws-mcp-server | Command Injection RCE | Публичный бюллетень |
| **TorchGeo** | TorchGeo | eval() RCE | Публичный бюллетень |
Четыре инструмента разработки AI/ML поражены критическими уязвимостями RCE за один месяц. Конвейер разработки AI/ML становится новым shadow IT: инструменты развёртываются с широким доступом, минимальным контролем безопасности и богатыми хранилищами учётных данных.```
┌─────────────────────────────────────────────────┐
│ Why AI/ML Tools Are Prime Targets │
├─────────────────────────────────────────────────┤
│ │
│ 1. DESIGNED to execute arbitrary code │
│ (that's literally what notebooks do) │
│ │
│ 2. Run with elevated privileges │
│ (GPU access, cloud SDKs, network access) │
│ │
│ 3. Contain high-value credentials │
│ (AWS keys, API tokens, DB connections) │
│ │
│ 4. Often exposed to the network │
│ (for collaboration and remote access) │
│ │
│ 5. Security hardening is an afterthought │
│ (focus on features and UX, not security) │
│ │
│ 6. Users are researchers, not security experts │
│ (default configs, weak passwords, no VPN) │
└─────────────────────────────────────────────────┘
Python-скрипт обнаруживает уязвимые экземпляры Marimo с помощью многоэтапного анализа.
Как это работает:
/api/status, /api/health и / для поиска признаков Marimo в содержимом ответов и заголовках/terminal/ws (данные через соединение не передаются)/terminal/ws (должен требовать аутентификацию) с /ws (известно, что требует аутентификацию) для подтверждения несоответствияНа целевых системах не выполняется никаких команд. Рукопожатие WebSocket тестируется, но данные через соединение не отправляются. Проверка полностью пассивна и безопасна для продакшена.
Использование:```bash
pip install -r requirements.txt
python CVE-2026-39987_Marimo_RCE_detector.py -t http://marimo-host:2718
python CVE-2026-39987_Marimo_RCE_detector.py -t https://marimo-host:443
python CVE-2026-39987_Marimo_RCE_detector.py -f targets.txt -o results.json -v
python CVE-2026-39987_Marimo_RCE_detector.py -t http://10.0.0.5:2718 --timeout 15
**Параметры:**
| Флаг | Описание | По умолчанию |
|------|-------------|---------|
| `-t`, `--target` | Один целевой URL (например, `http://host:2718`) | — |
| `-f`, `--file` | Файл с целевыми URL, по одному в строке (поддерживаются комментарии `#`) | — |
| `-o`, `--output` | Сохранить результаты в JSON-файл | — |
| `--timeout` | Таймаут подключения в секундах | `10` |
| `--verify-ssl` | Включить проверку SSL-сертификата | Отключено |
| `-v`, `--verbose` | Подробный вывод с полными деталями | Выкл |
**Пример вывода:**```
[*] CVE-2026-39987 Marimo Pre-Auth RCE Scanner
[*] Scanning 1 target(s)...
[*] NOTE: This scanner only checks for endpoint exposure.
[*] No commands are executed on target systems.
======================================================================
Target: http://10.0.0.5:2718
Scan Time: 2026-04-14T16:00:00Z
Risk Level: CRITICAL
======================================================================
Is Marimo: YES
Marimo Version: 0.19.2
/terminal/ws Open: YES — UNAUTHENTICATED
Vulnerable: YES
*** CRITICAL: Pre-authenticated RCE is possible! ***
*** An attacker can get a full PTY shell without any auth ***
Details:
- Marimo instance detected via /api/status
- Marimo version: 0.19.2
- WebSocket upgrade accepted — /terminal/ws accessible WITHOUT auth
- CONFIRMED: /terminal/ws accepts unauthenticated connections while
/ws requires auth — classic CVE-2026-39987 signature
- Version 0.19.2 <= 0.20.4 — VULNERABLE to pre-auth RCE
======================================================================
[*] Scan Complete: 1 targets scanned
[*] Marimo Instances: 1 | Vulnerable: 1 | Critical: 1
======================================================================
sudo cp CVE-2026-39987_Marimo_RCE.nse /usr/share/nmap/scripts/ sudo nmap --script-updatedb
nmap -p 2718 --script CVE-2026-39987_Marimo_RCE
nmap -p 2718,8080,8443,443 --script CVE-2026-39987_Marimo_RCE
nmap -p 2718 --script CVE-2026-39987_Marimo_RCE 10.0.0.0/24
nmap -p 2718 --script CVE-2026-39987_Marimo_RCE -iL targets.txt
nmap -sV -p 2718 --script CVE-2026-39987_Marimo_RCE
**Пример вывода Nmap:**```
PORT STATE SERVICE
2718/tcp open http
| CVE-2026-39987_Marimo_RCE:
| VULNERABLE:
| Marimo Pre-Auth RCE (CVE-2026-39987)
| State: VULNERABLE
| Risk level: CRITICAL
| Marimo Version: 0.19.2
| /terminal/ws: accessible without authentication
| Description:
| The Marimo /terminal/ws WebSocket endpoint accepts connections
| without authentication, enabling pre-authenticated RCE.
| An attacker can obtain a full PTY shell without any credentials.
| References:
|_ https://nvd.nist.gov/vuln/detail/CVE-2026-39987
Если у вас есть доступ к экземпляру Marimo:```bash
curl -s http://:2718/api/status | python3 -m json.tool
curl -s -o /dev/null -w "%{http_code}"
-H "Upgrade: websocket"
-H "Connection: Upgrade"
-H "Sec-WebSocket-Key: dGVzdC1rZXktMTIzNDU2Nzg="
-H "Sec-WebSocket-Version: 13"
http://:2718/terminal/ws
curl -s -o /dev/null -w "%{http_code}"
-H "Upgrade: websocket"
-H "Connection: Upgrade"
-H "Sec-WebSocket-Key: dGVzdC1rZXktMTIzNDU2Nzg="
-H "Sec-WebSocket-Version: 13"
http://:2718/ws
Если `/terminal/ws` возвращает 101, а `/ws` — 401/403, это классическая сигнатура CVE-2026-39987.
---
## Индикаторы компрометации
Следите за этими признаками в вашем окружении:
| Индикатор | Где проверять | На что обращать внимание |
|:---|:---|:---|
| Несанкционированные WebSocket-соединения | Журналы сервера/прокси | Соединения с `/terminal/ws` с неожиданных IP-адресов |
| Создание PTY-сессий | Мониторинг процессов | Неожиданные shell-процессы, запущенные сервером Marimo |
| Доступ к файлам | Журналы аудита файлов | Чтение `.env`, файлов с учётными данными или SSH-ключей |
| Использование учётных данных | Журналы аудита облачного провайдера | API-вызовы с использованием ключей, хранившихся в окружении Marimo |
| Исходящая передача данных | Мониторинг сети | Необычный исходящий трафик с хоста Marimo |
**Команды для расследования:**```bash
# Check for active WebSocket connections
ss -tnp | grep <MARIMO_PORT>
# Review process tree for unexpected shells
ps aux --forest | grep -A5 marimo
# Check if .env or credential files were recently accessed
stat .env
stat ~/.aws/credentials
# Review cloud provider activity logs for unauthorized access
aws cloudtrail lookup-events --lookup-attributes AttributeKey=AccessKeyId,AttributeValue=<KEY_ID>
# Check for unauthorized outbound connections
netstat -tnp | grep ESTABLISHED | grep -v 127.0.0.1
Немедленные действия (выполните сейчас):
pip install --upgrade marimo)Краткосрочные меры (в течение недели):
.env и переменные окружения на предмет конфиденциальных данных, которые могли быть раскрытыДолгосрочные меры:
Керем Оруч — инженер по кибербезопасности