Вы слышали о микроархитектурных атаках, да? Вы знаете такие вещи, как Rowhammer, обход ASLR, атаки с адресацией DRAM и так далее? Отличная статья Шварца и др. — обзор Адриана Кольера здесь — описывает, как можно защититься от этих атак, исходящих через JavaScript. Что в основном переводится как предотвращение взлома любого случайного веб-сайта 🙌.

Особенно меня поразило то, как можно использовать JavaScript для идентификации адресов памяти и как JavaScript Zero (решение, описанное в статье) побеждает его. Особенность JavaScript в том, что его песочница гарантирует, что виртуальные адреса никогда не раскрываются пользователю. Тем не менее,

  1. В JavaScript есть ArrayBuffers — блок виртуальной памяти, доступный пользователю напрямую быстрым и эффективным способом.
  2. Браузеры выделяют ArrayBuffers в соответствии со страницей (в начале новой физической страницы и с младшими 12 битами, установленными на «0»).
  3. Браузеры используют mmap для выделения больших блоков памяти.
  4. mmap оптимизирован для выделения прозрачных огромных страниц (THP) размером 2 МБ.

Вот где начинается атака по сторонним каналам. Физические страницы в THP отображаются по запросу (когда происходит первый доступ). Таким образом, когда вы перебираете индексы массива в своем ArrayBuffer, когда вы доберетесь до первой новой физической страницы, возникнет ошибка страницы, и доступ к базовым данным займет гораздо больше времени (это не просто доступ к памяти , понимаете?). Итак, вы — или, точнее, злоумышленник — теперь знаете индекс, с которого начинается новая страница, страница, у которой 21 младший значащий бит установлен в «0».

Поскольку эти физические страницы отображаются по запросу, т. е. как только происходит первый доступ к странице, итерация по индексам массива приводит к ошибкам страницы в начале новой страницы. Время устранения страничной ошибки значительно выше, чем при обычном доступе к памяти. Таким образом, злоумышленнику известен индекс, с которого начинается новая 2-мегабайтная страница. В этом индексе массива базовая физическая страница имеет 21 младший значащий бит, установленный на «0».

Прикольно, не правда ли? И одна из тех вещей, о которых нужно быть действительно подлым педерастом, чтобы вообще додуматься 😮.

И здесь мы подходим к Javascript Zero и предотвращению атак по сторонним каналам с помощью ArrayBuffers. Решение, оказывается, такое же простое (и не такое хитрое). Особенно

  1. Buffer ASLR: когда вы запрашиваете ArrayBuffer, запрос добавляет дополнительные 4 КБ. Массив, который вы возвращаете, случайным образом расположен внутри того массива «размер + 4 КБ», который был фактически выделен. Это не позволяет злоумышленникам предположить, что массив начинается с новой физической страницы.
  2. Предварительная загрузка. Когда массив выделен, система выполняет итерацию по нему, вызывая все отказы страниц. Таким образом, пользователь не может инициировать отказы страниц, перебирая массив (он уже загружен в память!), и находя границы страниц.
  3. Недетерминизм: установщик массива изменен таким образом, что каждый доступ также включает доступ к случайному местоположению в том же массиве. Таким образом, злоумышленник не может узнать фактический индекс, в котором произошла ошибка страницы (это не позволяет пользователю просто ждать, пока предварительно загруженные страницы будут выгружены).

Есть еще много чего, включая рандомизацию индекса массива, манипулирование временными метками, обновления многопоточности и многое другое, но вышеизложенное должно служить объяснением того, как атаки работают, и как некоторые из атак можно предотвратить.
Производительность также на удивление высока, в основном потому, что JIT постоянно оптимизируют этот материал. Суть в том, что, по большому счету, люди понятия не имели, что это работает в их браузерах.

Прочтите бумагу для всего, или резюме Адриана Кольера для краткого изложения.