Гостевая статья Побег из песочницы Chrome при помощи RIDL

Побег из песочницы Chrome при помощи RIDL

Уязвимости, дающие утечку кросс-процессной памяти, могут быть использованы для выхода из песочницы Chrome. Злоумышленнику по-прежнему необходимо скомпрометировать визуализацию до начала этой атаки. Для защиты от атак на уязвимые процессоры убедитесь, что ваш микрокод обновлен, и отключите гиперпоточность (HT).

В этой статье мы рассмотрим изолированную программную среду и, в частности, влияние и подобных аппаратных уязвимостей при использовании скомпрометированного средства визуализации. Механизм Chrome IPC Mojo основан на секретах для маршрутизации сообщений, и утечка этих секретов позволяет отправлять сообщения на привилегированные интерфейсы и выполнять действия, которые не должны выполняться средством визуализации. Мы будем использовать это для чтения произвольных локальных файлов, а также для запуска файла .bat вне песочницы в Windows. На момент написания статьи как Apple, так и Microsoft активно работали над исправлением, чтобы предотвратить эту атаку в сотрудничестве с командой безопасности Chrome.

Вот упрощенный обзор того, как выглядит модель процесса Chrome:
imageLikeEmbed.png

Процессы рендеринга находятся в отдельных песочницах, и доступ к ядру ограничен, например, с помощью фильтра seccomp в Linux или в Windows. Но для того, чтобы средство рендеринга сделало что-нибудь полезное, ему нужно пообщатса с другими процессами для выполнения различных действий. Например, чтобы загрузить изображение, необходимо попросить сетевой сервис получить его от его имени.

Механизм по умолчанию для межпроцессного взаимодействия в Chrome называется Mojo. Под капотом он поддерживает каналы сообщений/данных и разделяемую память, но вы обычно используете одну из языковых привязок более высокого уровня в C++, Java или JavaScript. То есть вы создаете интерфейс с методами на пользовательском языке определения интерфейса (IDL), Mojo создает для вас заглушки на выбранном вами языке, и вы просто реализуете эту функциональность. Чтобы увидеть, как это выглядит на практике, вы можете проверить URLLoaderFactory в , и .

Одна примечательная особенность заключается в том, что Mojo позволяет вам пересылать конечные точки IPC по существующему каналу. Это широко используется в кодовой базе Chrome, т. е. всякий раз, когда вы видите параметр pending_receiver или pending_remote в файле .mojom.

imageLikeEmbed (1).png

Под капотом Mojo использует канал сообщений для конкретной платформы между процессами или, более конкретно, между узлами в Mojo. Два узла могут быть связаны напрямую друг с другом, но это не обязательно, так как Mojo поддерживает маршрутизацию сообщений. Один узел в сети называется брокерским узлом, который имеет некоторые дополнительные обязанности по настройке каналов узла и выполнению некоторых действий, ограниченных песочницей.


Сами оконечные точки IPC называются портами . В приведенном выше примере URLLoaderFactory и клиент, и сторона реализации идентифицируются портом. В коде, порт выглядит как :
Код:
class Port : public base::RefCountedThreadSafe<Port> {
public:
  // [...]
  // Текущее состояние порта.
  State state;
   // Адрес узла и порта, на который следует направлять события ОТ этого порта.
  // Обратите внимание, что это НЕ обязательно адрес порта, который в данный момент отправляет
  // события в этот порт.
  NodeName peer_node_name;
  PortName peer_port_name;
  // Следующий доступный порядковый номер для использования в исходящих событиях сообщений пользователя
  // исходящий из этого порта.
  uint64_t next_sequence_num_to_send;
  // [...]
}

Указанные выше peer_node_name и peer_port_name являются 128-битными случайными целыми числами, используемыми для адресации. Если вы отправляете сообщение в порт, он сначала перенаправляет его на нужный узел, а принимающий узел ищет имя порта на карте локальных портов и помещает сообщение в нужную очередь сообщений.

