August 22, 2023

SSRF via DNS Rebinding (CVE-2022–4096)

В мире информационной безопасности постоянно появляются новые методы атак на приложения и системы. Одной из таких хитрых уязвимостей является SSRF (Server-Side Request Forgery) через DNS Rebinding. В данной статье мы погрузимся в механизм работы этой уязвимости и поймем как она работает.

SSRF - это атака, при которой злоумышленник заставляет целевой сервер выполнять HTTP-запросы к внутренним или внешним ресурсам, обходя ограничения безопасности. В случае с SSRF через DNS Rebinding, атакующий использует изменение DNS-записей с целью обмана сервера и выполнения запросов к внутренним ресурсам.

На схеме ниже показан простой пример такой атаки:

Данная уязвимость часто возникает в веб-приложениях, которые взаимодействуют со сторонними сервисами для загрузки каких-либо данных. В таких системах совершаемые запросы бывают подконтрольны пользователям. И в результате подмены ссылки запрос уходит уже на другие ресурсы.

На первый взгляд может показаться, что отправка подобного запроса не представляет опасности. Однако это мнение ошибочно. Атаки такого рода могут иметь разнообразные последствия: начиная от получения информации о внутренних ресурсах системы, и заканчивая полной компрометацией приложения. Результат атаки зависит от её типа, расположения сервера в сети, содержания отправленного запроса и других факторов.

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

Автор CVE-2022–4096 выпустил разбор уязвимости в видео-формате и создал лабораторную работу, чтобы продемонстрировать, как уязвимость эксплуатируется. С помощью этой лабораторной работы я попробую показать, как работает данная уязвимость. Давайте загрузим репозиторий с лабораторной работой:

git clone https://github.com/leetCipher/bug-bounty-labs.git

Собираем образ лабораторной:

docker build -t ssrf-bug .

Запускаем приложение:

docker run -p 80:80 ssrf-bug
Теперь нам доступно уязвимое приложение и можно переходить к тестированию.
При входе в систему можно заметить вызов метода API - /api/v3/users и параметр который передаётся - это пользовательский uuid
Сервис, в котором была найденна данная уязвимость, похожа на платформy для обмена файлами, по типу google drive или dropbox.

Можно заметить, что когда мы отправляем запрос, нам становится доступен список файлов которые мы загрузили:

Также у нас доступна функциональность загрузки изображения с помощью URL, и это открывает возможность для SSRF-атак. Попробуем получить ответ на свой collaborator, чтобы убедиться, в том что запрос отправляется:

Но при попытке обратиться к localhost можно заметить, что у сервиса есть защита от обращений к внутренним сервисам или localhost:

Советую пользоваться примерно таким словарем при попытках обхода ограничений:

http://127.0.0.1:80
http://127.0.0.1:443
http://127.0.0.1:22
http://127.1:80
http://0
http://0.0.0.0:80
http://localhost:80
http://[::]:80/
http://[::]:25/ SMTP
http://[::]:3128/ Squid
http://[0000::1]:80/
http://[0:0:0:0:0:ffff:127.0.0.1]/thefile
http://①②⑦.⓪.⓪.⓪
http://127.127.127.127
http://127.0.1.3
http://127.0.0.0
http://2130706433
http://017700000001
http://0x7f000001
http://[email protected]
http://127.0.0.1#google.com
http://google.com.127.0.0.1
http://127.0.0.1/google.com
http://127.0.0.1/?d=google.com
https://[email protected]
https://127.0.0.1#google.com
https://google.com.127.0.0.1
https://127.0.0.1/google.com
https://127.0.0.1/?d=google.com
http://google.com@localhost
http://localhost#google.com
http://google.com.localhost
http://localhost/google.com
http://localhost/?d=google.com
http://127.0.0.1%00google.com
http://127.0.0.1?google.com
http://127.0.0.1///google.com
https://127.0.0.1%00google.com
https://127.0.0.1?google.com
https://127.0.0.1///google.com
http://localtest.me
https://customer1.app.localhost.my.company.127.0.0.1.nip.io
mail.ebc.apple.com
127.0.0.1.nip.io
http://customer1.app.localhost.my.company.127.0.0.1.nip.io
http://bugbounty.dod.network
http://spoofed.burpcollaborator.net
localhost:+11211aaa
localhost:00011211aaaa
http://0/
http://127.1
http://127.0.1
http://127.1.1.1:80\@127.2.2.2:80/
http://127.1.1.1:80\@@127.2.2.2:80/
http://127.1.1.1:80:\@@127.2.2.2:80/
http://127.1.1.1:80#\@127.2.2.2:80/

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

