← Назад к блогу

CARBONATO: ИИ-червь угоняет Docker-серверы. Чек-лист для тех, кто держит парсеры на VPS

Ботнет CARBONATO захватывает серверы через Docker API без пароля, ставит ИИ-агента GH0ST и первым делом крадёт ключи к языковым моделям. Разбираем цепочку атаки, признаки заражения и чек-лист на 15 минут для тех, кто держит парсеры, LLM-ключи и логины прокси на своём VPS.

📅27 сентября 2026 г.
CARBONATO: ИИ-червь угоняет Docker-серверы. Чек-лист для тех, кто держит парсеры на VPS

22 сентября 2026 года исследователи ThreatDown описали ботнет CARBONATO. Он заходит на серверы через открытый Docker API без пароля, поднимает на хосте ИИ-агента и первым делом ищет ключи к языковым моделям. SSH-ключи и токены доступа идут уже потом. Если у вас на VPS крутятся парсеры в контейнерах, а в .env лежат ключи OpenRouter или OpenAI и логины прокси, это ваш профиль риска. Ниже разбор того, как устроена атака, и чек-лист на 15 минут, который закрывает этот вход.

Что нашли: 4,3 ГБ образов и агент по имени GH0ST

Всё началось с незакрытого Docker-реестра самих операторов. По данным ThreatDown, в нём было 59 репозиториев, 234 тега образов и 4,3 ГБ данных. Архив охватывает период с октября 2024 по август 2026 года, то есть ботнет работал почти два года, прежде чем его описали. В репозиториях нашлись майнер XMRig и образы с названиями вида fsociety/agent.

Цепочка заражения по отчёту выглядит так:

  1. Сканер ищет хосты, где Docker API открыт на TCP 2375 без аутентификации.
  2. Через этот API червь запускает привилегированный контейнер с примонтированной файловой системой хоста. С этого момента он фактически root на машине.
  3. Поднимается обратный SSH-туннель к инфраструктуре операторов, ставится SSH-сервер с их ключом.
  4. Закрепление идёт через cron, таймеры systemd, rc.local и OpenRC, причём файлы помечаются неизменяемыми. Сторожевые процессы заново скачивают образы, если что-то удалить.
  5. Контейнер маскируется под systemd-resolved, а процесс под поток ядра [kworker/u2:0].
  6. Каждые пять минут скрипты сканируют соседние подсети /24 и Docker-мосты в поисках следующего открытого порта 2375.

Распространение полностью автоматическое и от ИИ не зависит. ИИ здесь отвечает за то, что происходит внутри уже захваченного сервера.

Зачем ботнету ИИ-агент и почему ему нужны именно ключи LLM

На хост ставится открытый фреймворк Hermes Agent с персоной GH0ST (её инструкции лежат в файле SOUL.md). Оператор пишет задачу в Telegram, агент пересылает её вместе с инструкциями на LLM-шлюз операции. Модель разбирает задачу, пишет команды для терминала, читает вывод и решает, что делать дальше. Результаты уходят обратно в Telegram.

В инструкциях агента приоритеты расписаны прямо. ThreatDown цитирует: «AI API keys are the absolute priority. Exfiltrate first». В списке 14 провайдеров моделей, в том числе OpenAI, Anthropic, Google, Groq, Mistral и OpenRouter. SSH-учётки, токены доступа и данные баз идут следующими пунктами.

Логика простая: украденный ключ LLM сразу превращается в бесплатные вычисления или в товар на перепродажу, а платит за них владелец ключа. В отличие от майнинга, такую кражу не видно по нагрузке на процессор. Заметите её только по счёту от провайдера модели.

Почему это касается тех, кто парсит

Типичный стек парсинга в 2026 году выглядит так: VPS, несколько контейнеров (краулер, очередь, база, headless-браузер), LLM для разбора страниц и пул прокси. Все секреты лежат в одном .env или в переменных окружения контейнеров. Для агента, который «читает всё подряд» на root-доступе, это готовая добыча одним файлом:

  • ключи LLM: ради них CARBONATO и создавался;
  • логины и пароли прокси: отчёт их отдельно не выделяет, но агент с root-доступом собирает любые учётки и токены, которые видит, а прокси-креды обычно лежат рядом;
  • ключи облака и доступы к базам с результатами парсинга;
  • сам сервер: XMRig в том же архиве означает, что CPU вашего краулера уйдёт на майнинг, и задачи начнут отваливаться по таймаутам.

Отдельная проблема с репутацией. Сервер, с которого червь сканирует чужие подсети, быстро попадает в абьюз-листы, и хостер может заблокировать его по жалобе. Для парсинга это двойной удар: IP сервера помечен, а украденные прокси-креды уже расходуют ваш трафик чужими запросами.

Как порт 2375 оказывается открытым, хотя вы его не открывали

По умолчанию Docker слушает локальный UNIX-сокет, а не сеть. Порт 2375 появляется, когда кто-то сознательно добавил -H tcp://0.0.0.0:2375 в конфиг демона. Обычно это делают, чтобы подключить удалённую IDE, CI или панель управления контейнерами, и потом об этом забывают. Документация Docker прямо предупреждает, что доступ к демону равен root-доступу к машине, и советует беречь ключи от него как пароль root. Шифрованный вариант с TLS работает на порту 2376, а 2375 означает открытый текст без проверки клиента.

Вторая ловушка бьёт тех, кто уверен, что «у меня же ufw». В документации Docker сказано, что трафик опубликованных портов контейнеров перенаправляется в таблице nat до цепочек INPUT и OUTPUT, на которые опирается ufw. На практике правила ufw для таких портов просто не срабатывают. Если вы запустили Redis, панель очереди или прокси-менеджер с -p 6379:6379, порт торчит в интернет, что бы ни показывал ufw status.

