Как Strix это нашёл

Всё началось с классической разведки. Strix перечислил хосты, посмотрел логи сертификатов, замапил всю поверхность атаки. В результате нашёлся Harbor registry по адресу gcp-us-east4-zlw.registry.baseten.co.

Harbor хранит container образы и группирует репозитории в проекты. Один из этих проектов был открыт для всех. Без токена и аутентификации Strix смог:

  • Перечислить репозитории
  • Получить анонимные pull токены
  • Скачать manifests и blobs образов, включая baseten/baseten-app

На этом этапе было бы просто сообщить об exposed registry и закончить. Но компании иногда намеренно публикуют образы, и Strix не хочет создавать false positives. Главный вопрос — какой у образов реальный impact.

Агент решил pull'ить образ и посмотреть, что внутри:

Harbor Exposure Impact Review

Strix начал с проверки — есть ли в образе proprietary код, internal binaries, hardcoded credentials или internal hostnames. Первая находка: пара AWS ключей в baseten/baseten-app. Попытался read-only запрос sts:GetCallerIdentity, который показывает, к какому account'у принадлежит credential. Ответ: InvalidClientTokenId. Ключ был мёртв.

Потом пришёл рабочий токен

Агент распаковал слои образа, запустил TruffleHog и посмотрел конфиг образа напрямую. И нашёл: классический GitHub personal access token в поле history[].created_by.

В Docker image'е есть не только слои (layers) файловой системы, но и config с информацией о самом образе и его build history. Этот config скачивается вместе с образом. И важный момент — удаление файла с credential'ом в Dockerfile ничего не даёт, если история сборки всё ещё содержит копию токена.

Strix использовал токен для read-only запроса GET /user к GitHub API.

Токен в Docker build history с идентификацией GitHub как basetenbot
Токен в Docker build history с идентификацией как basetenbot. Credential'ы редактированы.

Ответ: 200 и account name basetenbot. И это произошло более трёх лет спустя после создания образа в марте 2023 года.

Что может делать basetenbot?

Job's not finished

Живой токен интересен, но permissions определяют всё. Этот токен мог иметь нулевые права и нулевой impact. Strix проверил account и его membership в организации. GitHub вернул X-OAuth-Scopes: repo, и account принадлежал basetenlabs.

GitHub вернул repo scope для basetenbot в организации basetenlabs
GitHub вернул repo scope для basetenbot в организации basetenlabs

Затем агент проверил permissions на каждом репозитории, используя только read-only запросы:

Репозиторий Доступ
basetenlabs/b*** admin: true, push: true
basetenlabs/f*** admin: true, push: true
basetenlabs/h*** admin: true, push: true
basetenlabs/r*** Private, read/write
basetenlabs/b*** Private, read/write
basetenlabs/t*** Private, read/write
basetenlabs/b*** Private, read/write

Это сумасшедший объём доступа в publicly скачиваемом образе.

basetenlabs/b*** — это сам product. Владелец этого токена имел admin и push права на основной репозиторий исходного кода платформы inference. Можно было изменить код, на который другие компании полагаются для запуска своих моделей. А Strix рассматривал отправку своего кода и моделей этой компании — именно поэтому они проводят такие проверки.

basetenlabs/f*** ещё страшнее. Это их GitOps: репозиторий содержит desired state кластеров и применяет его на инфраструктуру. Admin доступ сюда — это route от leaked build токена к изменениям production.

basetenlabs/h*** — это как CLI Baseten попадает на машины разработчиков. Tampering с distribution channel может превратить это в supply chain атаку на всех, кто устанавливает tooling Baseten.

Потом обнаружилось, что в private repo basetenlabs/f*** есть top-level директория customers/ со subdirectories по именам клиентов Baseten.

На этом этапе было достаточно для репорта и уверенности, что это не false positive. Ничего не клонировали, не пушили, не меняли конфиги. Остановились и сразу написали disclosure email.

Как токен оказался в образе?

Build history имел timestamp. Step с токеном выполнился 3 марта 2023 года. Это был старый build credential, который сохранил все права, когда его проверили в июле 2026.

