Categories: Обучение

Обходим черный список, защищающий от SSRF

Давайте представим себе типичную ситуацию: есть веб-приложение, которое должно уметь брать какой-то контент с других серверов, но, что самое опасное, путь до контента задается пользователем. Атакующий имеет потенциал для проведения SSRF-атак. Даже если предположить, что мы можем использовать только протоколы HTTP и HTTPS, открывается немалый простор для действий: от простейших (можно узнать IP хостов во внутренней сети и открытые порты на них) до хитрых махинаций, которые в определенных ситуациях могут приводить к RCE.

Казалось бы, простейшее решение — вынести веб-приложение в специальную DMZ или, наоборот, полностью перенести в интернет. Во втором случае атакующий может попытаться проводить SSRF, но к внутренним ресурсам доступа не получит. Но, во-первых, современные веб-приложения обычно являют собой многокомпонентную систему (хотя бы веб-сервер и СУБД) с распределением нагрузки (серверов больше, чем один) и системами мониторинга. Все это расширяет простор для атаки. Во-вторых, есть различные локальные сервисы, которые висят на loopback-интерфейсе (а-ля 127.0.0.1), который часто рассматривается как «доверенный» (к нему же можно подключиться только с того же хоста).

Разработчики в итоге приходят к выводу, что давать доступ нужно ко всему, кроме определенного набора мест. Классический способ реализации — это черные списки. Создается набор регулярных выражений, по которому и проверяют полученный от пользователя URL перед инициализацией запроса. Наше же дело — попытаться обойти эти блек-листы. Вот несколько вариантов путей обхода.

Обход регекспа.

Составить правильное регулярное выражение, если возможностей много, — не самая простая задача. Чтобы обойти черный список, нужно для начала вспомнить, как составляется URL. Вот общая схема.

scheme://[user:password@]domain:port/path?query_string#fragment_id
Самое важное то, что перед именем хоста мы можем указать через двоеточие логин и пароль, а далее поставить «собаку». В этом случае мы говорим, что хотим аутентифицироваться на ресурсе. Это не какая-то хитрая фича, а изначальная задумка, и поддерживается она в том или ином виде везде.

Возьмем адрес safecurl.fin1te.net. Вот примеры того, как его можно спрятать в URL.

http://validurl.com#user:pass@safecurl.fin1te.net
Парсер увидит validurl.com и даст добро, но тот же curl посчитает кредами все, что идет до @. Если точнее, то validurl.com#user — это имя пользователя.

Вот еще один пример.

http://user:pass@safecurl.fin1te.net?@google.com/
Парсер клюнет на последнее значение, а curl использует центральное (safecurl.fin1te.net), так как все перед первой «собакой» считается кредами, но то, что после знака вопроса, — уже часть query_string. На отсутствие закрывающего слеша curl внимания не обратит.

Постатака.

Мы можем предоставлять корректный URL, но тем или иным способом менять точку для конечного подключения. Например, можно указать корректное доменное имя нашего сайта, но резолвиться имя будет в 127.0.0.1 или другой IP-адрес внутренней сети. И так как реально подключение идет по IP, мы обходим черный список. Еще можно использовать редиректы. Указываем имя своего хоста, а на нем ставим редирект на другой хост, который мы хотим атаковать.

Удар в незащищенное место.

На самом деле специальных адресов, помимо 127.0.0.1, множество. Весь диапазон 127.0.0.1/8 принадлежит интерфейсу loopback. К примеру, 127.123.45.67 — это тоже он, и все сервисы loopback тут тоже доступны.

Есть еще специальный локальный адрес — 0.0.0.0, а также адрес IPv6 — ::1.
Добавим сюда мультикаст-адреса, через которые можно общаться с некоторыми системами из окружения (примеры смотри в презентации по ссылке ниже). Еще можно использовать доменное имя localhost или, если хост состоит в домене, варьировать короткое или длинное имя хоста.

Игра с кодировкой.

IP-адрес или имя хоста можно попробовать закодировать. Черный список будет обойден, а адрес автоматически преобразуется в классический вид перед подключением. Интересно, что эта тема была обнаружена еще в 2000 году. И вот через пятнадцать лет она опять актуальна.