Конечно, это означает, что если у вас есть уязвимость утечки информации в процессе браузера, вы можете слить имена портов и использовать их для внедрения сообщений в привилегированные каналы IPC. И на самом деле, на это ссылаютса в разделе основной документации Mojo:
«[...] любой узел может отправлять любое сообщение на любой порт любого другого узла, если он знает имена портов и узлов. [...] Поэтому важно не просачивать имена портов в узлы, которым не должна быть предоставлена соответствующая возможность ».
Хорошим примером ошибки, которая может быть легко использована для утечки номеров портов, был от . Это было целочисленное переполнение в реализации которое позволило вам прочитать произвольный объем памяти кучи перед BLOB-объектом в процессе браузера. Тогда эксплойт будет выглядеть примерно так:
  1. Компромитация рендерера.
  2. Используйте ошибку blob для утечки кучи памяти.
  3. Поиск в памяти портов (действительное состояние + 16 старших байтов энтропии).
  4. Используйте пропущенные порты, чтобы внедрить сообщение в привилегированное соединение IPC.
Далее мы рассмотрим две вещи. Как заменить вышеприведенные шаги 2. и 3. на ошибку процессора и какие примитивы мы можем получить через привилегированные соединения IPC.
RIDL
Чтобы использовать это сценарий с аппаратной уязвимостью, я искал ошибку, которая позволяет утечке памяти через границу процесса. RIDL от кажется идеальным кандидатом, поскольку обещает именно это: он позволяет вам пропускать данные из различных внутренних буферов на задействованных ЦП. Для получения подробной информации о том, как это работает, посмотрите или поскольку они объясняют это гораздо лучше, чем я.


Были выпущены обновления микрокода и ОС для защиты от атак MDS. Тем не менее, если вы прочтете по этой теме, вы заметите, что меры по смягчению очищают затронутые буферы при переходе в менее привилегированный контекст выполнения. Если ваш процессор поддерживает гиперпоточность, вы все равно сможете пропускать данные из второго потока, работающего на вашем физическом ядре. Для решения этой проблемы либо отключить гиперпоточность, либо реализовать планировщик группы.

Вы можете найти multiple PoCs for в Интернете, некоторые из них уже доступны с мая 2019 г. PoC представлены с различными свойствами:
  • Они ориентированы либо на loads либо на stores .
  • Некоторые требуют, чтобы секрет был сброшен из кэша L1.
  • Вы можете управлять индексом в строке 64-байтового кэша, чтобы получить утечку из 64-битного значения из предыдущего доступа.
  • Скорость сильно варьируется в зависимости от варианта и эксплойта. Самый высокий отчет, который я видел, Брэндона Фалька с с 228 КБ / с. Для сравнения, наивный эксплойт на моей машине достигает только 25 КБ / с.
Единственное свойство, которое разделяют все варианты, заключается в том, что они вероятностны в том, что утекло. В то время как в описываются некоторые примитивы синхронизации для целевых значений, обычно требуется запускать повторный доступ к секрету, чтобы полностью его утечь.

Я закончил тем, что написал два эксплойта для Chrome, используя разные варианты MDS, один из которых предназначен для сборки Linux на Xeon Gold 6154, а другой для Windows на Core i7-7600U. Я опишу оба, так как они в итоге ставили разные задачи при применении их на практике.

Микроархитектурная выборка данных Fill Buffer (MFBDS)

Моим первым эксплойтом было использование MFBDS, который предназначается для буфера заполнения строки ЦП. PoC очень прост:
Код:
xbegin out            ; start TSX to catch segfault
mov   rax, [0]        ; read from page 0 => leaks a value from line fill buffer
; the rest will only execute speculatively
and   rax, 0xff       ; mask out one byte
shl   rax, 0xc        ; use as page index
add   rax, 0x13370000 ; add address of probe array
prefetchnta [rax]     ; access into probe array
xend
out: nop

