Се­год­ня погово­рим о самой мас­совой уяз­вимос­ти в веб‑при­ложе­ниях — XSS. Я пос­тара­юсь рас­толко­вать суть этой проб­лемы на паль­цах. Из этой статьи ты узна­ешь, как отпра­вить пос­лание всем поль­зовате­лям сай­та, как устро­ить ата­ку на сайт при помощи фай­ла SVG и как увес­ти cookie адми­на.

warning

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

XSS (Cross-Site Scription) — внед­рение вре­донос­ного кода на JavaScript в веб‑при­ложе­ние. Ата­кующий ищет спо­соб нарушить логику исполне­ния и вкли­нива­ется меж­ду при­ложе­нием и поль­зовате­лем. Резуль­тат ата­ки — выпол­нение дей­ствий от име­ни поль­зовате­ля, кра­жа дан­ных (вклю­чая cookie), под­мена любых дан­ных на сай­те, фишинг, перенап­равле­ние на сто­рон­ние ресур­сы, осно­ва для дру­гих век­торов ата­ки.

Есть три основных типа XSS:

  • reflected (отра­жен­ная) — воз­ника­ет, ког­да дан­ные из зап­роса под­став­ляют­ся в ответ сер­вера без дол­жной филь­тра­ции;
  • stored (хра­нимая) — хакер может добавить вре­донос­ный код в базу дан­ных, который без филь­тра­ции будет выведен на стра­нице при­ложе­ния;
  • DOM-based (осно­ван­ная на DOM) — при этой уяз­вимос­ти у ата­кующе­го есть воз­можность передать код на выпол­нение фун­кции eval() или в тег innerHTML.

Все три типа XSS осно­ваны на одной ошиб­ке — пло­хой филь­тра­ции дан­ных, на которые поль­зователь может пов­лиять.

 

Отраженные XSS

Пред­ставь, что тебе на поч­ту при­летел про­мокод 01234 от какого‑то интернет‑магази­на. Перехо­дишь по ссыл­ке в пись­ме и видишь: «Ваш пер­сональ­ный про­мокод 01234, дает скид­ку 50%». При­ятная скид­ка, но ты замеча­ешь параметр ?promo=01234 в URL. Про­буешь поменять на OLJSxksa858, и над­пись меня­ется на «Ваш пер­сональ­ный про­мокод OLJSxksa858, дает скид­ку 50%». Пах­нет пря­мым отра­жени­ем парамет­ра в кон­тенте стра­ницы. «Пря­мое отра­жение» зна­чит, что сер­вер берет стро­ку и без каких‑то серь­езных манипу­ляций встав­ляет ее в ответ.

Пер­вое — смот­ришь исходник в инспек­торе объ­ектов бра­узе­ра, что­бы понять верс­тку и пос­тро­ить стра­тегию для тес­та:

<h3>Ваш персональный промокод OLJSxksa858, дает скидку 50%</h3>

Сле­дующий шаг — вбить пей­лоад для тес­тирова­ния XSS: <script>alert("Pwned")script>. Жмешь Enter и видишь, как поверх стра­ницы появ­ляет­ся сооб­щение со стро­кой Pwned. Поз­драв­ляю, ты нашел reflected XSS. То есть XSS, которая выз­вана пря­мым отра­жени­ем зна­чения парамет­ра из зап­роса в отве­те сер­вера.

Смот­ри, как это может выг­лядеть на сто­роне сер­вера. Нап­ример, веб‑при­ложе­ние на PHP с про­моко­дами:

$promo = $_GET['promo'];
echo "<h3>Ваш персональный промокод: $promo, дает скидку 50%</h3>";

Скрипт выводит зна­чение пря­мо в HTML, без какой‑либо филь­тра­ции или санити­зации. Код HTML, который бра­узер получит от сер­вера, будет выг­лядеть так:

<h3>Ваш персональный промокод <script>alert("Pwned")</script>, дает скидку 50%</h3>

Бра­узер, может, и решит, что раз­работ­чик не в сво­ем уме, но коман­ду выпол­нит. И это не сов­сем шут­ка: сов­ремен­ные бра­узе­ры ста­рают­ся защитить поль­зовате­лей от подоб­ных инъ­екций, но механизм защиты сла­бый. У бра­узе­ра нет мар­керов, что­бы надеж­но отде­лить код инъ­екции от кода, написан­ного раз­работ­чиком.

