Как 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'ить образ и посмотреть, что внутри:
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.
Ответ: 200 и account name basetenbot. И это произошло более трёх лет спустя после создания образа в марте 2023 года.
Что может делать basetenbot?
Живой токен интересен, но permissions определяют всё. Этот токен мог иметь нулевые права и нулевой impact. Strix проверил account и его membership в организации. GitHub вернул X-OAuth-Scopes: repo, и account принадлежал 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, это стоит проверить в своей инфраструктуре:
- Посмотрите, что можно pull'ить без логина, включая старые tags и проекты, о которых давно не думали.
- Прочитайте build history командой
docker history --no-trunc, или инспектируйте полеhistory[].created_byв config blob. Проверьте и слои. - Вытащите secrets из build arguments. Используйте secret mounts, убедитесь, что команды, которые их используют, не пишут их обратно в образ.
- Проверьте, что реально может делать ваш build token. Fetch зависимости требует read доступ к этой зависимости. Давать этому токену admin на product и deployment repos делает утечку намного хуже. Ограничьте permissions и поставьте expiry.
И запустите что-то вроде Strix против своих систем. Этот скан начался просто так — нужен был inference provider. Дали домен и получили critical уязвимость, которую Baseten могла actioned на следующее утро.
AI attackers могут следовать этими же путями. Если agent за 25 минут может найти live admin token в старом образе, вы хотите, чтобы ваш нашёл его первым.