Привет, Хабр! На связи Антон Белоусов и Алексей Вишняков, и мы продолжаем вместе с вами изучать буткиты — наиболее опасный класс вредоносного ПО. Гонка вооружений между разработчиками решений в области ИБ и вирусописателями не останавливается ни на секунду: первые активно внедряют новые механизмы противодействия киберугрозам, а вторые — инструменты их обхода. Как раз с точки зрения безопасности нас заинтересовал современный стандарт предзагрузки операционных систем UEFI. Поэтому в этом посте мы решили:
разобраться, чем загрузка в режиме UEFI отличается от загрузки в режиме Legacy BIOS;
рассказать о новых экземплярах буткитов, нацеленных на компрометацию UEFI;
выяснить, похожи ли они на своих предшественников (обширную группу так называемых legacy-буткитов мы подробно изучили в прошлый раз);
рассмотреть используемые злоумышленниками техники и слабые места систем на платформе UEFI.
Кто не хочет читать много букв, может ознакомиться с этой же информацией в формате вебинара. Такой же уровень полезности гарантируем!
Буткиты бывают двух типов. Первые заточены на более ранние платформы, так называемые Legacy BIOS. Вторые адаптированы под современные решения, которые имеют универсальный интерфейс UEFI для связи ОС с программами, которые управляют низкоуровневыми функциями устройств (далее — UEFI, прошивка).
Несмотря на более современный подход к обеспечению безопасности UEFI, в коде его прошивок регулярно обнаруживают уязвимости. Например, в феврале этого года наши коллеги из компании Binarly выявили более двух десятков брешей в UEFI, которые затрагивают миллионы устройств от крупнейших производителей. Злоумышленники, конечно же, не дремлют и стараются их активно эксплуатировать. Поэтому актуальность исследования защищенности платформы только растет. Большинство буткитов универсальны. Например, ESPecter и FinSpy умеют заражать как старые, так и новые платформы. Одним из недавно обнаруженных UEFI-буткитов стал MoonBounce.
Архитектура UEFI
UEFI (Unified Extensible Firmware Interface, единый интерфейс расширяемой прошивки) — это небольшая операционная система, которая начинает работать при включении компьютера. Она пришла на замену устаревшей модели BIOS. UEFI позволяет унифицировать процесс разработки и создавать отдельные модули, а не только прошивку целиком. Фреймворк имеет хорошо читаемый и документированный код на C. Многие пользователи отмечают удобный интерфейс, который разрешает пользоваться мышью. Среди других преимуществ платформы — поддержка сети «из коробки» и возможность работать с дисками объемом более 2 ТБ. Все это позволяет ускорить процесс разработки и сделать его безопаснее.
По требованиям платформы диск должен быть размечен в формате GPT. Соответственно, длина адреса блока будет равна 64 битам. Опираясь на эти данные, давайте вычислим гипотетический размер диска, который можно адресовать с помощью этого формата. Если использовать сектор размером 512 байтов, получим более 9 зеттабайт.
Код для инициализации оборудования, в том числе код для загрузки ОС, разумеется, важный элемент платформы, однако наибольший интерес для нас представляет интерфейс, который позволяет взаимодействовать с UEFI.
Приложения и драйвера UEFI имеют две таблицы для работы с API-интерфейсом — EFI Boot Services Table (службы доступны в процессе загрузки) и EFI Runtime Services Table (службы доступны и в процессе загрузки, и в процессе работы ядра ОС). API-интерфейс необходим, чтобы взаимодействовать с оборудованием, выделять память и выполнять другие простые функции по аналогии с WinAPI.
Как мы уже говорили ранее, прошивку UEFI можно дополнять модулями. Утилита UEFI Tool позволяет парсить файлы прошивок различных вендоров. Например, файл прошивки Asus содержит множество драйверов и модулей. С помощью этой утилиты в процессе разработки можно менять, удалять или добавлять модули в зависимости от случая.
Напомним схему цепочки программ в Legacy BIOS (ее мы рассматривали в прошлой статье).
При переходе к UEFI такие компоненты, как MBR и VBR, исчезают, и их функции частично берут на себя другие модули.
В UEFI процессор тоже стартует в реальном режиме, но переключение происходит на раннем этапе исполнения кода прошивки. За счет этого компоненты, которые находятся в файловой системе и затрагивают ОС, сразу работают в защищенном режиме.
Код UEFI достаточно сложный. Он включает семь стадий работы прошивки: Security, Pre-EFI Initialization, Driver Execution Environment, Boot Device Selection, Transient System Load, Runtime и Afterlife. Что интересно, работа на стадии SEC происходит во временной памяти, формирующейся из кэша процессора. На этом уровне прошивка становится корнем доверия всей системы.
На стадии DXE выполняются DXE-драйверы для устройств. Именно на данном этапе злоумышленникам удобнее всего встраивать вредоносные сущности в полезную нагрузку. BDS — стадия выбора загрузочного устройства, которое отвечает за запуск ОС. Runtime — стадия работы ОС. Afterlife — стадия завершения работы компьютера.
NVRAM-переменные помогают UEFI понимать, с какого устройства требуется загрузиться. Они хранятся в энергонезависимой памяти. Есть стандартные переменные и те, которые определяют разработчики платформ. Например, переменные db и dbx используются Secure Boot, они содержат базу данных с ключами и хешами.
Нам интересна переменная BootOrder, определяющая порядок загружаемых устройств, и переменная Boot#### с номером устройства.
Если рассмотреть дамп переменной Boot004 (она относится к первому устройству в списке BootOrder), можно обнаружить путь к менеджеру загрузки bootmgfw.efi. Так переменная указывает прошивке, где искать менеджер загрузки. В переменной также есть отсылка в виде GUID на хранилище параметров загрузки BCD (Boot Configuration Data). В зависимости от системы важные данные можно сохранять непосредственно в NVRAM-область.
Ниже представлена схема диска, размеченного с помощью GPT. В нулевом секторе расположен Protective MBR. Его формат предполагает всего один раздел, описывающий весь диск. Намеренно указывается специальный диск этого раздела: это необходимо, чтобы компьютер без поддержки UEFI мог распознать диск формата GPT и случайно не затер его.
В первом секторе размещен заголовок. Он содержит параметры диска, разметку и адреса смещения данных.
За первым сектором следует таблица разделов. В ней 128 элементов, каждый из которых описывает определенный логический раздел на диске.
Больше всего интересен первый элемент — type (член структуры, описывающей раздел), который позволяет найти раздел EFI System Partition. Там расположен менеджер загрузки.
Раздел EFI System Partition имеет структуру формата FAT32, содержит таблицы кластеров, адрес корневой директории и древовидную структуру расположения всех файлов. Его можно распарсить, прочитать и найти все необходимые файлы. Например, для Windows есть специальная утилита mountvol.exe, позволяющая смонтировать по умолчанию скрытый раздел EFI System Partition и посмотреть его содержимое. Для этого необходимо указать ключ /S и букву диска, куда его следует смонтировать. В директории, которая была в NVRAM-переменной, находится файл менеджера загрузки. Помимо него, там есть и другие файлы: модуль, отвечающий за работу отладчика, и файл BCD (куст реестра с BCD-параметрами). Параметры будут необходимы в процессе работы менеджера загрузки и инициализации системы.
Менеджер загрузки является UEFI-приложением, поэтому должен соответствовать соглашению о точке входа. Точка входа менеджера загрузки имеет определенную сигнатуру. Ее второй параметр — SystemTable — указывает на структуру, которая, в свою очередь, содержит указатели на таблицу RuntimeServices и таблицу BootServices. Их задача — предоставить API-интерфейс для работы с оборудованием.
Давайте обобщим ключевые отличия загрузки в режиме UEFI от загрузки в режиме Legacy BIOS:
переключение режимов выполняется UEFI на ранней стадии;
для работы с оборудованием используется API-интерфейс, в частности RuntimeServices и BootServices;
менеджер загрузки (bootmgf.efi) инициализирует механизм проверки целостности кода (Code Integrity);
загрузчик ОС winload.efi заворачивает UEFI-сервисы для использования ядром через HAL.
Изучаем Secure Boot
На наш взгляд, самый интересный компонент прошивки UEFI — Secure Boot. Его архитектура хорошо описана и проиллюстрирована в книге «Руткиты и буткиты. Обратная разработка вредоносных программ и угрозы следующего поколения» (12+) за авторством Алекса Матросова, Евгения Родионова и Сергея Братуся.
В основе Secure Boot лежит набор ключей разного уровня. Самый важный среди них — platform key (PK), который содержится в прошивке. Начальный PK верифицирует key exchange key (KEK).
Стоит отметить, что существует две базы ключей:
db — база ключей для аутентификации, отвечает за проверку подписей модулей;
dbx — база запрещенных ключей и хешей.
Согласно алгоритму работы Secure Boot, при загрузке нового модуля проверяется не только его подпись, но и хеш. Дело в том, что бывают модули UEFI-драйверов с абсолютно легитимными подписями. При обнаружении критически опасной уязвимости, даже если модуль пройдет проверку подписи, но его хеш будет отмечен в базе исключений, он не будет допущен к загрузке и исполнению.
UEFI-модули могут быть двух форматов:
· Portable Executable (привычный для Windows);
· Terse Executable. Этот формат похож на Portable Executable, но лишен части полей и имеет другие сигнатуры.
Как работает UEFI-буткит
Есть два варианта заражения:
1. Заражение прошивки
2. Заражение в EFI System Partition, в частности:
· подмена bootmgfw.efi
· добавление нового модуля
Известны успешные атаки, в ходе которых злоумышленникам удавалось перезаписать код в SPI-чипе, то есть, по сути, заразить его. Позднее появилась пара буткитов, нацеленных на компрометацию менеджера загрузки.
На данный момент мы можем выделить два вектора атаки на UEFI-платформу: перепрошивка SPI и модификация менеджера загрузки. Касательно второго вектора следует выделить частный случай добавления нового модуля в раздел EFI System Partition и последующего изменения пути в NVRAM-переменной, которая указывает, с какого устройства необходимо загрузиться.
Первая ласточка — буткит LoJax
Первый UEFI-буткит был найден и описан специалистами ESET. В LoJax использовался подход перезаписи содержимого прошивки. Другими словами, он считывал прошивку, находил маркеры, что-то добавлял и записывал обратно.
Важно понимать, что чип SPI представляет собой некое PCI-устройство. Следовательно, чтобы с ним взаимодействовать нужен особый интерфейс. Поэтому чтение и запись содержимого чипа — это многоступенчатая и сложная операция, включающая работу с различными флагами и параметрами. Если изучить код одного из модулей LoJax, умеющих читать и писать в SPI, можно убедиться, что это действительно PCI-устройство. В модуле прописан PCI-адрес устройства, а с устройством этот модуль взаимодействует посредством драйвера с помощью функции DeviceIoControl.
Интерфейс работы с SPI непрозрачен. Кроме того, механизмы безопасности защищают его от простого считывания. В случае LoJax в основу подхода по компрометации легла эксплуатация уязвимости race condition. Стоит отметить, что данная уязвимость присутствует не во всех системах. У SPI-чипа есть набор флагов, управляющих не только разрешением на запись, но и его блокировкой. Один из флагов можно сбросить с помощью кода, работающего в режиме System Management Mode (SMM). Это привилегированный режим, в котором приостанавливается исполнение другого кода, в том числе кода ОС и гипервизора. Как только происходит попытка записи, SMM блокирует возможность записывать. Поэтому создатели буткитов используют два потока. Таким способом они одновременно пытаются сбросить защитные флаги и записать что-то в чип.
Чтобы взаимодействовать c PCI-устройством, LoJax использует драйвер в составе утилиты Read & Write Everything. Утилита позволяет работать с низкоуровневыми компонентами системы: физической памятью, устройствами ввода-вывода и другими. Чтобы выполнять функции чтения-записи физической памяти, взаимодействия с устройствами и различными низкоуровневыми подсистемами и настройками, она имеет подписанный легитимный драйвер, который как раз и использовали разработчики LoJax.
Отметим, что PCI-устройства должны следовать соглашениям и в том числе иметь в своем составе блок данных PCI Configuration Space. PCI Configuration Space находится непосредственно в PCI-устройстве. Чтобы читать этот блок или записывать в него, есть два порта ввода-вывода: 0xCF8 и 0xCFC.
Через порт PCI_COFIG_ADDRESS указывается адрес внутри блока данных, откуда можно считать (read) или записать (write) данные. Так, можно считать данные из PCI Configuration Space, например идентификатор устройства или его производителя, а также специфические для этого устройства значения. Порт PCI_CONFIG_DATA используется для считывания и записи данных. Направление зависит от применяемой инструкции: IN — считывание, OUT — запись.
Кроме того, у PCI-устройства есть Base Address Registers — область, содержащая специфические значения для данного устройства. Например, адрес в физической памяти, куда отображены управляющие регистры SPI. По сути, за счет взаимодействия с PCI-регистрами, отображенными в физической памяти, и выполняется чтение прошивки и запись в нее.
Обращаем внимание, что в памяти отображается не содержимое чипа, а, например, четыре или восемь байтов, каждый из которых может быть либо флагом, либо управляющим значением. Эти значения изменяются — и выполняется взаимодействие. Если расписать последовательность шагов, то сначала мы:
считываем PCI-регистры из Base Address Registers с помощью портов CF8 и CFC;
получаем адрес регистров, отображенных в физической памяти;
пишем в отображенные регистры команды управления, в частности указываем адрес, откуда необходимо считать новую прошивку.
Все перечисленные операции выполняются в цикле. Он включает установку контрольного флага, начало чтения (или записи) и изменение дополнительных параметров.
Эти, на первый взгляд, сложные манипуляции помогают буткиту LoJax достичь цели: записать вредоносный код в прошивку, заменить модуль с легитимным кодом на свой и исполнить его на ранней стадии загрузки системы. Так он будет недосягаем для антивирусов и других средств защиты, а атакующие смогут получить необходимый им persist.
Знакомьтесь: MosaicRegressor
Еще один интересный буткит — MosaicRegressor, исследованный специалистами «Лаборатории Касперского». В его случае начальный вектор заражения неизвестен, то есть никто не знает, как буткит был установлен.
В зараженной прошивке исследователи обнаружили четыре модуля, два драйвера и два приложения, исполняющих вредоносную нагрузку. Рассмотрим их подробнее. Первым исполняется модуль SmmInterfaceBase. В его задачу входит создание c помощью API-функции CreateEventEx события EVENT_GROUPR_READY_TO_BOOT. Это событие наступает перед тем, как управление передается менеджеру загрузки. Тогда же вызывается обратный вызов NotifyFunction.
Интересно, что происходит внутри callback? Давайте посмотрим. С помощью уникального идентификатора приложения (GUID) обнаруживается модуль SmmAccessSub. Затем он запускается.
В этом модуле SmmAccessSub, по нашему мнению, довольно тривиальный подход к persist. Он заключается в сбросе пользовательского модуля в папку Startup. При этом никаких действий с драйверами или подмен не происходит.
Еще одним модулем в зараженной прошивке был SmmReset. Он сбрасывает значение EFE до нуля.
Буткит MosaicRegressor не использует SmmReset, однако модуль для работы с точно такой же переменной есть в утекшем репозитории Hacking Team и применяется, чтобы определить, заражена прошивка или нет.
MoonBounce — самый примечательный экземпляр
MoonBounce можно назвать новинкой в списке буткитов, угрожающих UEFI. Его обнаружили в конце января 2022 года. Вредонос отличает сложная и интересная схема заражения. Так же, как и в случае с MosaicRegressor, начальный вектор проникновения неизвестен.
В прошивке буткит начинает работать до стадии исполнения драйверов. При этом вредоносные действия выполняются не в драйвере, а непосредственно в коде прошивки — в начале стадии DXE. Принцип работы MoonBounce следующий: буткит перехватывает несколько функций Boot-сервисов. Первая функция размещает в памяти шеллкод уровня Ring 0, другая — перехватывает функцию, которая передает управление ядру ОС, а затем перехватывает функцию выделения памяти внутри ядра ExAllocatePool. Все это MoonBounce делает, чтобы исполнить шеллкод, который был «замаплен» еще на стадии работы прошивки. Дальше в дело вступает сам шеллкод: он загружает и вызывает вредоносный драйвер, который обладает возможностями руткита. Как вы, наверное, уже поняли, необычно здесь то, что случилось не «аккуратное внедрение» модуля, а заражение всей прошивки.
Атаки на Boot-менеджеры
Атаки на Boot-менеджеры рассмотрим на примере буткитов FinSpy и ESPecter. FinSpy, также известный как FinFisher, подменяет оригинальный менеджер загрузки на вредоносный. В частности, при передаче управления от UEFI к менеджеру загрузки вызывается вредоносный менеджер, который выполняет некоторые функции (например, перехватывает функции передачи управления ядру или патчит код, ответственный за проверку цифровых подписей), а затем загружается оригинальный Boot-менеджер.
Для заражения раздела EFI System Partition злоумышленникам достаточно воспользоваться банальным набором API. Это позволяет им смонтировать раздел и в дальнейшем работать с ним, как с любым другим диском.
Алгоритм работы FinSpy выглядит так:
подмененный Boot-менеджер загружает оригинальный Boot-менеджер;
подменный менеджер патчит в оригинальном функцию, передающую управление сначала загрузчику, а затем ядру.
Второе действие позволяет перехватить ядерную функцию создания системного потока. Она, в свою очередь, дает возможность сбросить вредоносную нагрузку. Прошивка UEFI в этой атаке затронута не была.
ESPecter замыкает пятерку буткитов, на текущий момент известных исследователям. Его корни уходят как минимум в 2012 год. В процессе своей работы ESPecter патчит менеджер загрузки, чтобы заменить легитимный драйвер (либо beep.sys, либо winsys.dll) на вредоносный. Кроме того, загрузчик буткита патчит проверку целостности загрузчика (да-да, загрузчик проверяет сам себя на пропатченность).
Буткит ESPecter отключает проверку цифровой подписи драйверов, чтобы загрузить в систему подменный beep.sys или winsys.dll и запустить его.
В ядре Windows предусмотрена функция, отвечающая за инициализацию проверки целостности. Если ее запатчить и приравнять к нулю переменную CiOptions, проверки будут проходить так, словно драйверы подписаны. Этот подход ESPecter применяет для legacy-систем.
Что это за зверь такой — System Management Mode
Рассказывая о буткитах, нельзя не упомянуть System Management Mode — привилегированный режим работы процессора. Он необходим для управления электропитанием и проприетарными функциями (их определяют сами вендоры).
Режим SMM, который часто еще называют Ring -2, непрозрачен для ОС. Переходя в него, процессор может выполнять код. Код обычно находится в области памяти SMRAM. Она начинается с адреса SMBASE. SMBASE по дефолту имеет фиксированный адрес, но также может меняться с помощью специальных MSR-регистров. В режиме SMM процессор сохраняет весь свой контекст (значения почти всех регистров в верхней области памяти).
Перейти в режим SMM процессор может несколькими способами. Например, он может выполнить прерывание (System Management Interrupt, SMI). Выделяют три вида прерывания:
· аппаратное,
· системное,
· программное.
С позиции атакующего системные прерывания сложны в реализации. Тогда как программные вполне можно вызвать кодом на уровне драйвера. Для этого на порты b3 и b2 необходимо отправить определенное значение (оно отличается для разных устройств), и процессор переключится в режим SMM.
Режим SMM опасен еще и тем, что имеет доступ ко всей памяти системы. Из этого вырисовывается несколько возможных вариантов эксплуатации: использование руткита с доступом к системе либо перезапись содержимого чипа SPI. SMM имеет много уязвимостей, преимущественно вендороспецифичных, например, SMM Callout, которая позволяет выполнить код в режиме SMM за пределами SMRAM. Убедиться в неиллюзорности угроз можно, ознакомившись со списком уязвимостей. По большей части они характерны либо для конкретного девайса, либо фреймворка.
Резюме
В заключение кратко перечислим главные моменты статьи:
UEFI-буткиты могут обойти все механизмы защиты и получить контроль над всей системой;
SPI-буткит невозможно удалить ни переустановкой ОС, ни форматированием жесткого диска;
для борьбы с буткитами есть механизмы защиты, такие как Secure Boot и Intel Boot Guard, но они не панацея;
наибольшую опасность сегодня представляют уязвимости в режиме SMM и подсистеме Intel ME.
Как эффективно обнаруживать UEFI-буткиты, расскажем на следующем вебинаре. Stay tuned!
Посмотреть видеоверсию статьи и скачать презентацию вы можете на сайте Positive Technologies.
Авторы:
Антон Белоусов, старший специалист отдела обнаружения вредоносного ПО экспертного центра безопасности Positive Technologies
Алексей Вишняков, руководитель отдела обнаружения вредоносного ПО экспертного центра безопасности Positive Technologies
Комментарии (4)
kovserg
28.05.2022 11:44+1Интересно что мешает сделать переключатель или jumper что бы отключать или включать возможность изменения загрузчика? Зачем надо было городить весь этот огород?
dentata911
29.05.2022 16:53Так раньше ж так и было. Прежде чем прошивать биос надо было замкнуть джампер, иначе не прошивалось. Потом решили, что это слишком скучно :-)
CodeRush
Приятно, когда по теме что-то пишут на русском, но для специалиста в статье нет ничего нового и\или интересного, к сожалению.
Не соглашусь. Наибольшую реальную опасность, на мой взгяд, представляют:
— отключенный или неверно настроенный UEFI SecureBoot, который по прежнему никак не могут включить по умолчанию по разным причинам. Т.к. технологию проектировали и в спешке, и при активном противостоянии OSS-сообщества, результат получится «ни жив, ни мертв», т.е. в теории у нас есть безопасная загрузка какая-то, а на практике UEFI CA доверять нельзя совсем (им за 10 лет подписали слишком много уязвимого), работающего механизма отзыва подписей нет (а тот, что есть в виде dbx и dbt страдает от комбинаторного взрыва), работающего механизма отзыва сертификатов нет (все попытки решить вопрос через Audit Mode и Deployment Mode можно считать провалившимися), придумывать что-то новое ни UEFI Forum, ни остальные не торопятся.
— отключенный, или неверно настроенный, или тривиально отключаемый условный BootGuard (т.е. все скопом технологии защиты от модификации прошивки на SPI NOR и\или предотвращения запуска неавторизованного модифицированного кода). Проблема неработоспособности статического корня доверия стоит у MS настолько остро, что они требуют наличия TPM 2.0 для Windows 11, и стремятся всеми силами выкинуть прошивку из цепочки доверия, и я их прекрасно понимаю.
— поистине огромное количество уязвимостей в драйверах, взаимодействующих с переменными NVRAM, вызванное плохим дизайном интерфейса GetVariable и SetVariable.
— практическая незащищенность подавляющего большинства прошивок от DMA-атак, которые за последние ~7 лет превратились из «нужен специалист с ФПГА за 5 килобаксов и куча кастомного кода» в «нужен донгл за 30 баксов и PCILeech с гитхаба».
Самое неприятное, что хочется отметить, это отсутствие и прогресса в безопасности UEFI относительно ее состояния от 2015 года, когда я про нее писал серию статей, и интереса вендоров к этому прогрессу. Годы идут, а мы решаем те же самые проблемы теми же самыми методами, игнорируя десятилетия усилий безопасников и системных программистов, занятых развитием современных ОС. Нельзя сказать, что никто вообще ничего не делает, но все, что делается — делается двумя с половиной вендорами для себя же, а независимого аудита этих «решений» либо не проводится совсем, либо его результаты сразу же засекречиваются по соглашению сторон.
Короче, с точки зрения реальной безопасности прошивок для Wintel PC — «мы в жопе, Баттхед».
vassabi
ИМХО это все потому, что вариант "пользователь сможет вас выкинуть со своего устройства" - даже не рассматривается. Либо "пользователи должны быть у нас в рабстве", либо "ой, как-то не интересно".