info

Chrome и Firefox могут бло­киро­вать всплы­вающие окна команд alert(), prompt() и confirm(). Это защита от спа­ма и вре­донос­ных скрип­тов. Для под­твержде­ния XSS луч­ше исполь­зуй print() или console.log().

Ал­горитм поис­ка reflected XSS выг­лядит так. Про­веря­ем все воз­можные тек­сто­вые поля и парамет­ры URL. Вво­ди уни­каль­ную ерун­ду, которой точ­но не будет на стра­нице, нап­ример OISJDflkj349sdkljf0304. Ког­да получишь ответ сер­вера, жми Ctrl-F и ищи свою стро­ку.

Воз­можно, при­дет­ся зак­рыть тег или вый­ти за рам­ки текуще­го тега, вно­ся изме­нения в HTML. Сто­ит об этом узнать заранее. Для это­го про­буй добавить в стро­ку спец­симво­лы: <, ', ", / и про­чие. В отве­те смот­ри, что про­изош­ло с ука­зан­ными сим­волами. Если видишь, что сим­волы прев­ратились в HTML-entity (&​lt; вмес­то <) или еще как‑то изме­нились, ско­рее все­го, на сер­вере есть филь­тры или санити­зация вво­да. Если экра­ниро­вания не про­изош­ло, ты на 99% нат­кнул­ся на уяз­вимость. Про­буй соб­рать рабочий пей­лоад.

Тес­тировать луч­ше все­го в Burp. Бра­узер может иска­жать вывод в инспек­торе объ­ектов или исходном коде стра­ницы. Нап­ример, открой игру XSS game area, которую в Google раз­работа­ли спе­циаль­но для изу­чения XSS. В пер­вой задаче отправь что‑нибудь вро­де <". При прос­мотре исходни­ка HTML в бра­узе­ре ты уви­дишь, что про­изош­ла под­мена на \&​lt;", и оши­боч­но решишь, что сайт безопа­сен. На самом деле под­мену выпол­нил бра­узер! Отправь <script>script>, что­бы уви­деть раз­ницу.

Разница в ответах браузера и Burp
Раз­ница в отве­тах бра­узе­ра и Burp

Ес­ли хочешь работать в бра­узе­ре, смот­ри вклад­ку «Сеть/Network». В сырых дан­ных response иска­жений нет.

Сырой ответ в браузере
Сы­рой ответ в бра­узе­ре

Что­бы соб­рать рабочий пей­лоад, вни­матель­но изу­чай HTML-код сай­та. Иног­да дос­таточ­но вста­вить <script>script>, иног­да нуж­но добавить обра­бот­ку события или что‑то еще. В при­мере с игрой Google задача реша­ется прос­то:

<script>alert("Pwnded")</script>

В дру­гих слу­чаях это не прой­дет. Смот­ри на то мес­то, где про­исхо­дит встав­ка, и думай, как мож­но внед­рить скрипт.

www

Соб­рать рабочий пей­лоад тебе помогут при­меры в чит­шите по XSS от PortSwigger.

Но вер­немся к при­меру с про­моко­дами. Иног­да раз­работ­чики добав­ляют про­моко­ды в фор­му через скры­тые поля. Нап­ример, так:

<input type="hidden" name="promo" value="012345" />

Как вылез­ти за рам­ки инпу­та и нахули­ганить?