Затем я попробовал сменить версию API на v2, ведь старая версия API обычно не поддерживается, и это могло бы повлиять на результат:

После нескольких попыток как-либо обойти ограничение, возникла идея поменять заголовок content-type на json и передать содержимое в формате json:

И здесь мне удается получить новый ответ от сервера: возвращается ответ, в котором сказано, что запрос на localhost не разрешен. Теперь можно быть уверенным, что здесь возможна уязвимость SSRF. Попробуем перебрать значения localhost:

К сожалению, это также не помогло обойти ограничения.

Во время тестирования данной программы bugbounty мною было замечено, что сервер использует Reverse Proxies, и запросы которые мы отправляем, сначала проходят через этот прокси-сервер, а затем reverse-proxy перенаправляет их на внутренний сервер.

В этом момент я вспоминаю об уязвимости DNS Rebinding!

Попробую сначала объяснить как работает DNS и данная уязвимость.

Для того чтобы понять, как работает данная уязвимость, необходимо разобраться с тем, что такое DNS и сам DNS rebinding.

DNS (Domain Name System) - это система доменных имен, и её работа примерно следующая:

Когда вы посещаете какой-либо сайт, например, google.com, ваш компьютер или маршрутизатор сначала выполняет DNS-запрос к ближайшему DNS-серверу

DNS-сервер возвращает IP-адрес, который соответствует имени хоста google.com. После того как браузер получит данный IP-адрес, он будет использовать его для перехода на сайт google.com

DNS Rebinding

Теперь, когда мы принудительно отправляем HTTP-запрос на 127.0.0.1, наш запрос сначала проверяется с помощью reverse-proxy, который проверяет, какой IP-адрес разрешен

Если это 127.0.0.1, то reverse-proxy полностью отклонит запрос, и он не будет перенаправлен на внутренний сервер. Однако, если разрешены любые домены с IP-адресом, отличным от 127.0.0.1, то reverse-proxy перенаправит запрос на внутренний сервер, и это является важной частью атаки.

Прямо перед тем, как внутренний сервер сделает запрос к домену, домен будет перепривязан на 127.0.0.1, и это произойдет благодаря низкому TTL. В момент, когда внутренний сервер делает HTTP-запрос к нашему домену, он будет переадресован на 127.0.0.1, что обманет внутренний сервер и позволит получить доступ к внутренней системе.

Как будто это больше похоже на Race Condition чем на SSRF :)

Exploit

Чтобы воспользоваться данной атакой, можно использовать этот инструмент для перепривязки DNS имени:

Имя хоста, сгенерированное на этой странице, будет случайным образом перепривязываться на один из адресов с очень низким TTL.

TTL - Time To Live

Time To Live означает "время жизни" и является важным механизмом, используемым локальным DNS для кэширования всех адресов, которые преобразуются в домены, которые вы посетили. Когда истекает TTL, срок действия кэша истекает, и при следующем посещении домена, который был закэширован, ваш компьютер выполняет новый DNS-запрос, чтобы преобразовать домен в IP-адрес. В нашем случае для этого имени хоста TTL будет менее одной секунды. В качестве первого параметра указывается 127.0.0.1, а второй - наша VPS.

Но для демонстрации атаки достаточно будет использовать ip адрес google:

Сейчас, когда мы делаем запрос на получившееся доменное имя, можно заметить, что иногда адрес разрешается в 64.233.164.100, а иногда в 127.0.0.1.

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

Таким образом, получается обойти защиту от SSRF и получить доступ к внутреннему серверу. Однако можно заметить, что возвращаемый ответ - 404, и стоит добиться большего импакта.

Для этого необходимо попробовать найти пути или файлы внутри системы. Во время анализа API был обнаружен метод /api, который раскрывает внутреннюю информацию:

И когда я отправил запрос на конечную точку /api/users, мне вернулись uuid всех зарегистрированных пользователей:

Вспомнив запрос, который отправлялся для получения файлов, попробуем добавить параметр uuid в запрос и получить доступ к файлам другого пользователя:

Канал автора статьи - https://t.me/wr3dmast3rvs