company_banner

Web Security Testing Starter Kit

    Всем привет!

    Меня зовут Андрей. Уже 10 лет я занимаюсь поиском уязвимостей в различных веб-сервисах. и готов поделиться своими знаниями с вами. В мае прошлого года я выступал с докладом про это на конференции Heisenbug, а теперь готов поделиться своими знаниями еще и здесь, на просторах Хабра. Итак, начнем.

    Однажды я нашел уязвимость на серверах небезызвестной компании 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 и выясните, как с помощью него проверить ваше приложение. Если он ничего не найдет, возьмите другую тулзу и изучите её, а затем протестируйте свое приложение на другие уязвимости. Как я сказал в начале, в статье разобраны примеры лишь нескольких уязвимостей, но тысячи их.
    Я не верю, что можно писать абсолютно безопасный код. Уязвимости всегда есть, просто вы их еще не нашли.
    • +24
    • 14,1k
    • 8
    SEMrush
    93,53
    Компания
    Поделиться публикацией

    Комментарии 8

      0
      Спасибо за статью!
        0
        Да не за что:)
          –1
          Нет, за статью!:)
        0
        Вопрос по XSS — большинство браузеров такие атаки успешно блокирует. Есть ли техники обхода анти-XSS механизмов браузеров?
          +1
          В общем случае — есть. В частности — зависит от версии и типа браузера. XSS Auditor bypass поищите в поисковике — статей много.
          0
          Я правильно понял, что на примере с аватарой вы, по сути, атаковали собственную компанию изнутри? Я понимаю, что если писать код во всех отношениях грамотно, то подобной ситуации возникнуть не должно и все же, в данном случае это больше вопрос лояльности сотрудника, чем вопрос уязвимости кода. Вы абсолютно справедливо заметили, что главный источник безопасности — это пользователи системы, но должен быть какой-то разумный подход к внутренней безопасности, потому что безопасность и удобство являются антагонистами. :)
            0
            В данном случает — это учебный стенд для сотрудников, на котором мы и показываем как работает эта уязвимость. Он изолирован от основной инфраструктуры. Но да, на нём мы и показываем, что «вот если такое будет в реальном веб-приложении, то через этот сервер можно попасть вот сюда и сюда и сюда».
            0
            Тогда понятно, я просто отметил, что требования к безопасности системы извне и изнутри обычно бывают разные. Под правильным кодом, я как раз подразумевал то, что проверка типа файла являет необходимым условием, для безопасности. Если файл не является изображением, то он не должен приниматься системой, отвечающей за обработку графической аватары.

            Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

            Самое читаемое