Без башки. Эксплуатируем динамический рендеринг для захвата веб-приложений

Содержание статьи

  • Архитектура
  • Разведка
  • SSRF по-легкому
  • Rendertron
  • Prerender
  • Атаки через веб-приложения
  • Советы и трюки
  • Вывод

Ди­нами­чес­кий рен­деринг — это тех­ника, которая исполь­зует­ся, что­бы отда­вать поис­ковикам и ботам заранее отренде­рен­ные веб‑стра­ницы. В этой статье я иссле­дую, как две популяр­ные ути­литы для динами­чес­кого рен­дерин­га добав­ляют уяз­вимос­ти в веб‑при­ложе­ние, если нас­тро­ены неп­равиль­но, и поп­робую объ­яснить, как я исполь­зовал уяз­вимость в одном из них, что­бы зах­ватить сер­вер ком­пании в рам­ках bug bounty.

Для соз­дания сай­тов и веб‑при­ложе­ний активно при­меня­ются фрей­мвор­ки JavaScript — вмес­то ста­тич­ных стра­ниц HTML теперь популяр­но делать PWA (progressive web apps) и SPA (single page applications), которые фор­миру­ют боль­шую часть кон­тента в бра­узе­ре поль­зовате­ля. У это­го под­хода мас­са пре­иму­ществ, и на вебе это поз­воля­ет сде­лать отзывчи­вый интерфейс, но в то же вре­мя такой под­ход нед­ружелю­бен для SEO, потому что боль­шинс­тво поис­ковиков и ботов не понима­ют JavaScript и не могут рен­дерить стра­ницы самос­тоятель­но.

Один из рас­простра­нен­ных спо­собов помочь ботам в таком слу­чае — это открыть зап­рашива­емую стра­ницу в headless-бра­узе­ре на сто­роне сер­вера, дож­дать­ся, пока стра­ница отри­сует­ся, и вер­нуть получив­ший­ся HTML, пред­варитель­но почис­тив его от лиш­них тегов. Этот метод и называ­ется «динами­чес­кий рен­деринг» и сей­час активно прод­вига­ется ком­пани­ей Google как воз­можность опти­мизи­ровать сайт для поис­ка.

Я нат­кнул­ся на этот тип при­ложе­ний, ког­да про­водил нем­ного дру­гое иссле­дова­ние: я искал уяз­вимос­ти в модулях npm, которые исполь­зуют headless-бра­узе­ры. Я написал пра­вила для Semgrep (ути­литы с откры­тыми исходни­ками, в раз­работ­ке которой я при­нимаю учас­тие) и при­менил их к тысячам модулей, которые исполь­зуют Puppeteer, Playwright и PhantomJS в качес­тве зависи­мос­тей. Находок было мно­го, и пос­ле рас­сле­дова­ния и раз­бора резуль­татов я обна­ружил мно­жес­тво модулей, помога­ющих веб‑мас­терам в орга­низа­ции динами­чес­кого рен­дерин­га.

www

Мой набор пра­вил для Semgrep

По­пуляр­ность динами­чес­кого рен­дерин­га рас­тет, поэто­му будет небес­полез­но понять, что может пой­ти не так в про­дак­шене при его исполь­зовании.

В сво­ем иссле­дова­нии я разоб­рал два самых популяр­ных при­ложе­ния для динами­чес­кого рен­дерин­га — Rendertron и Prerender, но опи­сан­ные ата­ки мож­но исполь­зовать и для дру­гих при­ложе­ний такого типа.

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

 

Архитектура

Один из воз­можных спо­собов показать поис­ковому боту под­ходящий для индекса­ции кон­тент работа­ет так: перех­ватыва­ется зап­рос, стра­ница рен­дерит­ся на сер­вере, а резуль­тат в виде HTML со всем нуж­ным содер­жимым воз­вра­щает­ся боту.

  • Сер­вер опре­деля­ет, что зап­рос при­ходит от кра­уле­ра, по заголов­ку User-Agent (в некото­рых слу­чаях — по парамет­рам URL).
  • Зап­рос перенап­равля­ется при­ложе­нию для динами­чес­кого рен­дерин­га.
  • При­ложе­ние для динами­чес­кого рен­дерин­га запус­кает headless-бра­узер и откры­вает исходный URL так, буд­то его смот­рит обыч­ный поль­зователь.
  • По­лучив­ший­ся HTML очи­щает­ся от уже не нуж­ных тегов <script> и воз­вра­щает­ся на сер­вер.
  • Сер­вер воз­вра­щает резуль­тат кра­уле­ру.
  •  

    Разведка

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

    Ког­да потен­циаль­ная цель най­дена, мож­но про­верить, исполь­зует ли она динами­чес­кий рен­деринг, отпра­вив нес­коль­ко зап­росов с раз­ными зна­чени­ями заголов­ка User-Agent.

    Вот зап­рос, который прит­воря­ется Google Chrome:

    curl -v -A "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36" https://shop.polymer-project.org/

    А вот зап­рос яко­бы от бота Slack:

    curl -v -A "Slackbot-LinkExpanding 1.0 (+https://api.slack.com/robots)" https://shop.polymer-project.org/

    Ес­ли отве­ты от сер­вера раз­лича­ются, а ответ на зап­рос от под­дель­ного кра­уле­ра при­ходит в виде кра­сиво­го HTML без тегов <script>, это озна­чает, что сайт исполь­зует динами­чес­кий рен­деринг.

    www

    В качес­тве подопыт­ного я исполь­зовал де­мосайт Google для фрей­мвор­ка Polymer. Под капотом у него Rendertron.

    Срав­нива­ем зап­росы

    Под­робнос­ти того, на какие кон­крет­но зна­чения User-Agent реаги­рует при­ложе­ние, мож­но пос­мотреть в исходном коде Rendertron (файл middleware.ts). Так­же Rendertron всег­да воз­вра­щает заголо­вок X-Renderer: Rendertron. Prerender может писать в отве­тах X-Prerender: 1, но это не умол­чатель­ное поведе­ние.

    Оба фрей­мвор­ка дают раз­работ­чикам воз­можность управлять заголов­ками отве­та с помощью метате­гов на стра­нице. Это полез­но для детек­та динами­чес­кого рен­дерин­га.

    При­мер для Prerender:

    <meta name="prerender-status-code" content="302" /><meta name="prerender-header" content="Location: https://www.google.com" />

    При­мер для Rendertron:

    <meta name="render:status_code" content="404" /> 

    SSRF по-легкому

    Лег­че все­го зах­ватить при­ложе­ние для динами­чес­кого рен­дерин­га, если оно дос­тупно извне. Тог­да мож­но вза­имо­дей­ство­вать с ним нап­рямую и отправ­лять через него про­изволь­ные зап­росы, вклю­чая зап­росы к локаль­ной инфраструк­туре.

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

    Источник: xakep.ru

    Ответить

    Ваш адрес email не будет опубликован. Обязательные поля помечены *