Physical Address
304 North Cardinal St.
Dorchester Center, MA 02124
Physical Address
304 North Cardinal St.
Dorchester Center, MA 02124
Отравление кеша (cache poisoning) — это вид атаки, при котором атакующий вносит в кеш системы некорректные данные. Когда система пытается их использовать, это приводит к проблемам: от нарушения работы до компрометации данных. В этой статье я покажу отравление кеша на примере реальной уязвимости в движке сайта, а также разберемся c HTTP-заголовками, которые препятствуют этой атаке.
В качестве примера мы будем разбирать CVE-2016-2784 — уязвимость в опенсорсном движке CMS Made Simple. Однако принцип атак на кеш схожий, и изложенное по большей части будет применимо и к другим случаям.
warning
Статья имеет ознакомительный характер и предназначена для специалистов по безопасности, проводящих тестирование в рамках контракта. Автор и редакция не несут ответственности за любой вред, причиненный с применением изложенной информации. Распространение вредоносных программ, нарушение работы систем и нарушение тайны переписки преследуются по закону.
Кеш — это временное место хранения данных, используемых приложениями или операционной системой. Грубо говоря, чтобы не делать какой‑то запрос дважды, мы можем сохранить результат и в следующий раз обратиться сразу к нему.
Есть несколько типов кеша, но нам здесь наиболее важны два из них:
Некоторые механизмы кеширования на стороне сервера:
Для демонстрации локального кеша возьмем движок XenForo, который используется для создания форумов. Если ты перезагрузишь сайт на XenForo несколько раз, а затем отключишь интернет, то получишь кешированную страницу с сообщением «Страница не может быть загружена». При этом если ты откроешь инструменты разработчика, то увидишь service worker в разделе «Передано». Сервисный работник действует как прокси‑сервер и позволяет заменять ответы сервера данными закешированными данными, что и произошло в нашем примере.

Чтобы посмотреть на серверное кеширование, нам понадобится ставить на компьютер целую CMS. Но прежде чем это делать, давай разберемся с HTTP-заголовками, которые связаны с кешированием.
Cache-Control — самый важный HTTP-заголовок кеширования. Он включает в себя несколько директив для управления поведением кеширования. Первые две из них — самые важные:
public/private — указывает, может ли ответ быть закеширован любым кешем (public) или только кешем браузера клиента (private). Когда директива указана как public, ответ может быть сохранен в любом кеше на пути запроса‑ответа. Это включает кеш клиентского браузера, промежуточные прокси и даже CDN. Например: открыто закешированное изображение на сайте может быть сохранено в кеше браузера пользователя, кеше прокси‑сервера интернет‑провайдера и на серверах CDN по всему миру. Когда указано private, ответ может быть закеширован только браузером клиента; no-cache — заставляет кеши отправлять запрос на исходный сервер для проверки перед выдачей закешированной копии. В режиме no-cache при каждом запросе ресурса системы кеширования должны отправлять запрос на исходный сервер, чтобы проверить, актуальна ли копия. Проверка обычно происходит с использованием заголовков ETag или Last-Modified. ETag — это уникальный идентификатор, присвоенный сервером конкретной версии ресурса. Клиент при запросе ресурса может отправить значение закешированной копии в заголовке If-None-Match. Сервер сравнивает этот ETag с текущим ETag ресурса. Если они совпадают, это значит, что ресурс не изменился, и сервер отвечает статусом 304 Not Modified. Пример: мы отправили запрос на получение гифки. Получаем ответ с заголовками ETag и Last-Modified.

Полученный нами заголовок ETag будет отправлен как заголовок If-None-Match, а заголовок Last-Modified — как If-Modified-Since. Здесь сервер сравнивает отправленный нами ETag (значение If-None-Match) с текущим ETag ресурса, и, как видишь, они совпадают, поэтому статус ответа — 304.

Может возникнуть вопрос: что произойдет, если изображение изменится? Если запрашиваемый нами ресурс был изменен и мы (клиентская сторона) об этом не знаем, запрос будет отправлен со старыми заголовками If-None-Match и If-Modified-Since. Если ETag не совпадает, сервер ответит новыми заголовками ETag и Last-Modified, которые станут использоваться в будущих запросах.


Пройдемся по остальным возможным директивам заголовка Cache-Control:
no-store — запрещает кешам сохранять ответ при любых обстоятельствах; max-age=[секунды] — определяет максимальное время, в течение которого ресурс считается актуальным; s-maxage=[секунды] — похоже на максимальный возраст (max-age), но применимо только к общедоступным кешам (таким как кеши CDN). Предположим, что новостной сайт использует CDN для распространения статей. На таком сайте может быть такой заголовок: Cache-Control: s-maxage=600, max-age=300. Это говорит общедоступным кешам (таким как CDN), что они должны хранить статью десять минут (s-maxage=600), но браузер должен хранить ее только пять минут (максимальный возраст равен 300); must-revalidate — указывает, что кеш должен быть проверен на актуальность. Когда срок годности закешированного ресурса (определяется заголовками max-age, s-maxage или expires) истекает, он становится устаревшим. Директива must-revalidate обязывает системы кеширования проверять у исходного сервера (используя ETag или Last-Modified), актуален ли еще устаревший ресурс, перед его повторным использованием, чтобы пользователи не видели устаревший контент; proxy-revalidate — похоже на must-revalidate, но применимо только к общедоступным кешам; no-transform — запрещает любым промежуточным устройствам (таким как прокси) изменять данные ответа. Например, оператор мобильной сети может снижать качество изображений для экономии передаваемых данных. При использовании no-transform такие изменения запрещены.
Pragma: no-cache — это старый заголовок, существующий со времен HTTP/1.0. Он сообщает системам кеширования, что нужно отправить запрос на исходный сервер для проверки перед выдачей закешированной копии. Сейчас он в основном заменен Cache-Control: no-cache.
Expires — это абсолютная временная метка (timestamp), которая задает время, когда закешированный ресурс начнет считаться устаревшим. Это похоже на директиву max-age в Cache-Control. Если присутствует и то и другое, то Cache-Control имеет приоритет.
Age — показывает, как долго ресурс хранился в прокси‑кеше (в секундах). Vary — используется, когда в ответ на запросы с разными заголовками запроса предоставляются разные версии ответа. Представь, что у тебя есть веб‑сайт, который обслуживает изображения в двух форматах: WebP и JPEG. WebP поддерживается не всеми браузерами, но Chrome его поддерживает. Когда браузер запрашивает изображение с сайта, в запросе будет заголовок Accept. Этот заголовок сообщает серверу, какие типы контента может обрабатывать клиент. Для браузеров, поддерживающих WebP, в заголовке Accept будет среди прочего указано image/webp, то есть готовность принимать WebP.
Наличие заголовка Vary: Accept гарантирует, что система кеширования будет хранить несколько версий одного и того же ресурса, если он запрашивается с разными заголовками Accept. Таким образом, если браузер Chrome запрашивает изображение, система кеширования на сервере сохранит эту версию с ключом для заголовка запроса Accept: image/webp. Если позже более старый браузер запросит то же изображение, сервер ответит версией изображения в формате JPEG, и кеш сохранит эту версию отдельно.
С точки зрения отравления кеша нас интересует именно серверный кеш. Он может находиться как на самом веб‑сервере, так и на промежуточном кеш‑сервере (например, на обратном прокси или CDN).
При отравлении кеша на стороне сервера атакующий манипулирует процессом хранения и получения кешированного контента. Поскольку последующие запросы к ресурсу будут обслуживаться из кеша с отравленным контентом, такая атака повлияет на других пользователей.
Источник: xakep.ru