Основы поиска WEB-уязвимостей
Однажды я нашел уязвимость на серверах небезызвестной компании Facebook. Ребята забыли обновить ImageMagick (библиотеку по обработке изображений) и поплатились за это:) Этим примером я хотел показать, что все мы люди, и все можем допускать ошибки, неважно, в каких компаниях и на каких должностях мы работаем. Проблема только в том, что эти ошибки могут приводить к разного рода рискам.
Чем сложнее у вас приложение/сайт/инструмент, тем больше вероятность того, что что-то может пойти не так.
Проверять на уязвимости нужно. Глупо не делать этого совсем.
Этапами проверки web приложений могут стать следующие пункты:
- Подготовка:
- Определить точки входа — по сути дела откуда в приложение могут поступить данные;
- Определить скоп технологий;
- Определить на какие уязвимости имеет смысл проверять;
- Проверка. Необходимо проверить все уязвимости, которые применимы к скопу технологий вашего веб-приложения;
- Анализ кода. Не ленитесь, не пропускайте этот пункт. В некоторых случаях без доступа к коду сложно найти какие-либо уязвимости, а еще это позволяет обойти фильтрации, которые могут быть заложены в ваше приложение.
Обо всем этом расскажу подробно.
Итак, подготовка. Не буду заострять внимание на всех типах уязвимостей, иначе количество текста в статье превысит все мыслимые и немыслимые размеры. Советую начать изучение со списка OWASP Top Ten Project. Раз в несколько лет консорциум OWASP формирует лист наиболее актуальных на данный момент уязвимостей. Несколько из них мы рассмотрим детально.
XSS
XSS (межсайтовый скриптинг) — атака на пользователя, которая позволяет атакующему выполнить произвольный сценарий в контексте браузера его жертвы. Чаще всего подразумевается внедрение HTML-тегов и JS-сценариев.
Пример: был в свое время такой сервис Yahoo! Ad Exchange. В форме восстановления пароля был параметр "URL возврата". В нем можно было указать, на какую страницу нужно вернуться после процесса сброса пароля. Если туда вставить тот или иной вектор, который приводит к исполнению JS-кода, можно получить все cookies пользователя. Разберем, как это происходит и почему.
Вот сам вектор. Весь сразу:
Чтобы произвести инъекцию, нужно выйти из контекста той переменной, в которой мы находились по умолчанию (по умолчанию — это то, что предполагал программист, когда писал код).
Ниже представлено исходное значение. Это является валидным поведением.
Видим, что returnURL=/login.jsp
Это попадает в значение тэга <input>.
Если мы добавим кавычку после jsp, то увидим, что посыпался синтаксис HTML. Благо, HTML язык не строгий, парсер пропустит и проблем не будет:
Добавим еще один символ. Здесь мы полностью выходим из контекста тэга <input>:
Теперь добавляем открытие тега <svg>, и получаем два валидных тега с точки зрения синтаксиса:
Далее добавляем весь вектор атаки:
В данном случае отрисовывается svg картинка. С помощью JavaScript хэндлера "onload" выполняем алерт с cookies документа. Все! Атакующий получил ваши cookies.
Если в пруфе мы показываем только сам алерт, то атакующему не составит труда послать куки на подконтрольный ему сервер и, в самом худшем случае, захватить ваш аккаунт. Давайте еще раз посмотрим как это выглядит:
Векторов атаки на клиента также существует больше количество. Приведу несколько примеров с кратким описанием. Но для начала надо понять, как выйти из контекста:
- Выход из контекста. Эти 6 символов помогут выйти не только из контекста значения параметра HTML, но и из многих других:
- -->'">
Ну и, собственно, два простейших вектора атаки:
- Вставка ссылки, в частности. А в общем случае — изменение внешнего вида страницы. Например, в вашу страницу могут внедрить html-код и выводить какое-нибудь фейковое окно с запросом данных:
- Строка "аааа" указывает на google.com. Вы можете увидеть, что ссылка отрисовалась на странице, а можете не увидеть, но это не отменяет ее присутствия в коде. И в том, и другом случае дело можно считать сделанным;
- Исполнение скрипта, например, перехват cookies пользователя:
Где и как искать XSS?
Для начала найдите функции вывода информации/данных на страницу. Проанализируйте, какие данные туда попадают? Здесь появляется такое понятие, как недоверенный источник. И таковым, в первую очередь, является ваш пользователь. Векторы атаки могут содержаться и в любых других источниках: RSS ленты, другие сервисы, как внешние, так и внутренние.
Вот пример из жизни: я работаю в IT компании SEMrush. Мы разрабатываем онлайн-платформу, которая является универсальным инструментом для интернет-маркетологов. Так вот, один сервис у нас сохранял данные, другой выводил, каждая из команд понадеялась друг на друга, получилось так, что на втором сервисе XSS возник из-за отсутствия должной обработки выводимых данных, а первая команда не сочла необходимым входящие данные вообще фильтровать каким-либо образом и хранила их "как есть". Вывод: если вы не знаете, как хранятся те или иные данные, их нужно считать недоверенными. Или заранее договориться о том кто и как хранит, фильтрует, выводит данные.
Если у вас уже присутствуют данные из недоверенных источников, то рекомендую:
- Изучить фильтрацию поступающих данных;
- Изучить контекст выводимых данных. Далеко не во всех случаях ХSS может быть исполнена, даже при отсутствии фильтрации. Например, если Content-type ответа: text/plain;
- Не надеятся на фильтрацию встроенных систем защиты framework-ов.
SQL injection
Внедрение SQL-кода (англ. SQL injection) — внедрение в запрос к базе данных инструкций, которые там не подразумевались.
Пример (не из жизни, но т��же хороший):
Видим скрипт, который выводит таблицу пользователей. В данном случае он умеет осуществлять поиск по имени пользователя.
Справа показано, как выглядит запрос к базе данных, а слева — результат работы скрипта в браузере.
Дальше для того, чтобы протестировать и получить хоть какой-нибудь первичный результат, вставляем две кавычки в значение параметра name. Одинарную и двойную:
Видим, что с точки зрения вывода у нас все развалилось, таблица пропала полностью. В запросе также получаются лишние 2 кавычки. Нарушается весь синтаксис SQL запроса.
Если далее мы возьмем и добавим вот такую конструкцию в значение параметра (прим.ред.: смотри в желтой рамочке), мы получаем абсолютно валидный запрос. Вывод страницы полностью идентичен первому запросу, который не включал в себя инъекцию:
Для того, чтобы полностью убедиться, что уязвимость есть, добавляем вместо единицы двойку:
Получается, что запрос становится ложным, с точки зрения булевой логики и, как следствие, не содержит данных. Однако, в отличие от того момента, когда у нас была ошибка, у нас есть вывод самой таблицы.
Чтобы не перечислять все то, что может быть в разных движках баз данных и запросах разных типов, я свел все в одну строку:
Это то, что вы можете попробовать ввести в параметр, а далее уже, по разницам в ответах попытаться понять, что происходит, есть там уязвимость или нет. Где-то 80% случаев вы сможете покрыть данной манипуляцией.
Остается 20% сложных ситуаций, в которых это не даст вам никакой информации, например: вложенные запросы/фильтрации и прочее. В своей практике я встречался с подобным только однажды. Тогда мне пришлось обойти фильтрацию на стороне сервиса.
Какую же опасность несет в себе SQL?
- Из очевидного — утечка информации. Если атакующий смог сформировать запросы к вашим базам данных, он может тем или иным образом получить значения, которые не предполагались для вывода (хэши паролей, адреса клиентов и прочее);
- Изменение данных. Сейчас многие разработчики используют БД через ORM системы. Сами эти системы по умолчанию позволяют делать несколько запросов за 1 раз, синтаксис SQL также позволяет это делать. При наличии уязвимости все может обратиться не только к выборке данных, но и к их изменению (вплоть до удаления таблиц, изменения структуры базы данных и пр.).
- В этой же ситуации возможно повышение привилегий пользователя. Меняете поля в тех или иных таблицах (например, в базе users поставить is_admins=true) и тем самым поднимаете свои права до админа;
- DoS. В некоторых случаях инъекция может привести к отказу в обслуживании. Формируется несколько тяжелых запросов к БД, запускается в несколько потоков и ваш сервис на какое-то время может перестать работать;
- Чтение системных файлов — еще одна беда, которую может принести SQL инъекция. Возникновение этого риска зависит от того, какую систему БД вы используете, а также от того, какой пользователь с ней работает (привилегированный или нет);
- RCE (Remote Code Execution) — это исполнение кода. Напрямую зависит от типа БД, которая используется (раньше такое было возможно по умолчанию, с дефолтными настройками БД, в MSSQL).
Ниже приведена таблица уязвимых мест в SQL запросе. Запоминать ее, конечно же, не нужно. Кроме этого, это не все возможные варианты, а просто примеры. Просто обратите внимание на то, что красным выделены те места, где может возникнуть инъекция в SQL запрос, если туда придут необработанные данные. Опытный атакующий может найти способ это эксплуатировать. Будьте осторожны и внимательны.
SSRF (Server Side Request Forgery)
Дословный перевод — подделка запросов на стороне сервера. То есть атака, которая позволяет манипулировать данными, которые вы отсылаете приложению, заставляя сделать запрос не туда, куда изначально планировала бизнес-логика.
Работа SSRF демонстрируется на изображении ниже:
Есть атакующий, есть ваш сервер, который закрыт Firewall, но при этом есть функционал, который позволяет делать запросы от имени сервера дальше.
Если этот функционал не защищен должным образом и/или не корректно, как это обычно и бывает:), обрабатывает входящие данные, атакующий может обратиться к Memcached, Redis или получить доступ к любым внутренним ресурсам жертвы.
Для более наглядного примера рассмотрим стенд, который используется в компании SEMrush для внутреннего обучения сотрудников.
Здесь важно обратить внимание на значение "URL аватара":
Здесь обращаем внимание на 2 момента:
- сам URL аватара;
- и возможность загрузить его с помощью файла.
Обратите внимание, поле "URL аватара" — текстовое поле для ввода, а значит мы можем манипулировать этим значением. Попробуем поставить на аватар, пожалуй, самую известную картинку в интернете — робота Google со страницы ошибки 404.
Делай раз:
Делай два — жми "Сохранить". Получаем робота вместо моей фотографии:
Тут стоит обратить внимание на то, что значение "URL аватара" поменялось, на какой-то локальный путь — видимо, приложение запросило картинку, сохранило её на сервер и подставило новое значение.
Двигаемся дальше. В поле "URL аватар" вводим значение file:///etc/passwd. Такая конструкция позволяет обращаться к файловой структуре. В некоторых библиотеках, которые используют для работы с HTTP, по умолчанию включена работа с файловой структурой, и получать данные с серверов, на которых они запустились:
Что же произойдет после нажатия кнопки "Сохранить"?..
Во-первых, картинка стала "битой", во-вторых, поменялся путь к файлу.
Если мы попытаемся запросить эту картинку в браузере, нам ничего не покажут (файл "битый"). Но мы-то не первый день в информационной безопасности, мы знаем что надо делать:) Путем нехитрых манипуляций получаем содержание файла на своем компьютере:
Таким образом, с помощью этой уязвимости я, как атакующий, могу получить все исходные коды вашего сервиса и другие данные, доступные для чтения пользователю, от которого работает веб-сервис.
Разберем опасности SSRF:
- Сканирование портов. Для внешней сети ваш сервис — это одна точка входа и 2 порта (http и https). Сканируя порты изнутри вашей инфраструктуры, атакующий может обнаружить открытые;
- Обход host-based аутентификации. В чем суть? Вполне возможно, что внутри вашей структуры существуют сервисы, которые доступны только определённым ресурсам (читай: белый список IP). Так вот, если ваш уязвимый сервер находится в white-листе, вы получаете доступ к тем сервисам, которые для него доступны, и, как следствие, можете ими манипулировать;
- В продолжение развития вышеупомянутой атаки, вы можете продолжать эксплуатировать уязвимые программы, запущенные в интранете или на локальном сервере. Довольно часто происходят ситуации, когда внутренняя инфраструктура, недоступная извне, слабо мониторится (не всегда обновляются версии/security патчи и пр.). Это все также может привести к атакам и краже данных;
- Чтение локальных данных. Показал это выше на примере. Ваши конфиги можно прочитать и получить доступы, токены и пароли.
Где искать SSRF?
Это не настолько стандартная уязвимость. Она может встречаться в разных, часто в самых неожиданных местах. В данной ситуации прием “тут смотри, а вот сюда можешь даже не смотреть” не работает.
Признаки возможной SSRF:
- Webhook-и. Если они настроены плохо, если неправильно настроен Firewall на тех серверах, откуда Webhook-и делают запросы, можно получить уязвимость;
- Конвертация HTML. Попробуйте вставлять <iframe>, <img>, <base>, <script> или CSS конструкцию url, указывающую на внутренний сервис;
- Загрузка удаленных файлов. Помните пример с аватаром? Когда у пользователя есть возможность загрузить данные по URL на сервер, это влечет за собой большие проблемы. Попробуйте отправить URL c портом и посмотрите, какой контент загрузится.
Как искать SSRF?
- Поднять сервер и запустить listener:
user$ nc -1 -n -vv -p 8080 -k
- Таким образом вы будете видеть все запросы, которые приходят на ваш сервер на порт 8080. С одной стороны, это незанятый порт вашими веб-серверами, а с другой стороны, это порт, который часто не фильтруется Firewall. Считается, что он используется для http протокола и запрещать его вроде как необязательно;
- В исследуемом приложении найти параметр, в который можно передавать путь для запроса данных с нашего хоста (к нашему listener’у);
- Изучить вывод listner’а или полученный обратно ответ.
XXE (XML External Entity)
Начну рассказ как всегда с примера, чтобы было нагляднее:
В вышеупомянутом сервисе SEMrush есть такой инструмент, который занимается анализом контента. Пользователь вбивает URL своего сайта или сайта-конкурента и перед тем, как его проанализировать, сервис этот самый контент скачивает. Смотрите как это происходит на видео:
Для скачивания сервис обращается к заданному сайту и краулер скачивает файл sitemap.xml. А это уже тот самый XML, о котором мы и ведем речь в этом блоке.
В видеоролике мы можем наблюдать, как атакующий сайта добавил в sitemap.xml определение внешней сущности XXE, которая указывает на "file:///etc/hosts". На Linux системах это файл, в котором описывается, какой IP соответствует хостам, если для них нет DNS записей.
Далее атакующий указывает в тэге <loc> раскрытие этой переменной. Парсер XML после этого, обработав весь XML в купе, в переменную location (выделено на видео красным цветом) вставит то значение, которое было в "file:///etc/hosts".
Атакующий инициирует запуск нашего парсера, идет в логи, и вот тут появляется строка, выделенная на видео белым цветом. Это содержимое файла "file:///etc/hosts", полученное в GET-запросе в логах подконтрольного ему сервера.
Что дальше? Атакующий убедится, что был прав. Поменяет название файла на "file:///etc/fstab", еще раз запустит парсер и получит статистику по запущенным процессам.
И напоследок: "file:///etc/hostname", запуск парсера, заходим в логи и получаем hostname машины, на которой находимся.
Это реальная бага, обнаруженная реальным человеком, не относящимся к нашей компании. Пришлось посыпать голову пеплом и платить деньги:)
Еще один пример XXE:
Предположим, что у нас есть некий endpoint, он так и называется — example.com/xxe.
Отправляем в него XML структуру → в XML объявляем сущность ХХЕ → сущность ХХЕ обращается к системному файлу /etc/passwd и раскрывается дальше в теле ответа → получаем в ответ содержимое этого файла.
Стоит заметить, что это встречается не так часто, как хотелось бы.
Почему это работает?
XML документы — это структурированный формат данных, который, кроме всего прочего, позволяет описать те типы данных, которые могут в нем содержаться, с помощью специального тега DOCTYPE.
Data Type Definition (DTD) — определение типов данных внутри этого XML документа. Есть три варианта того, как это можно сделать:
- Если документ придерживает DTD. На примере ниже представлен некий XML, в котором есть структура заказов, есть элемент product и элемент count (предположительно: инвентаризационный номер продукта и количество позиций в заказе). Order является родителем для этих двух элементов:
- Внешние DTD на локальном сервере.
- Делаем то же самое, что и в первом варианте, но все определения выносим в отдельный файл, который находится на локальном сервере:
- Внешние DTD на стороннем сервере:
Если вы внимательный читатель, то уже наверняка соотнесли тот факт, что возможность делать такие запросы у вас на сервисе и запрашивать внешние схемы DTD — уже сама по себе уязвимость, о которой мы говорили выше, а именно уязвимость SSRF.
Частое явление: разработчики исправляют XXE уязвимость, но при этом забывают запретить обработку внешних DTD и получают SSRF. Смертельных последствий из этого не последует, но забывать об этом все же не стоит.
Что такое сущности (Entities) в XML?
Entities в XML. Что это? Это возможность определить некие значения и загнать их в переменные. В сущностях есть три разных типа:
Predefined — предопределенные. Аналогичны HTML-коду, дают возможность использовать те символы, которые являются частью синтаксиса языка, как текстовую структуру. Здесь они приведены для примера, чтобы вы поняли, что это такое. Да, в большинстве атак они не используются, но могут понадобиться в некоторых исключительных случаях.
General и Parameter. Это одно и то же, только меняется синтаксис их объявления и то, как их можно вызывать после объявления
Где искать XXE?
Несколько вариантов:
- Обработка XML в любом проявлении:
- SVG;
- sitemap;
- Конвертация HTML в другие форматы;
- Обработка docx, xlsx и подобных форматов.
Как их искать?
На картинке изображен всего лишь пример от которого нужно стартовать. Он не является универсальным ключом. Его нужно оптимизировать под те форматы, которые вы используете в исследуемом сервисе.
Первое, что мы делаем: объявляем некую сущность Z, которая обращается к подконтрольному для атакующего хосту, раскрываем ее внутри тела документа XML.
Здесь возможен и второй вариант — как раз сущность Parameter. Отличается от первого объявления (знаком %). И для того, чтобы обратиться к этой сущности, тело документа уже не нужно. Мы можем обратиться к ней напрямую в DOCTYPE структуре и раскрыть эту сущность:
Третий вариант развития событий — посмотреть, включена или выключена возможность запрашивать DOCTYPE c внешних сервисов:
Далее смотрим вывод подконтрольного нам сервера. Если мы видим обращение к URL /check с тех или иных IP-адресов, значит, с большой долей вероятности, уязвимость прошла, и дальше нам просто нужно разбираться, как получить более интересные нам данные.
В чем же состоит опасность XXE? Напишу кратко и по пунктам:
- Чтение локальных файлов;
- Доступ к локальным ресурсам;
- Сканирование портов/хостов;
- Plain-text wrappers. Это возможность обращаться к вашим сервисам, которые работают по plain-text протоколу;
- Remote Code Execution (не часто). Он выключен по умолчанию;
- DoS. За счет того, что сущности можно конкатенировать, есть возможность легко получить экспоненциальный рост объема памяти, занимаемой этими параметрами. Как результат — атака Billion laughs attack. Иными словами, вы просто перегружаете память атакуемого сервера.
В заключение скажу, что все это (статья, да и любые полученные вами знания) не имеют смысла без практического применения. Поэтому попрошу вас о сущей мелочи: задумайтесь о приложении, которое вы писали/тестировали вчера и попробуйте его проверить на уязвимости. Есть ли там формы ввода или обработка XML? А защищено ли это приложение? Добавлена ли фильтрация и отключены ли DTD?
Откройте мануал SQLmap и выясните, как с помощью него проверить ваше приложение. Если он ничего не найдет, возьмите другую тулзу и изучите её, а затем протестируйте свое приложение на другие уязвимости. Как я сказал в начале, в статье разобраны примеры лишь нескольких уязвимостей, но тысячи их.
Я не верю, что можно писать абсолютно безопасный код. Уязвимости всегда есть, просто вы их еще не нашли.
источник: https://habr.com/ru/company/semrush/blog/441966/