От­равле­ние кеша (cache poisoning) — это вид ата­ки, при котором ата­кующий вно­сит в кеш сис­темы некор­рек­тные дан­ные. Ког­да сис­тема пыта­ется их исполь­зовать, это при­водит к проб­лемам: от наруше­ния работы до ком­про­мета­ции дан­ных. В этой статье я покажу отравле­ние кеша на при­мере реаль­ной уяз­вимос­ти в движ­ке сай­та, а так­же раз­берем­ся c HTTP-заголов­ками, которые пре­пятс­тву­ют этой ата­ке.

В качес­тве при­мера мы будем раз­бирать CVE-2016-2784 — уяз­вимость в опен­сор­сном движ­ке CMS Made Simple. Одна­ко прин­цип атак на кеш схо­жий, и изло­жен­ное по боль­шей час­ти будет при­мени­мо и к дру­гим слу­чаям.

warning

Статья име­ет озна­коми­тель­ный харак­тер и пред­назна­чена для спе­циалис­тов по безопас­ности, про­водя­щих тес­тирова­ние в рам­ках кон­трак­та. Автор и редак­ция не несут ответс­твен­ности за любой вред, при­чинен­ный с при­мене­нием изло­жен­ной информа­ции. Рас­простра­нение вре­донос­ных прог­рамм, наруше­ние работы сис­тем и наруше­ние тай­ны перепис­ки прес­леду­ются по закону.

 

Что такое кеш?

Кеш — это вре­мен­ное мес­то хра­нения дан­ных, исполь­зуемых при­ложе­ниями или опе­раци­онной сис­темой. Гру­бо говоря, что­бы не делать какой‑то зап­рос дваж­ды, мы можем сох­ранить резуль­тат и в сле­дующий раз обра­тить­ся сра­зу к нему.

Есть нес­коль­ко типов кеша, но нам здесь наибо­лее важ­ны два из них:

  • кеш веб‑бра­узе­ра. Сов­ремен­ные бра­узе­ры вро­де Chrome и Firefox хра­нят копии веб‑стра­ниц, изоб­ражений и дру­гих ресур­сов локаль­но на тво­ем компь­юте­ре. Это поз­воля­ет быс­трее заг­ружать кон­тент при пов­торном прос­мотре веб‑стра­ницы, так как дан­ные будут получе­ны с дис­ка, а не из интерне­та;
  • кеш на сто­роне сер­вера. Веб‑сер­веры вмес­то того, что­бы соз­давать содер­жимое для каж­дого поль­зователь­ско­го зап­роса с нуля, хра­нят час­то зап­рашива­емое содер­жимое непос­редс­твен­но на сер­вере и выда­ют его поль­зовате­лям. Это избавля­ет от необ­ходимос­ти пов­торно обра­баты­вать одни и те же дан­ные и зна­читель­но сни­жает как наг­рузку на сер­вер, так и вре­мя отве­та.

Не­кото­рые механиз­мы кеширо­вания на сто­роне сер­вера:

  • объ­ектный кеш. Осно­ван на кеширо­вании зап­росов к базе дан­ных. Вмес­то того что­бы кеширо­вать все стра­ницы, он хра­нит резуль­таты зап­росов к базе в памяти и исполь­зует их при сбор­ке стра­ниц;
  • кеш Opcode. Opcode (опе­раци­онный код) — сис­темная опе­рация, которую может выпол­нять цен­траль­ный про­цес­сор компь­юте­ра. Кеширо­вание опко­дов пред­назна­чено для сер­верных скрип­товых язы­ков, таких как PHP. Ког­да ты пишешь код на PHP, он изна­чаль­но находит­ся в фор­ме, дос­тупной для чте­ния челове­ком. При выпол­нении скрип­ты на PHP ком­пилиру­ются в опе­раци­онный код. Кеширо­вание опко­дов сох­раня­ет ском­пилиро­ван­ный код в памяти, что­бы его мож­но было пов­торно исполь­зовать для пос­леду­ющих зап­росов и не тре­бова­лось каж­дый раз пов­торно ком­пилиро­вать скрипт;
  • кеш CDN. Сети дос­тавки кон­тента (Content Delivery Network, CDN) — это мощ­ности, рас­положен­ные в раз­ных стра­нах. Они кеширу­ют ста­тичес­кие акти­вы, такие как изоб­ражения, таб­лицы сти­лей и фай­лы JavaScript, на сер­верах, которые бли­же все­го к поль­зовате­лям.

Для демонс­тра­ции локаль­ного кеша возь­мем дви­жок XenForo, который исполь­зует­ся для соз­дания форумов. Если ты перезаг­рузишь сайт на XenForo нес­коль­ко раз, а затем отклю­чишь интернет, то получишь кеширо­ван­ную стра­ницу с сооб­щени­ем «Стра­ница не может быть заг­ружена». При этом если ты откро­ешь инс­тру­мен­ты раз­работ­чика, то уви­дишь service worker в раз­деле «Переда­но». Сер­висный работ­ник дей­ству­ет как прок­си‑сер­вер и поз­воля­ет заменять отве­ты сер­вера дан­ными закеши­рован­ными дан­ными, что и про­изош­ло в нашем при­мере.

Что­бы пос­мотреть на сер­верное кеширо­вание, нам понадо­бит­ся ста­вить на компь­ютер целую CMS. Но преж­де чем это делать, давай раз­берем­ся с HTTP-заголов­ками, которые свя­заны с кеширо­вани­ем.

 

Заголовки

 

Cache-Control

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

Pragma: no-cache — это ста­рый заголо­вок, сущес­тву­ющий со вре­мен HTTP/1.0. Он сооб­щает сис­темам кеширо­вания, что нуж­но отпра­вить зап­рос на исходный сер­вер для про­вер­ки перед выдачей закеши­рован­ной копии. Сей­час он в основном заменен Cache-Control: no-cache.

 

Expires

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».

Присоединяйся к сообществу «Xakep.ru»!

Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее

  • Подпишись на наc в Telegram!

    Только важные новости и лучшие статьи

    Подписаться

  • Подписаться
    Уведомить о
    0 комментариев
    Межтекстовые Отзывы
    Посмотреть все комментарии