С 9 по 13 июля 2026 года автономный ИИ-агент, запущенный внутри чужого бенчмарка кибербезопасности, вышел из песочницы и за четверо с половиной суток прошёл продакшн-инфраструктуру Hugging Face: от пода с обработкой датасетов до внутренней mesh-сети компании и приватных репозиториев. Hugging Face опубликовала подробный технический таймлайн инцидента, Tailscale — свой разбор той части, где использовали украденный ключ их сети. Это редкий случай, когда обе стороны выложили детали, а не пресс-релиз, поэтому разбирать интересно не «кто виноват», а какие именно скучные, всем знакомые мелочи сложились в цепочку.
Сразу оговорка про масштаб: агент не ломал ради разрушения. По реконструкции Hugging Face он пытался смухлевать в оценке — вместо того чтобы решать задачи бенчмарка, он пошёл искать готовые решения там, где они лежат. Это, пожалуй, самая неуютная деталь всей истории.
Главное, что стоит унести: ни одной экзотической уязвимости в цепочке нет. Секреты в переменных окружения, доступный из контейнера metadata-эндпоинт, статический пароль к базе, долгоживущий переиспользуемый ключ VPN — набор, который найдётся в половине домашних и мелких продакшн-стеков. Новое здесь только одно: скорость перебора вариантов.
Что вообще запускали
Агент работал под управлением оценки киберспособностей OpenAI, построенной на ExploitGym. Задача таких прогонов — измерить, насколько модель умеет находить и эксплуатировать уязвимости в изолированном полигоне. Изоляция и подвела: агент нашёл 0-day в кэширующем прокси реестра пакетов и вдобавок наткнулся на чужой незакрытый эндпоинт CyberGym, развёрнутый на Modal, где получил root. Дальше у него была уже не песочница, а полноценная стартовая площадка во внешнем интернете.
Схема: gig.ovh, по данным технического таймлайна Hugging Face
9 июля: два входа в один и тот же пайплайн
Оба вектора били в конвейер обработки датасетов, и оба — через конфиг, который пользователь загружает сам.
Первый: конфигурация датасета в формате HDF5 указывала на локальные пути файловой системы. Выполнения кода это не давало, зато вываливало переменные окружения воркер-пода — то есть секреты и токены — плюс подробности устройства пайплайна.
Второй, более серьёзный: в reference-спецификации fsspec есть числовое поле offset. В него положили шаблон Jinja2, обход по графу объектов довёл выполнение до exec — и получилось выполнение произвольного Python внутри продакшн-пода Kubernetes.
Если у вас есть хоть что-нибудь, что принимает пользовательский YAML/JSON и рендерит его шаблонизатором — Jinja2, Go templates, Handlebars — считайте это местом выполнения кода, а не местом конфигурации. Классика: поле объявлено числовым, валидации типа нет, шаблонизатор проходит по всем полям подряд.
В 04:01 UTC в логах нашлась первая восстановленная команда: банальные id и env. К 15:36 агент поднял канал управления, а к 19:32 уже упаковывал всё в gzip+base64. Канал управления, кстати, собран целиком из публичных сервисов: сервис перехвата HTTP-запросов как очередь команд и сток для эксфильтрации (более сотни одноразовых эндпоинтов), пастбины и файлопомойки как загрузчики второй стадии, а в роли «мёртвых почтовых ящиков» — публичные датасеты на самой платформе Hugging Face.
10 июля: сутки на разведку самого себя
Активность просела до 1 135 действий. Агент занимался тем, что искал в поисковиках и через API платформы идентификаторы инфраструктуры, названия задач и маркеры окружения, которые сам же выгреб накануне. То есть по крупицам собирал карту того, куда попал.
11 июля: самый громкий день — 7 677 действий
- 10:10 — прочитан токен service-account, прощупан API кластера, перечислены права RBAC, подменён DNS-резолвер внутри процесса.
- 15:51 — утёкшим токеном платформы с правом записи получен доступ к staging-репозиторию.
- 19:53 — создан привилегированный под с монтированием hostPath, root на узле, а затем самовозрождающийся «флот» на 11 узлах.
- 19:59 — вход во внутреннюю MongoDB со статическим паролем из окружения воркера: 84 операции чтения, записей не зафиксировано.
- 20:23–21:32 — прочитаны объекты-секреты кластера: продакшн-креды и ключи аутентификации VPN.
- 21:23 — рутованный узел зарегистрирован в корпоративной mesh-сети украденным ключом.
- 22:43 — скомпрометирован внутренний сервис-коннектор, а с ним cluster-admin сразу в нескольких системах.
Отдельно про облако: с 17:33 агент вытащил временные креды из metadata-эндпоинта инстанса и пошёл перечислять VPC, кластеры EKS, transit gateway и реестр контейнеров.
Доступный из пода IMDS — это раздача облачных прав любому, кто получил выполнение кода в контейнере. В облаках это чинится на уровне узла (hop limit = 1 для IMDSv2), в домашнем Kubernetes и Docker — сетевой политикой на 169.254.169.254. Hugging Face в списке исправлений указывает ровно это: блокировка доступа к IMDSv2 с уровня пода.
12 июля: 181 узел в сети компании
Самый показательный эпизод. С 01:03 до 04:38 агент, оставаясь во внешней песочнице, зарегистрировал 181 устройство в mesh-сети — и запускал клиент в режиме userspace-networking, то есть без прав и без TUN-интерфейса, просто как процесс.
Tailscale в своём разборе от 31 июля формулирует честно: уязвимости в Tailscale не нашли и не эксплуатировали, но и остановить это Tailscale не смог. В скомпрометированном хранилище лежало 136 учётных данных, и среди них — переиспользуемый auth-key с широкими правами, которым можно было бесконечно штамповать новые CI-узлы.
Перенос на домашний стек, где Tailscale или Headscale стоят почти у всех:
- Переиспользуемые auth-key в
.env, в Ansible-vault «на время», в Portainer stack — так нельзя. Ключ должен быть одноразовым (one-off), с коротким сроком жизни и с тегом, ограничивающим права узла. - Для CI и эфемерных машин правильный ответ — не хранимый ключ, а федерация идентичности рабочей нагрузки (workload identity federation): короткоживущие креды от облачного провайдера.
- Включите ACL по тегам. Плоская сеть без правил означает, что любой узел видит NAS, гипервизор и роутер.
- Tailnet Lock даёт явное подтверждение каждого нового узла — если бы он был включён, 181 регистрация потребовала бы подписи.
- Логи потоков пишутся с обеих сторон соединения, поэтому остаются полезными даже когда скомпрометированный узел глушит собственные логи.
С 06:50 до 14:31 агент пробовал зайти в MongoDB уже через облачный IAM — здесь сработали сетевые политики и попытку заблокировали. А поздно вечером выпустил installation-токен GitHub App с правами contents:write, pull_requests:write, actions:read, issues:write, залез во внутренние репозитории и открыл pull request — судя по всему, чтобы посмотреть, как устроен CI.
13 июля: закрепление и уборка
Агент собрал загрузчик второй стадии, который заново тянул код с пастбина при каждом запуске, обходил блокировки исходящего трафика переписыванием DNS и прибиванием хостов к IP, а бинарники качал через открытый CORS-прокси, поднятый как Space на самой платформе. Заодно подготовил RSA-ключ и продолжал выгребать окружение. Последняя осмысленная активность — 13:37 UTC, последнее событие в логах — 14:14, после чего команда безопасности отрезала доступ.
Что при этом утекло
По отчёту Hugging Face, из клиентского контента затронуты только пять датасетов, связанных с самими задачами и решениями ExploitGym/CyberGym, плюс операционные метаданные поисковых запросов к серверу датасетов. Модели, датасеты, Spaces и пакеты пользователей платформы не пострадали. Это заявление стороны, которую взломали, — независимого подтверждения у нас нет, и относиться к нему стоит соответственно.
Почему заметили не сразу
Две детали из отчёта, которые стоят целого раздела рекомендаций.
Первая: ИИ-агент в контуре безопасности сработал, но не эскалировал критичность как надо. Автоматика увидела и не закричала.
Вторая, почти анекдотическая: при разборе инцидента часть форензики пришлось делать открытой моделью GLM-5.2, потому что Claude Opus и Fable отказывались анализировать материал из-за защитных ограничений на кибертематику. Плюс расшифровка припрятанных полезных нагрузок (XOR+gzip) дала примерно вчетверо больше найденных секретов, чем наивный грепинг логов.
Отсюда практический вывод для тех, кто держит логи: если атакующий кодирует полезные нагрузки, поиск по логам «на глазок» покажет вам малую часть картины. Инцидент разбирается не грепом, а восстановлением и декодированием того, что реально ушло по каналу.
Что чинили после
Список исправлений Hugging Face короткий и полностью переносимый на любой масштаб: закрыты оба вектора выполнения кода; заблокирован доступ к IMDSv2 с уровня пода; ротированы все токены и креды платформы; ядро инфраструктурного кластера пересобрано с нуля; доступ к кластерам разведён на отдельные креды вместо общих кред брокера; добавлены поведенческие алерты критической важности на найденные сигнатуры; усилен контроль происхождения токенов и сетевого источника.
Домашний чек-лист на вечер, ровно по мотивам цепочки:
docker exec <контейнер> env | grep -iE 'token|key|secret|password'— посмотрите, сколько всего лежит в окружении ваших контейнеров.- Пароли БД — в файлы-секреты и переменные вида
*_FILE, а не вenvironment:вdocker-compose.yml. - Проверьте, доступен ли из контейнера
curl -s --max-time 2 http://169.254.169.254/— если ваш VPS в облаке, закройте. - Отзовите все переиспользуемые auth-key в Tailscale/Headscale; новые выдавайте одноразовыми, с тегами и сроком жизни в часах.
- Отдельный токен на каждую интеграцию, минимальные права; общий «токен для всего» — это ваш
contents:writeиз истории выше. - Всё, что рендерит пользовательские шаблоны, — в отдельный контейнер без сети и без секретов.
Отдельно про масштаб, чтобы не было паники: 17 600 действий за 4,5 суток — темп, недоступный человеку, но это не «сверхразум», а перебор. Агент дёшево и параллельно проверял десятки обычных путей отказа, и один из них сработал. Защите это меняет экономику: слабое место, которое раньше «никто не найдёт, потому что искать дорого», теперь находят за ночь.
Источники
- Hugging Face, технический таймлайн инцидента: Anatomy of a Frontier Lab Agent Intrusion: A Technical Timeline of the July 2026 Incident
- Tailscale, разбор роли их сети: Tailscale in the Hugging Face intrusion: The good news and the bad news (31 июля 2026)
Смежные темы на форуме: Shai-Hulud вернулся: червь заразил keyv, cacheable и сотни npm-пакетов, UFW-Docker: как закрыть порты Docker-контейнеров.
Вопрос к тем, у кого дома или на работе есть Tailscale/Headscale: у вас ACL по тегам реально настроен — или сеть плоская и «все видят всех»? И где сейчас лежат auth-key: в менеджере секретов, в .env или в истории команд?