После этого вы получите временный доступ к массиву зондов, чтобы увидеть, какой индекс был кэширован.
Вы можете изменить 0 в начале, чтобы контролировать смещение в строке кэша для вашей утечки. Кроме того, вы захотите реализовать префиксный или суффиксный фильтр для пропущенного значения, как описано в . Обратите внимание, что это приводит только к утечке значений, которых нет в кеше L1, поэтому вам нужно иметь способ извлечь секрет из кеша между доступами.


Для моей первой цели утечки я выбрал привилегированную . Как упомянуто выше, URLLoaderFactory используется средством визуализации для выборки сетевых ресурсов. Он будет применять ту же политику происхождения (фактически на том же сайте) для вашего средства визуализации, чтобы убедиться, что вы не можете нарушить ограничения веб-платформы. Тем не менее, процесс браузера также использует URLLoaderFactories для различных целей, и те имеют дополнительные привилегии. Помимо игнорирования политики одного и того же происхождения, им также разрешено загружать локальные файлы. Таким образом, если мы можем слить одно из имен их портов, мы можем использовать его для загрузки
/etc/passwd на .


Следующим шагом будет запуск повторного доступа к имени порта привилегированного загрузчика. Заставить процесс браузера делать сетевые запросы может быть одним из вариантов, но, похоже, слишком много ресурсов затрачиваетса. Вместо этого я решил настроить поиск портов в узле.
Код:
class COMPONENT_EXPORT(MOJO_CORE_PORTS) Node {
  // [...]
  std::unordered_map<LocalPortName, scoped_refptr<Port>> ports_;
  // [...]
}
Каждый узел имеет хеш-карту, в которой хранятся все . Если мы отправим сообщение на несуществующий порт, целевой узел найдет на карте, увидит, что оно не существует, и отбросит сообщение. Если имя нашего порта окажется в том же сегменте хэша, что и другое имя порта, он будет неизвестного порта, чтобы сравнить его с ним. Это также загрузит само имя порта в кеш, поскольку он обычно хранится в той же строке кеша, что и хеш. MFBDS позволяет нам пропускать всю строку кэша, даже если значение не было получено напрямую.

Карта начинается с размера сегмента примерно 700 на свежем экземпляре Chrome и растет в основном с количеством визуализаторов. Это делает атаку неосуществимой, поскольку нам придется грубо форсировать как индекс сегмента, так и смещение строки кэша (1 в 4 благодаря выравниванию). Однако я заметил путь к коду, который позволяет вам создавать большое количество привилегированных URLLoaderFactories с помощью сервисных работников. Если вы создадите сервисного воркера с включенной , каждая навигация верхнего уровня . Просто создав несколько фреймов и остановив запросы на стороне сервера, вы можете одновременно поддерживать несколько тысяч загрузчиков и значительно упростить грубую силу.

Единственное, чего не хватает, - это удалить целевое значение из кэша L1. Кажется, простое заполнение наших сообщений 32 КБ данных на практике помогает, так как я предполагаю, что данные будут загружены в кэш L1 жертвы и изгнать все остальное.
Подводя итог всему эксплойту:
  1. Компрометация рендерера.
  2. Запустите эксплойт RIDL в процессах $ NUM_CPU-1 с различными смещениями строк кэша.
  3. Установите сервисный работник с предварительной загрузкой навигации.
  4. Создавайте множество фреймов и блокируйте их запросы.
  5. Отправлять сообщения в сетевой процесс со случайными именами портов.
  6. Если мы столкнемся с индексом сегмента, процесс в 2. может привести к утечке имени порта.
  7. Поддать сообщение в URLLoaderFactory для загрузки локальных файлов на .
TSX асинхронный прерывание (TAA)