Ошибка банальна. Build нужно было fetch'ить private зависимости из GitHub, поэтому токен передали как build argument. Паттерн выглядел так:

ARG GITHUB_TOKEN
RUN GITHUB_TOKEN=${GITHUB_TOKEN} bash -c '\
  if [[ "${GITHUB_TOKEN}" != "" ]]; then \
    git config --global --add \
      url."https://${GITHUB_TOKEN}@github.com/".insteadOf "git@github.com:"; \
  fi'

Понятно, как в это попадают разработчики. Нужна private зависимость, передаёшь токен, Git аутентифицируется, build работает. Но Docker может записать build argument в metadata и history образа. И так случилось — он записал саму value токена. Docker явно предупреждает об этом.

Есть ещё одна проблема с этим паттерном: git config --global пишет authenticated URL в Git конфиг. Даже если изменить, как токен попадает в build, нужно избежать сохранения его в образе.

Решение: использовать BuildKit secret mount и temporary аутентификацию, которая не сохраняет credential. Потом инспектировать как слои образа, так и его history. И главное — ревокировать старый токен! Изменение Dockerfile ничего не сделает с образом, который кто-то уже скачал.

Что Strix сделал автономно

Baseten имеет responsive security team и уже использует AI security tooling. Но этот токен 2023 года имел admin доступ к product и deployment repos, когда его нашли.

Легко сосредоточиться на application и source репозиториях и забыть про старый container образ. Даже если сканировать файлы образа, нужно ещё проверить его build history.

Что нравится в этом скане — Strix продолжал следовать finding. Нашёл registry, проверил, может ли реально pull'ить образ, протестировал credential и понял, что он мёртв, нашёл другой credential в build history, проверил, что он может делать.

Никто не указывал ему искать Harbor и не давал hints про токен. Agент прошёл через всё автономно примерно за 25 минут.

Вот почему они строят Strix. AI-powered атаки в последние недели становятся суперпугающими, и единственный способ защиты — постоянно взламывать себя, чтобы найти такие проблемы (они всегда будут) раньше, чем это сделают bad guys.

Процесс disclosure

Baseten хорошо справилась. Timeline:

  • 13 июля, 23:10: Сообщение о live токене basetenbot, public Harbor project и repository permissions.
  • 14 июля, утро: Baseten сделала Harbor project private. Указано, что сам токен всё ещё работает.
  • 14 июля, 16:34: Anton из Baseten Security подтвердил issue как critical, сказал, что сделали Harbor private и ротировали токен. Попросили securely удалить скачанные образы.
  • 14 июля, 17:05: Подтверждение удаления и отправка ещё двух findings меньшей severity из того же скана.
  • 17 июля: Baseten закрыла остальные findings.
  • Сентябрь: Уведомление о публичном раскрытии и отправка draft'а поста.

Baseten также отправили благодарность в виде T-shirts и sweatshirts за находку этого critical бага.

Проверьте свои старые образы

Если вы используете containers и GitHub, это стоит проверить в своей инфраструктуре:

  1. Посмотрите, что можно pull'ить без логина, включая старые tags и проекты, о которых давно не думали.
  2. Прочитайте build history командой docker history --no-trunc, или инспектируйте поле history[].created_by в config blob. Проверьте и слои.
  3. Вытащите secrets из build arguments. Используйте secret mounts, убедитесь, что команды, которые их используют, не пишут их обратно в образ.
  4. Проверьте, что реально может делать ваш build token. Fetch зависимости требует read доступ к этой зависимости. Давать этому токену admin на product и deployment repos делает утечку намного хуже. Ограничьте permissions и поставьте expiry.

И запустите что-то вроде Strix против своих систем. Этот скан начался просто так — нужен был inference provider. Дали домен и получили critical уязвимость, которую Baseten могла actioned на следующее утро.

AI attackers могут следовать этими же путями. Если agent за 25 минут может найти live admin token в старом образе, вы хотите, чтобы ваш нашёл его первым.