Обыч­ная кавыч­ка (") может сло­мать раз­метку эле­мен­та: <input type="hidden" name="promo" value="012345"" />. Появи­лась висящая кавыч­ка. Бра­узер с ней спра­вит­ся и отбро­сит лиш­нее. Но теперь ты зна­ешь, что можешь управлять раз­меткой. Нап­ример, добавить событие для скры­того поля подоб­ным пей­лоадом:

" onfocus=alert(1) autofocus tabindex=1 accesskey="

Код фор­мы при­мет такой вид:

<input type="hidden" name="promo" value="" onfocus=alert(1) autofocus tabindex=1 accesskey="" />

По­лез­ная наг­рузка зак­рыва­ет кавыч­ку для value и добав­ляет обра­бот­ку события onfocus. При заг­рузке кон­тента поле пос­тара­ется получить фокус и сра­бота­ет событие. Свой­ство accesskey нуж­но, что­бы убрать висящую кавыч­ку и не нер­вировать бра­узер.

info

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

Каж­дый ресурс тре­бует инди­виду­аль­ного под­хода. В некото­рых слу­чаях script не прой­дет филь­тра­цию. Тог­да про­буй вари­ант через кар­тинку: "> <img onerror=alert(1) src="x. Сер­верный скрипт зак­роет висящую кавыч­ку и отдаст кор­рек­тный HTML. Бра­узер добавит кар­тинку и поп­робу­ет заг­рузить ее по адре­су x. Естес­твен­но, это поведе­ние вызовет ошиб­ку, и сра­бота­ет событие onerror, в котором заготов­лен наш код.

Байпас запуска функций

Для вызова алер­та я исполь­зовал alert(1). В клас­сичес­ком вари­анте при вызове фун­кции alert нуж­но ука­зывать стро­ку, то есть обер­нуть аргу­мент в кавыч­ки. Но филь­тры могут заметить инъ­екцию. Что­бы это­го избе­жать, есть мас­са вари­антов обхо­да. Вот нес­коль­ко при­меров, которые могут показать­ся неожи­дан­ными:

// Вызов без круглых скобок
alert`1`;
// Снова без кавычек
alert(/1/);
// Передаем массив
alert([1]);
// Обращение к объекту self как к массиву
self["al"+"ert"](1);
// То же, но через top
top["alert"](1);
// Прячем вызов за ASCII
self[String.fromCharCode(97,108,101,114,116)](1);
// То же, но через Base64
self[atob`YWxlcnQ=`](1);
// С использованием объекта globalThis
globalThis[[]+atob`YWxlcnQ=`](1);
// Функцией source объекта RegEx получаем строку alert
self[/alert/.source](1);

Ес­ли филь­тр работа­ет по сло­варю зап­рещен­ных слов, тег script на 100% будет заб­локиро­ван. В этом слу­чае попытай­ся понять, как имен­но про­исхо­дит филь­тра­ция. Ты можешь натол­кнуть­ся на филь­тр, который выпол­няет толь­ко одну замену. Нап­ример, отправь пей­лоад <scriptscript>. Сер­вер заменит пер­вое вхож­дение script, и оста­нет­ся нуж­ное тебе: <script>.

Иног­да филь­тр меня­ет все зна­чения, но дела­ет это в один про­ход. В этом слу­чае прос­кочит <scrscriptipt>. Филь­тр уда­лит script из цен­траль­ной час­ти, а край­ние час­ти сло­жат­ся в новый script.

Еще один спо­соб бай­паса — ран­дом‑кейс: ScRiPt. Подоб­ную запись не уви­дит филь­тр, который ищет точ­ное сов­падение.

Это малая часть вари­антов, ищи как мож­но боль­ше информа­ции про обман филь­тров и не стес­няй­ся кре­ати­вить при тес­тирова­нии. Может быть, при­дума­ешь что‑то, что еще никому не при­ходи­ло в голову!

Вла­дель­цы сай­тов ред­ко серь­езно вос­при­нима­ют reflected XSS, счи­тая это баловс­твом. Что может про­изой­ти, если поль­зователь выведет в сво­ем бра­узе­ре сооб­щение или строч­ку в кон­соль? Но при опре­делен­ных усло­виях отра­жен­ная XSS — мощ­ное ору­жие в руках зло­умыш­ленни­ка.

В 2015 году @mamyraimov написал твит о най­ден­ной уяз­вимос­ти на сай­те «Ламода». Это была обыч­ная reflected XSS в поис­ке по сай­ту. Но для ком­пании она нес­ла колос­саль­ные рис­ки. Дело в том, что тог­да в сети сво­бод­но гуляла сли­тая база покупа­телей «Ламоды». В базе были в том чис­ле имей­лы поль­зовате­лей. Еще не догадал­ся, в чем проб­лема?

Ис­поль­зуя XSS, хакеры мог­ли под­менить HTML-код стра­ницы и сфор­мировать фишин­говую фор­му или выпол­нить дру­гую ата­ку. Все, что оста­валось сде­лать, — про­вес­ти рас­сылку по готовой базе. Но уяз­вимость пофик­сили без лиш­него шума.

XSS в десктопных приложениях

С рос­том популяр­ности веб‑тех­нологий XSS выш­ли за рам­ки бра­узе­ра. Мно­гие кросс‑плат­формен­ные при­ложе­ния соз­даны на осно­ве тех­нологий вро­де Electron или Node.js, которые поз­воля­ют веб‑при­ложе­ниям работать нарав­не с обыч­ными прог­рамма­ми.

Ес­ли авто­ры такого при­ложе­ния не позабо­тились о филь­тра­ции вво­да и вывода, может всплыть XSS. В при­ложе­ниях на Electron ситу­ация ста­новит­ся кри­тичес­кой, если вклю­чена нас­трой­ка nodeIntegration. Эта нас­трой­ка дает дос­туп к Node.js API, что выводит при­ложе­ние из веб‑песоч­ницы сра­зу в ОС. Это пря­мой путь от XSS к RCE (Remote Code Execution). Для при­мера смот­ри ин­форма­цию о CVE-2020-16608 в прог­рамме Notable вер­сии 1.8.4.

Не думай, что этим гре­шат толь­ко мел­кие про­екты. Прог­раммис­ты из круп­ных и извес­тных ком­паний тоже час­то допус­кают такие оплошнос­ти. В 2020 году была най­дена огромная дыра в популяр­ном кор­поратив­ном мес­сен­дже­ре Slack. Slack дол­гое вре­мя работал с вклю­чен­ным nodeIntegration. Встро­енный рен­деринг поз­волял исполнить JavaScript — нап­ример, при отри­сов­ке превью для внеш­них ссы­лок. Цепоч­ка выпол­нения при­води­ла к тому, что хакер мог запус­кать любую коман­ду в опе­раци­онной сис­теме жер­твы. В резуль­тате хакеры получа­ли дос­туп к учет­ным дан­ным адми­нис­тра­торов и зах­ватыва­ли целые кор­поратив­ные сети.

Дру­гие популяр­ные дес­ктоп­ные при­ложе­ния, в которых находи­ли опас­ные XSS: Discord, Visual Studio Code, Skype, Signal Desktop, Everonet, Joplin. В популяр­ном мес­сен­дже­ре WhatsApp Desktop находи­ли мно­жес­тво XSS. Нап­ример, CVE-2019-11928.

Дес­ктоп­ные при­ложе­ния — это не толь­ко то, что запус­кает­ся на тво­ем компь­юте­ре. Тер­миналы оплат, киос­ки услуг и даже бан­коматы чаще все­го работа­ют на базе ПК или нет­топа. При­ложе­ния, в которые тыка­ет поль­зователь, час­то пред­став­ляют собой WebView (то есть встро­енный бра­узер). Если есть WebView, есть угро­за XSS.

За­щитить­ся от reflected XSS лег­ко: филь­труй любой поль­зователь­ский ввод. Любой! Даже если это скры­тые поля, которые генери­рует твой код. Экра­нируй спец­симво­лы или заменяй их HTML-entity. Если твой код испра­вит < на \&​lt;, у хакера поч­ти не оста­нет­ся шан­сов.

 

Хранимые XSS

Прос­той при­мер хра­нимой XSS — ком­мента­рии на сай­те. Пишешь обыч­ный ком­мента­рий, но в кон­це добав­ляешь <script>console.log("Pwnded")script>. Обновля­ешь стра­ницу, откры­ваешь кон­соль и видишь свое сооб­щение. Ком­мента­рий с вре­донос­ным пей­лоадом сох­ранил­ся в базе дан­ных. Отсю­да и наз­вание «хра­нимая», или stored. Твой код выпол­нится в бра­узе­ре каж­дого посети­теля, который зашел на стра­ницу.

Продолжение доступно только участникам

Материалы из последних выпусков становятся доступны по отдельности только через два месяца после публикации. Чтобы продолжить чтение, необходимо стать участником сообщества «Xakep.ru».

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

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

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

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

    Подписаться

  • Подписаться
    Уведомить о
    1 Комментарий
    Старые
    Новые Популярные
    Межтекстовые Отзывы
    Посмотреть все комментарии