Чек-лист на 15 минут для сервера с парсерами

1. Проверьте, что Docker API не слушает сеть

  1. Посмотрите слушающие порты: ss -tlnp | grep -E '2375|2376|dockerd'. Если в выводе есть 0.0.0.0:2375 или :::2375, закрывайте немедленно.
  2. Проверьте, откуда пришёл флаг: /etc/docker/daemon.json (ключ "hosts") и юнит systemctl cat docker (строка ExecStart с -H tcp://).
  3. Уберите TCP-слушатель и перезапустите демон. Для удалённого управления используйте SSH-контекст: docker context create remote --docker host=ssh://user@server. Порт наружу при этом не нужен вообще.
  4. Если TCP всё же необходим (CI, оркестратор), то только 2376 с взаимной TLS-аутентификацией и белым списком IP источника.

2. Проверьте, что торчит наружу из контейнеров

  • Выполните docker ps --format '{{.Names}} {{.Ports}}'. Всё, что начинается с 0.0.0.0:, доступно из интернета в обход ufw.
  • Служебные сервисы (Redis, Postgres, Mongo, панели очередей, Selenium Grid, API прокси-менеджера) публикуйте только на локальный адрес: -p 127.0.0.1:6379:6379. Внешний доступ делайте через SSH-туннель.
  • Если без внешнего порта не обойтись, фильтруйте в цепочке DOCKER-USER: её Docker не перезаписывает, и именно она применяется к трафику контейнеров.
  • Не держите на сервере открытый прокси без авторизации (Squid на 3128, SOCKS на 1080 «для своих»). Сканеры регулярно находят такие порты, и через ваш IP идёт чужой трафик с чужими жалобами.

3. Разберитесь с секретами

  • Разделите ключи: отдельный ключ LLM на каждый сервер или проект, с лимитом расходов у провайдера модели. Украденный ключ с лимитом $20 — неприятность, без лимита — дыра в бюджете.
  • Ключи прокси тоже разводите по задачам: отдельный логин (суб-аккаунт) на каждый парсер. Тогда утечку видно по расходу конкретного логина, и отозвать можно только его, не останавливая остальную работу.
  • Не пробрасывайте в контейнер весь .env через env_file, если сервису нужны два ключа из двадцати.
  • Где можно, привяжите доступ к IP сервера: белый список у прокси-провайдера или ограничение ключа API по адресу.

Подробнее о том, где и как хранить логины прокси в скриптах и контейнерах, мы писали в разборе безопасного хранения учётных данных прокси.

4. Уберите лишние привилегии

  • Не запускайте контейнеры с --privileged и не монтируйте / или /var/run/docker.sock внутрь без крайней нужды. Сокет внутри контейнера — это тот же root на хосте.
  • Для headless-браузеров обычно хватает --shm-size и seccomp-профиля. Привилегированный режим «чтобы Chrome завёлся» — плохой компромисс.

Как понять, что вас уже заразили

ThreatDown и разборы его отчёта приводят такие признаки компрометации:

  • файл SOUL.md со словом GH0ST (например, /root/.hermes/SOUL.md);
  • переменная окружения или строка в .env: CARBONATO_API_KEY;
  • файлы /usr/local/bin/.docker-network-monitor и подозрительный /usr/sbin/systemd-logind;
  • контейнер с именем systemd-resolved (настоящий systemd-resolved — служба хоста, а не контейнер);
  • неожиданный исходящий трафик в Telegram API и обратные SSH-туннели в сторону AS262145;
  • соединения с адресами 45.79.183.61, 213.136.79.115, 190.211.124.187;
  • неизменяемые файлы в cron и systemd: lsattr /etc/cron.d/* /etc/systemd/system/* покажет флаг i.

Если хоть один признак совпал, чистить сервер вручную бессмысленно: закрепление многослойное, и сторож вернёт импланты. Правильный порядок такой:

  1. С другой машины отзовите все ключи, которые были на сервере: LLM, облако, базы, прокси.
  2. Проверьте расход по каждому ключу за последние недели у провайдеров. Статистика по логину прокси сразу покажет чужой трафик.
  3. Поднимите новый сервер из чистого образа, выпустите новые ключи и только потом переносите данные, без бинарников и cron-файлов со старой машины.

Где здесь прокси и что они не решают

Прокси не защищают сервер от CARBONATO. Червь приходит не через ваши исходящие запросы, а через входящий порт. Зато правильная схема работы с прокси снижает ущерб. Отдельные логины на задачи, лимиты трафика и привязка по IP превращают утечку из «весь баланс ушёл» в «отозвал один логин».

Есть и обратная сторона, которую ботнеты показывают регулярно: чужие захваченные устройства сами становятся «резидентными прокси» сомнительных сетей. Поэтому для парсинга стоит брать трафик у провайдера с понятным происхождением пула. Для большинства задач сбора данных подойдут резидентные прокси с оплатой за гигабайт, где расход по каждому прокси виден в кабинете. Для сервисных задач без жёсткой антибот-защиты хватит более дешёвых прокси датацентров.

Вывод

CARBONATO не использует ни уязвимостей нулевого дня, ни хитрых эксплойтов. Он входит через дверь, которую владельцы серверов открыли сами: TCP 2375 без пароля. Новое в нём другое: внутри работает ИИ-агент, которому поручено первым делом забрать ключи к моделям. Для тех, кто собирает данные на своих VPS, вывод практический. Закройте Docker API, опубликуйте служебные порты на 127.0.0.1, разведите ключи LLM и прокси по задачам с лимитами расходов. Это 15 минут работы, и после них ваш сервер для таких ботнетов становится неинтересной целью.