В ноябре 2019 года были выпущены новые варианты MDS-атак, и, поскольку TAA PoC оказался быстрее, чем мой эксплойт MFBDS, я решил адаптировать его к эксплойту Chrome. Кроме того, VUSec выпустил эксплойт, предназначенный для операций хранилища, который должен позволить нам избавиться от требования очистки кеша, если мы сможем получить секрет для записи по разным адресам в памяти. Это должно произойти, если мы можем запустить браузер для отправки сообщения на привилегированный порт. В этом сценарии к имени секретного порта также будет добавляться имя узла, и мы можем использовать методы из бумаги RIDL для легкой фильтрации по нему.

Я также начал искать лучший примитив и обнаружил, что если я смогу пообщатса с , это позволит мне создать новый и тем самым выбрать к базы данных sqlite3, в которой хранятся файлы cookie.

Чтобы выяснить, как инициировать сообщения из процесса браузера в NetworkService, я посмотрел на методы IPC в интерфейсе, чтобы найти способ, который выглядит так, как будто я могу влиять на него из средства визуализации. попался на глаза, и на самом деле этот метод вызывается каждый раз, когда обновляется соединение WebRTC. Вам просто нужно создать поддельное соединение WebRTC, и каждый раз, когда вы помечаете его как подключенное / отключенное, оно вызывает новое сообщение для NetworkService.
imageLikeEmbed (2).png

Как только мы сливаем имя порта из скомпрометированного средства визуализации, мы получим примитив для написания базы данных sqlite3 с полностью контролируемым путем.

Хотя поначалу это звучало не очень полезно, на самом деле вы можете использовать его, чтобы добиться выполнения кода. Я заметил, что пакетные файлы Windows - очень простительный формат файлов. Если у вас есть мусор в начале файла, он пропустит его до следующего «\ r \ n» и выполнит оттуда следующую команду. В своем эксплойте я использую его для создания файла cookies.bat в каталоге автозапуска пользователя, добавления cookie с «\ r \ n» и командой в нем, и он будет выполнен при следующем входе в систему.

В конце концов, этот эксплойт работал в среднем за 1-2 минуты и последовательно работал менее чем за 5 минут на моей машине. И я уверен, что это может быть значительно улучшено, так как я видел много ускорений от небольших изменений и различных методов. Например, MLPDS кажется даже быстрее на практике, чем вариант, который я использую.

Резюме эксплойта:
  1. Компромитация рендерера.
  2. Запустите эксплойт RIDL в процессах $ NUM_CPU-1 с различными смещениями строк кэша.
  3. Создайте поддельное соединение WebRTC и переключайтесь между подключенным и отключенным.
  4. Утечка имени порта NetworkService.
  5. Создайте новый NetworkContext с файлом cookie по адресу c: \ path \ to \ user \ autorun \ cookies.bat
  6. Вставьте файл cookie «\ r \ ncalc.exe \ r \ n».
  7. Дождитесь следующего входа.
Резюме
Когда я начал работать над этим, я был удивлен тем, что он все еще может использоваться, хотя уязвимости были публичны некоторое время. Если вы прочтете руководство по этой теме, они, как правило, расскажут о том, как были устранены эти уязвимости, если ваша ОС обновлена, с указанием, что вам следует отключить гиперпоточность, чтобы полностью защитить себя. Сосредоточение внимания на митигации определенно дало мне ложное представление о том, что уязвимости устранены, и я думаю, что в этих статьях можно было бы более четко понять, как оставить включенным гиперпоточность.

При этом я хотел бы, чтобы вы убрали две вещи из этого поста. Во-первых, ошибки утечки информации могут быть не просто обходом ASLR. Даже если бы не зависимость от секретных имен портов, были бы утечки других интересных данных, например UnguessableTokens Chrome, куки Gmail или конфиденциальные данные в других процессах на машине. Если у вас есть идея, как найти утечки информации в масштабе, Chrome может быть хорошей целью.

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

Источник:
 
Мы в соцсетях:

Обучение наступательной кибербезопасности в игровой форме. Начать игру!