Вы слышали о микроархитектурных атаках, да? Вы знаете такие вещи, как Rowhammer, обход ASLR, атаки с адресацией DRAM и так далее? Отличная статья Шварца и др. — обзор Адриана Кольера здесь — описывает, как можно защититься от этих атак, исходящих через JavaScript. Что в основном переводится как предотвращение взлома любого случайного веб-сайта 🙌.
Особенно меня поразило то, как можно использовать JavaScript для идентификации адресов памяти и как JavaScript Zero (решение, описанное в статье) побеждает его. Особенность JavaScript в том, что его песочница гарантирует, что виртуальные адреса никогда не раскрываются пользователю. Тем не менее,
- В JavaScript есть ArrayBuffers — блок виртуальной памяти, доступный пользователю напрямую быстрым и эффективным способом.
- Браузеры выделяют ArrayBuffers в соответствии со страницей (в начале новой физической страницы и с младшими 12 битами, установленными на «0»).
- Браузеры используют mmap для выделения больших блоков памяти.
- mmap оптимизирован для выделения прозрачных огромных страниц (THP) размером 2 МБ.
Вот где начинается атака по сторонним каналам. Физические страницы в THP отображаются по запросу (когда происходит первый доступ). Таким образом, когда вы перебираете индексы массива в своем ArrayBuffer, когда вы доберетесь до первой новой физической страницы, возникнет ошибка страницы, и доступ к базовым данным займет гораздо больше времени (это не просто доступ к памяти , понимаете?). Итак, вы — или, точнее, злоумышленник — теперь знаете индекс, с которого начинается новая страница, страница, у которой 21 младший значащий бит установлен в «0».
Поскольку эти физические страницы отображаются по запросу, т. е. как только происходит первый доступ к странице, итерация по индексам массива приводит к ошибкам страницы в начале новой страницы. Время устранения страничной ошибки значительно выше, чем при обычном доступе к памяти. Таким образом, злоумышленнику известен индекс, с которого начинается новая 2-мегабайтная страница. В этом индексе массива базовая физическая страница имеет 21 младший значащий бит, установленный на «0».
Прикольно, не правда ли? И одна из тех вещей, о которых нужно быть действительно подлым педерастом, чтобы вообще додуматься 😮.
И здесь мы подходим к Javascript Zero и предотвращению атак по сторонним каналам с помощью ArrayBuffers. Решение, оказывается, такое же простое (и не такое хитрое). Особенно
- Buffer ASLR: когда вы запрашиваете ArrayBuffer, запрос добавляет дополнительные 4 КБ. Массив, который вы возвращаете, случайным образом расположен внутри того массива «размер + 4 КБ», который был фактически выделен. Это не позволяет злоумышленникам предположить, что массив начинается с новой физической страницы.
- Предварительная загрузка. Когда массив выделен, система выполняет итерацию по нему, вызывая все отказы страниц. Таким образом, пользователь не может инициировать отказы страниц, перебирая массив (он уже загружен в память!), и находя границы страниц.
- Недетерминизм: установщик массива изменен таким образом, что каждый доступ также включает доступ к случайному местоположению в том же массиве. Таким образом, злоумышленник не может узнать фактический индекс, в котором произошла ошибка страницы (это не позволяет пользователю просто ждать, пока предварительно загруженные страницы будут выгружены).
Есть еще много чего, включая рандомизацию индекса массива, манипулирование временными метками, обновления многопоточности и многое другое, но вышеизложенное должно служить объяснением того, как атаки работают, и как некоторые из атак можно предотвратить.
Производительность также на удивление высока, в основном потому, что JIT постоянно оптимизируют этот материал. Суть в том, что, по большому счету, люди понятия не имели, что это работает в их браузерах.
Прочтите бумагу для всего, или резюме Адриана Кольера для краткого изложения.