ZombieLoad

Как я читал академический текст или борьба с терминами

Что такое ZombieLoad

Исследователи из Грацского университета имени Карла и Франца в Австрии обнаружили уязвимость в процессорах Intel и назвали её ZombieLoad.
ZombieLoad – одна из трёх уязвимостей категории MDS (Microarchitectural Data Sampling). MDS может помочь злоумышленникам получить доступ к данным, к которым недавно обращался процессор. В MDS также входят уязвимости RIDL и Fallout.

Утверждается, что ZombieLoad может вызвать атаку типа Meltdown без обязательного вызова архитектурного сбоя (architectural exception). ZombieLoad`овские эксплойты загружают инструкции, которые должны работать с обновленными значениями, однако инструкции могут временно вычислять устаревшие значения, которые относились к предыдущим операциям с памятью с текущего или родственного потока.

Существует вариант ZombieLoad атаки, который провоцирует утечку данных даже на самых последних процессорах Intel, которые были заявлены защищенными от Meltdown, Foreshadow, RIDL и Fallout атак.

Чем ZombieLoad отличается от Meltdown и Spectre

Существуют довольно известные уязвимости, а именно Meltdown (программная уязвимость) и Spectre (архитектурная уязвимость).
Здесь также злоумышленник может получить доступ к данным, но только из кэша самого процессора. ZombieLoad в свою очередь позволяет получить данные, которые находятся во внутренних буферах процессора: буфер хранения и буфер заполнения.

Что нужно знать для понимания уязвимости

Out-of-order

Out-of-order – возможность процессора выполнять инструкции в различных потоках без создания оных программистом.

Приведу пример.
Здесь инструкция на строке 1 зависит от результата выполнения инструкции на строке 0 (r1). Однако инструкция на строке 2 не зависит от результатов выполнения предыдущих и ей не нужно ждать, когда они выполнятся, поэтому она выполняется одновременно с предыдущими.

0: mul r1, r2, r3
1: add r4, r1, 5
2: add r6, r2, 1 

Спекулятивное выполнение

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

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

Подсистема памяти

Кэши

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

Виртуальная память

Процессор использует виртуальную память, чтобы изолировать память между процессами. Адреса виртуальной памяти транслируются в адреса физической памяти. TLB (Translation-look-aside buffer) – специализированный кэш процессора, который ускоряет эту операцию. Он содержит недавно использованные пары/соответствия виртуальных и физических адресов.

Memory Order Buffer

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

Загрузка данных

При любой загрузке данных выделяется место в буфере загрузки. Для определения физического адреса верхние 36 бит линейного адреса транслируются блоком управления памятью (MMU). Младшие же 12 бит нужны для индексации кэша L1D. Если трансляция (пара: виртуальный адрес - физический адрес) находится в TLB, то физический адрес становится доступен незамедлительно. В противном случае с помощью обработчика промахов (page miss handler – PMH) выполняется обход таблицы страниц, чтобы успешно транслировать адреса и получить соответствующие биты разрешений. Если запрашиваемые данные находятся в L1D (то есть, попали в кэш), то операцию загрузки можно прекращать. В противном случае необходимо передавать данные с более высоких уровней кэша или даже с основной памяти через буфер заполнения. Буфер заполнения служит интерфейсом для других кэшей и основной памяти.

В случае ошибки, например, если физический адрес недоступен, переход по таблице страниц немедленно не прерывается. Инструкция загрузки должна пройти каждый этап конвейера и будет его проходить, пока неисправность не будет устранена. Лишь только после устранения неисправности конвейер (pipeline) очищается.

Расширения процессора

Микрокод

Для поддержки сложных (комплексных) инструкций процессора используется микрокод. Микрокод позволяет реализовывать более высокоуровневые инструкции, используя несколько низкоуровневых (на уровне hardware) инструкций. Предпочтительно добавлять новые архитектурные особенности именно через реализацию их с помощью микрокода. Например, Intel SGX.

Intel SGX

Вместе с процессорами на микроархитектуре Skylake Intel представили SGX (Software Guard Extension). Это набор инструкций для изолирования кода. SGX исполняет код внутри, так называемых, анклавов (enclaves), которые отображаются в виртуальном адресном пространстве процесса приложения, но изолированы от остальной системы оборудованием физически.

Подразумевается, что операционная система и все запущенные приложения могут быть скомпрометированы и, следовательно, им нельзя доверять. Любые попытки достучаться до памяти анклава в режиме, отличном от анклава, в результате вернут фиктивное значение 0xff. Также SGX, чтобы защититься от физических атак по шине, шифрует используемую область памяти.

Intel TSX

Вместе с процессорами на микроархитектуре Haswell Intel представили TSX (Transactional Synchronization Extensions). Это набор инструкций, с помощью которых определенные области кода выполняются транзакцией.
Если все области кода успешно выполняются, то все операции внутри транзакции считаются атомарными и другие логические процессы принимают эти операции за одну.
Если во время транзакции происходит ошибка, то транзакция прерывается и исполнение откатывается к состоянию до транзакции, а все выполненные операции отменяются.

Прерывания транзакции может быть вызвано считыванием или записыванием данных другим логическим процессором с адреса, который был изменен в рамках транзакции.
Кроме того, объем считанных и записанных данных в рамках транзакции не может превышать размер LLC и L1 соответственно. Некоторые инструкции или системное событие также могут привести к прерыванию транзакции.

Описание атаки

ZombieLoad – это атака со временным выполнением. ZombieLoad отслеживает значения из памяти, которая загружает и сохраняет их в текущем ядре процессора. Буфер заполнения используется всеми логическими процессорами ядра и не различает процессы и привилегии. Этим ZombieLoad и пользуется.

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

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

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

Однако, сравнивая с Meltdown, нельзя выбрать определенное значение для утечки. ZombieLoad просто-напросто вытаскивает любое значение, которое загрузилось или уже хранится в ядре процессора.

Для ZombieLoad неважны границы процессов и атака не ограничивается какой-либо конкретной. Данные могут утекать из приложения в user-space, из ядра, из SGX, из виртуальной машины и из гипервизора. Каждый из сценариев будет описан в следующем разделе.

Meltdown может вытаскивать данные только из адресного пространства злоумышленника.
Foreshadow сфокусирован только на SGX.
Foreshadow-NG к уже существующей утечке в предыдущем поколении добавил межпроцессную утечку и утечку между виртуальными машинами.
В Fallout утекать данные могут только в пределах одного логического ядра.

Следовательно, ZombieLoad – очень мощная и опасная атака.

Сценарии и модель атаки

Утечка из пользовательского пространства

В сценарии с межпроцессным взаимодействием внутри user-space непривилегированный злоумышленник получает данные, загруженные или сохраненные другим одновременно запущенным приложением. Злоумышленник находится вместе с жертвой на одном физическом, но на другом логическом ядре процессора, что естественно для гиперпоточности.

Утечка из ядра операционной системы

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

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

Утечка из Intel SGX

Утечка также может происходить внутри анклава Intel SGX, даже если злоумышленник нацелен на зашифрованную область памяти (кэш страниц анклава). Код злоумышленник исполняется за пределами анклава SGX на родственном логическом ядре процессора. В отличие от утечки с ядром операционной системы, утечка на том же логическом ядре после выхода злоумышленника из анклава не наблюдается.

В рамках SGX злоумышленник может, например, изменять записи таблицы страниц или точно выполнять анклавы жертвы, но не более, чем по одной инструкции за раз.

Утечка из виртуальной машины

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

Утечка из гипервизора

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

Контрмеры

Первое, что может прийти в голову, дабы избежать ZombieLoad – отключить SMT (Simultaneous Multithreading или же Hyperthreading). Гиперпоточность при определенных нагрузках позволяет работать процессору на 30-40% быстрее. Отключение данной функции нанесёт серьезный урон производительности.

Алгоритмы совместного планирования

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

Очистка кэшей

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

Инструкции, которые очищают L1 кэш, очищают его не полностью, но оставляют часть “грязных” линий кэша. И всё из-за обновления микрокода, которое затронуло редко используемую функцию VERW, проверяющую сегмент на запись, и породило side-effect для неё. Следовательно, эти “грязные линии” могут пройти через буфер заполнения и в кэш второго уровня (L2).

Чтобы полностью избавиться от утечки, можно дополнительно при каждой смене контекста полностью очищать L1 кэш, но как показывают эксперименты, очистка только L1 кэша занимает 1070 циклов процессора, что является довольно серьезным ударом по производительности.

Фильтрация инструкций

Для предотвращения атаки ZombieLoad внутри одного процесса, нужно убедиться, что условия для запуска ZombieLoad невозможны. Например, можно предотвратить генерацию и выполнение определенных инструкций, которые являются важной частью атаки.

Обмен секретными ключами

В рамках программного обеспечения, мы всегда можем полагаться на методы секретного обмена, которые используются для защиты от физических атак по сторонним (непрямым) каналам. Можно гарантировать, что секретный ключ никогда не загружается напрямую из памяти, но объединяется с данными в регистрах перед использованием. Следовательно, наблюдение за данными нагрузки не раскрывает секретный ключ. Для успешной атаки злоумышленник должен раскрыть все секретные значения.

Вывод

Итак, ZombieLoad – относительно новая атака типа Meltdown, нацеленная на заполнение буферов процессора. ZombieLoad позволяет злоумышленнику получать данные, недавно загруженные текущим или родственным логическим ядром процессора. ZombieLoad допускает утечку через процессы пользовательского пространства, анклавы SGX, виртуальные машины и гипервизоры.

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

Лицензия Creative Commons