Итак, мы все знаем про классическое десятеричное представление IP-адресов. Но оказывается, что есть и другие варианты. Причем варианты эти зависят от ОС, языка программирования и прочего. Несмотря на свою странность, работают они до сих пор (я проверял на Java и Python в Ubuntu и Windows).

Для эксперимента возьмем IP-адрес 188.226.235.38.

Для начала мы можем избавиться от точек между октетами (частями) IP-адреса.
Для этого нам нужно последовательно умножать каждое значение из октетов на 256 и суммировать с последующим.
188 256 = 48 128
48 128 + 226 = 48 354
48 354 256 = 12 378 624
12 378 624 + 235 = 12 378 859
12 378 859 * 256 = 3 168 987 904
3 168 987 904 + 38 = 3 168 987 942

3 168 987 942 — это то же самое, что и 188.226.235.38, только без точек. Называется такое представление IP-адреса Dword или Dotless.

Мы можем использовать шестнадцатеричные значения вместо десятичных. Для этого используем приставку «0x».
188 —> 0xbc
226 —> 0xe2
235 —> 0xeb
38 —> 0x26

Итого: 188.226.235.38 —> 0xbc.0xe2.0xeb.0x26. Кроме того, можно снова убрать точки и получить другое представление IP-адреса: 0xbce2eb26.

Использовать восьмеричное значение (octal IP). Тогда нужно добавлять 0 в начало.
188 —> 0274
226 —> 0342
235 —> 0353
38 —> 0046

Получается 188.226.235.38 —> 0274.0342.0353.0046.

Тут тоже есть трюк — можно добавлять произвольное количество нулей в начало. То есть IP-адрес 0000274.00000342.00353.000000046 такой же рабочий.

Также можно «перегружать» значения в IP-адресе. Если проще, то мы можем ставить значения более 255 (в десятичной системе). Значения более 255 будут уже больше одного байта, но лишние байты (слева) отсекаются.
188 — это 10111100, а 188 + 255 = 443, то есть 0000001 10111011.

При декодировании IP-адреса левый байт будет отброшен и опять останется только 188. На самом деле мы можем еще и еще добавлять 255 (правда, трех символов в октете уже быть не может). Добавляем в любой октет или во все сразу.

Аналогичный способ должен работать и для шестнадцатеричных значений. В теории к 0xbce2eb26 мы можем добавить любое значение слева. Например, числа 0x9A3F0800bce2eb26 и 9A3F0800 будут урезаны по тому же принципу. Но у меня данный метод нигде не заработал.

Все перечисленное можно смешивать в любой последовательности, а точки можно убирать частично. Например, 444.226.0000353.0×26 — это перегруженное десятичное, десятичное, восьмеричное и шестнадцатеричное значения. И это тоже IP-адрес.
Вариаций очень много, но не рассчитывай на уж очень изощренные варианты. Как я писал выше, есть зависимость от языка и от ОС — разные методы поддерживаются или не поддерживаются в разных местах.

Как видите, методов для обхода достаточно, но многое зависит от конкретной ситуации у жертвы и личной удачи. Большая часть примеров взята из презентации Server-side Browsing considered harmful Николаса Грегори c AppSec 2015 и проекта SafeCurl.

UPD: копи-паст
HelpUTeam

Recent Posts

Windows 10 стала работать медленнее после установки обновления? Исправим это

Ваш компьютер на Windows 10 перестал быть быстрым после обновления системы? Мы подскажем, как устранить…

3 года ago

Описание приложения-чата по приглашениям Clubhouse

Это приложение для iPhone основано на приглашениях и аудио. С его помощью можно всё равно…

3 года ago

Изменение почтового клиента по умолчанию в iOS 14 на Outlook, Spark, Gmail и другие

Одним из самых значительных изменений в операционной системе iOS 14 является возможность менять приложения по…

4 года ago

Включение пузырей уведомлений в чатах на Android 11

В системе Android 10 появился фреймворк для пузырей чатов, популярность которым принёс Messenger. Новая система…

4 года ago

Как настроить беспроводную отладку в Android 11

От загрузки в режим Fastboot при помощи одной команды до установки модов без рута, есть…

4 года ago

Главные проблемы Android 11 Beta и следует ли устанавливать её

Бета-версия операционной системы Android 11 в настоящее время доступна для устройств Google Pixel. Нужно посетить…

4 года ago