Prompt Worms Часть 2: Я проверил на практике — 31 уязвимость в экосистеме AI-агента
Это продолжение статьи «Prompt Worms: Как агенты стали новыми переносчиками вирусов». В первой части мы разобрали теорию: Lethal Trifecta, Persistent Memory, цепочки заражения через Moltbook. OpenClaw был назван «идеальным носителем». В этой части я проверил, насколько «идеальным» он является на самом деле — залез в исходники, прощупал инфраструктуру, нашёл экосистемный SaaS в их маркетплейсе и обнаружил, что их собственная threat model покрывает лишь 70% реальной attack surface.
Предыстория: от теории к практике
В первой статье мы показали таблицу — почему OpenClaw идеальный носитель Prompt Worms:
Условие | Реализация в OpenClaw |
|---|---|
Data Access | Полный доступ к файловой системе, .env, SSH ключам |
Untrusted Content | Moltbook, email, Slack, Discord, веб-страницы |
External Comms | Email, API, shell commands, любые инструменты |
Persistent Memory | Встроенное долгосрочное хранилище контекста |
Красивая теория. Но вопрос остался: а насколько легко этим воспользоваться?
Я скачал репозиторий (~50 тысяч строк TypeScript), открыл код и за трое суток прошёл путь, который не планировал:
Код → Сайт → Маркетплейс → Экосистемный SaaS → Их threat model
Каждый слой вскрывал следующий. И каждый следующий был хуже предыдущего.
Слой 1: Исходный код. Ноль санитизации на 867 строк мозга
Единственный фильтр
В первой статье я написал: «расширения без модерации, вредоносный код выполняется». Теория. Теперь — доказательство.
Я трассировал путь сообщения от Telegram-чата до LLM. Четыре файла:
dispatch.ts → envelope.ts → agent-runner-execution.ts → run.ts
Ни на одном этапе — ни одной проверки содержимого. А вот единственная «санитизация» в 867 строках brain core:
// run.ts:67-75 — ВСЁ. const ANTHROPIC_MAGIC_STRING_TRIGGER_REFUSAL = "..."; function scrubAnthropicRefusalMagic(prompt: string): string { return prompt.replaceAll( ANTHROPIC_MAGIC_STRING_TRIGGER_REFUSAL, ANTHROPIC_MAGIC_STRING_REPLACEMENT, ); }
Одна замена одного стринга. Для одного провайдера. Весь остальной текст идёт в LLM как есть.
В первой статье мы описали Content Boundary Enforcement — разделение данных и инструкций через XML-теги. OpenClaw этого не делает. Данные и инструкции смешиваются в одном промпте. Prompt injection становится не атакой, а штатным режимом работы.
SOUL_EVIL — бэкдор, который не нужно внедрять
В коде есть файл soul-evil.ts (281 строка). Он по конфигурируемому триггеру подменяет «личность» агента — заменяет системный промпт на альтернативный из файла SOUL_EVIL.md. Задумано как «пасхалка». Реализовано как backdoor:
// soul-evil.ts — решение о подмене логируется на debug // (в проде выключено) log.debug(`[soul-evil] decision: ${reason}`);
Активация через конфигурацию: soulEvil.chance: 1 — агент всегда в «злом» режиме. Пользователь не видит разницы — тот же интерфейс, тот же чат.
Timeout = Approval
Помните из первой статьи: агент может выполнять shell-команды? Перед этим он «спрашивает разрешения». Но:
// exec-approval-manager.ts:51-61 const timer = setTimeout(() => { this.pending.delete(record.id); resolve(null); // ⚠️ timeout = null = approval }, timeoutMs);
Если пользователь не ответил — команда выполняется. А ещё: любой WebSocket-клиент может одобрить запрос любого другого клиента. Нет проверки identity. Мы написали PoC-скрипт в 50 строк:
// Слушаем exec.approval.requested → ответ: allow-always // Результат: One-Click RCE — все последующие команды авто-одобрены
config.patch — всемогущий API
Один эндпоинт позволяет менять любое поле конфигурации через JSON merge:
Что патчим | Эффект |
|---|---|
| Активируем SOUL_EVIL |
| Заменяем аутентификацию |
| Открываем все каналы |
| Загрузка malicious-плагина |
| Индексация произвольных путей |
Нет field-level ACL. Нет аудит-лога.
Итого по коду: 12 уязвимостей
# | Уязвимость | Severity |
|---|---|---|
1 | Zero-sanitization pipeline (DM→LLM) | 🔴 Critical |
2 | SOUL_EVIL backdoor engine | 🔴 Critical |
3 | Timeout-as-approval (RCE) | 🔴 Critical |
4 | Config injection → system takeover | 🔴 Critical |
5 | Plugin system: jiti JIT arbitrary exec | 🔴 Critical |
6 | Default-open command gating | 🟠 High |
7 | Plaintext credential storage | 🟠 High |
8 | Memory exfiltration + injection | 🟠 High |
9 | System prompt injection surfaces | 🟠 High |
10 | Hook system: silent agent mutation | 🟠 High |
11 | Fast-lane privilege escalation | 🟠 High |
12 | BOOT.md persistent backdoor | 🟠 High |
5 Critical, 7 High. Все с PoC. Цепочки от DM до Full Compromise — от 3 до 5 хопов.
Слой 2: Инфраструктура. CORS * и открытый API маркетплейса
После кода — логично посмотреть, что живёт в проде. Три домена:
openclaw.ai — CORS wildcard
Access-Control-Allow-Origin: * ← Любой домен может делать запросы Content-Security-Policy: ← ОТСУТСТВУЕТ X-Frame-Options: ← ОТСУТСТВУЕТ
Это статика на Vercel. Но CORS * + отсутствие X-Frame-Options = clickjacking.
docs.openclaw.ai — CSP с unsafe-eval
Документация на Mintlify отдаёт:
Content-Security-Policy: worker-src * blob: data: 'unsafe-eval' 'unsafe-inline'
unsafe-eval означает: если найти XSS-точку — eval(), Function(), setTimeout(string) работают. CSP не защищает.
А ещё — рекламируются файлы /llms.txt и /llms-full.txt: полный дамп документации (~1.3 MB), включая credential storage paths, config examples, архитектуру Gateway. Один GET-запрос — и у атакующего полная карта системы.
clawhub.ai — находка, которая изменила всё
ClawHub — маркетплейс skills для OpenClaw. API работает без аутентификации:
GET https://clawhub.ai/api/search?q=email
Возвращает полный каталог community skills с описаниями, оценками, версиями. No auth, no rate limiting.
И вот что вернулось в выдаче:
{ "slug": "sendclaw", "displayName": "SendClaw - Bot creates own email address & sends without human permission" }
«Бот создаёт email-адрес и отправляет без разрешения человека»?
Помните Lethal Trifecta из первой статьи? External Comms — третье условие. Если этот skill реально работает, мы только что нашли автономную email-рассылку в экосистеме OpenClaw. Это превращает теоретического Prompt Worm в практическую угрозу: заражённый агент может не просто отравить память соседних агентов, но и рассылать вредоносные письма настоящим людям.
Я пошёл проверять.
Слой 3: SendClaw. Массовая рассылка, которая не работает
5 минут — 3 аккаунта
SendClaw (5Ducks) — живой SaaS на sendclaw.com. Express.js backend на Replit. Я зарегистрировал три аккаунта за 5 минут:
Нет email-верификации
Нет CAPTCHA
Нет rate limiting
Sequential User ID: 99 → 100 → 101 (≈100 пользователей на всей платформе)
Утечка хешей паролей
Первый же запрос GET /api/user вернул:
{ "id": 99, "username": "sentinel-test", "password": "533652d523a3c4daf...b30e80", "email": "sentinel-test@proton.me", "isAdmin": false }
Хеш пароля утекает при регистрации, логине и запросе профиля. Все три аккаунта — подтверждено.
Обещанная рассылка
Я попробовал авторизоваться через Gmail OAuth:
GET /api/gmail/auth?userId=99 → Error 400: redirect_uri_mismatch
Gmail OAuth полностью сломан. Google отклоняет Client ID из-за неправильного redirect URI.
Эндпоинт POST /api/send-gmail — существует, но не работает. Эндпоинты для daily outreach batch — HTTP 500 на любой запрос.
Расхождение: что рекламируется vs что работает
Обещание (на ClawHub) | Реальность |
|---|---|
«Bot creates own email address» | Не создаёт — требует OAuth авторизацию вашего Gmail |
«Sends without human permission» | Не отправляет — OAuth сломан |
«Email outreach automation» | Единственный рабочий endpoint — AI-чат для стратегии |
«Daily outreach batches» |
|
Это ключевой момент всей истории.
В первой статье я описывал страшный сценарий: заражённый агент рассылает prompt worm по email. SendClaw рекламируется на ClawHub именно как инструмент для этого — «отправляет без разрешения человека». Если бы этот skill работал, он был бы готовым delivery mechanism для Prompt Worms.
Но он не работает. Баг в OAuth конфигурации спас пользователей от самих себя.
Баг, который спас от уязвимости
OAuth state parameter содержит raw userId без подписи:
/api/gmail/auth?userId=99 → state=99 /api/gmail/auth?userId=1 → state=1
Если бы OAuth работал, это был бы прямой OAuth CSRF: подменить state в callback → привязать свой Gmail к чужому аккаунту → рассылать от чужого имени. Цепочка:
Attacker → /api/gmail/auth?userId=VICTIM → Google OAuth (state=VICTIM) → Callback с кодом авторизации → Gmail token привязан к VICTIM → POST /api/send-gmail (от имени VICTIM)
Сломанный OAuth → цепочка не замыкается. Баг ≠ защита. Если завтра кто-то поправит redirect URI — CSRF-цепочка сразу заработает.
12 уязвимостей в SendClaw
# | Находка | Severity |
|---|---|---|
1 | Password hash leakage | 🔴 Critical |
2 | Unrestricted registration | 🔴 Critical |
3 | Sequential predictable IDs | 🔴 Critical |
4 | OAuth CSRF via raw userId | 🔴 Critical |
5 | Broken Gmail OAuth (core non-functional) | 🔴 Critical |
6 | Admin panel exposure | 🟠 High |
7 | Batch endpoint DoS (500 crash) | 🟠 High |
8 | Account lockout anomaly | 🟠 High |
9 | Dual auth inconsistency | 🟠 High |
10 | Multi-tenant exposure | 🟡 Medium |
11 | Infrastructure fingerprinting | 🟡 Medium |
12 | Partial mass assignment block | 🟡 Medium |
Слой 4: Threat Model. 37 угроз и 14 слепых зон
Уже после всех находок я обнаружил, что OpenClaw опубликовал официальную threat model. 37 угроз × 8 MITRE ATLAS тактик × 6 attack chains × 5 trust boundaries.
Это первая публичная threat model для AI-агентной платформы. И она честная — они признают:
— «Pattern detection only, no blocking»
— «Skills execute with full agent privileges»
— «Tokens stored in plaintext, don't expire»
— «No rate limiting»
Что покрыто в их модели
Их фокус — Skill Supply Chain (ClawHub) и Prompt Injection. Это правильные приоритеты. Они документируют 6 attack chains, включая «Malicious Skill Full Kill Chain» и «Prompt Injection to RCE».
Что НЕ покрыто
Всё, что мы нашли на protocol level:
Наша находка | В их Threat Model? |
|---|---|
| ❌ |
| ❌ |
Credentials в cleartext WebSocket frame | ❌ |
Nonce без HMAC — replay attack | ❌ |
| ❌ |
| ❌ |
Timeout-as-approval | ❌ |
Loopback auth bypass | ❌ |
Memory flush autonomous writes | ❌ |
Plugin loader (jiti) arbitrary exec | ❌ |
SendClaw: сломанный OAuth + hash leak | ❌ |
ClawHub: unauthenticated API | ❌ |
CORS | ❌ |
CSP | ❌ |
14 из 14 наших критических находок — не отражены в официальной модели.
Модель покрывает ~70% реальной attack surface. Оставшиеся 30% — это как раз protocol-level и infrastructure-level уязвимости, которые делают описанные в модели атаки тривиально эксплуатируемыми.
Как это связано с Prompt Worms
Вернёмся к первой статье. Мы описали 4 условия для Prompt Worm:
Условие | Статус после аудита |
|---|---|
✅ Data Access | Подтверждено: |
✅ Untrusted Content | Подтверждено: zero-sanitization pipeline, контент из любых каналов |
✅ External Comms | Подтверждено: shell-commands через timeout-approval, email через skills |
✅ Persistent Memory | Подтверждено: |
Все 4 условия не просто «выполнены» — они не имеют ни одной защиты. В первой статье мы предложили защиту через Content Boundary Enforcement и Memory Sanitization. OpenClaw не реализовал ни одну из них.
Prompt Worm Kill Chain — полная цепочка
В первой статье мы описали теоретическую цепочку через Moltbook. Вот как она работает с реальным кодом:
1. Attacker публикует malicious skill на ClawHub (API без auth → без модерации → skill одобрен) 2. Жертва устанавливает skill (SKILL.md содержит prompt injection) 3. Агент выполняет инструкции из SKILL.md (zero-sanitization → raw в LLM) 4. LLM вызывает /bash через tool call (exec-approval: timeout → approval) 5. Команда записывает payload в memory/ (memory-flush: автономная запись на диск) 6. Следующие сессии загружают payload из памяти (BOOT.md / memory-search → persistent infection) 7. Агент рассылает payload через каналы (Telegram/Discord/email → cross-agent propagation) 8. Prompt Worm реплицируется
Каждый хоп подтверждён кодом. От DM до полной репликации — 8 шагов, 0 проверок.
Сухие цифры
Метрика | Значение |
|---|---|
Уязвимостей в коде OpenClaw | 12 (5 Critical, 7 High) |
Уязвимостей на сайтах | 7 (2 Critical, 3 High, 2 Medium) |
Уязвимостей в SendClaw | 12 (5 Critical, 4 High, 3 Medium) |
Всего unique findings | 31 |
Kill chains (PoC-ready) | 14 |
LOC трассировано | ~4,500 |
Gaps в официальной threat model | 14 |
Дней на исследование | 3 |
Выводы
1. Теория работает
В первой статье мы описали Lethal Trifecta и Persistent Memory как условия для Prompt Worms. Аудит подтвердил: OpenClaw удовлетворяет все 4 условия без единой защиты.
2. Маркетплейсы — Supply Chain Attack ждёт своего часа
ClawHub API без аутентификации. Описания skills без модерации. SendClaw рекламирует «рассылку без разрешения», хотя OAuth сломан. Если завтра появится skill, который реально работает и содержит prompt injection в SKILL.md — все установившие его агенты скомпрометированы.
3. Threat Models необходимы, но недостаточны
OpenClaw — единственная AI-агентная платформа с публичной threat model на MITRE ATLAS. Это заслуживает уважения. Но модель описательная (без PoC, без code references) и покрывает ~70% attack surface.
4. Баги ≠ Защита
Сломанный OAuth в SendClaw случайно блокирует OAuth CSRF и автономную email-рассылку. Если кто-то починит redirect URI — обе уязвимости мгновенно станут эксплуатируемыми.
5. Один regex — не security layer
function scrubAnthropicRefusalMagic(prompt) { ... }
Это единственная строка защиты между DM из Telegram и shell-командой на вашем сервере. В SENTINEL у нас 187 detection engines. Потому что один regex — это не security layer. Это комментарий в коде.
Ответственное раскрытие
OpenClaw: Контакт
security@openclaw.ai. Findings задокументированы.SendClaw (5Ducks): Публичный security контакт отсутствует (что само по себе — красный флаг для SaaS обрабатывающего email credentials).
Ссылки
Часть 1: Prompt Worms — теоретическая база
OpenClaw Trust / Threat Model — официальная модель
Morris-II: Self-Replicating Prompts — Cornell Tech, 2024
CrowdStrike: OpenClaw Security — Feb 2026
SENTINEL AI Security — Open Source
MITRE ATLAS — Adversarial Threat Landscape for AI Systems
Автор: @DmitrL-dev
Telegram: https://t.me/DmLabincev
