Здравствуйте, друзья! В сегодняшней статье мы рассмотрим схему функционирования и устройство прошивки UEFI на материнских платах с процессорами и чипсетами Intel, протестируем ее с помощью CHIPSEC Framework и сделаем ряд не очень утешительных выводов касательно безопасности UEFI.
- Unified Extensible Firmware Interface
- Аппаратное расположение прошивки UEFI
- Образы и протоколы в UEFI
- Основные фазы функционирования ПО UEFI
- Режим системного управления (SMM)
- SMRAM
- SMI-прерывания
- Обработчики SMI-прерываний
- CHIPSEC
- Что такое CHIPSEC?
- Установка
- Linux
- Windows
- Описание модулей CHIPSEC
- CHIPSEC в Python
- Практические результаты
- Заключение
Unified Extensible Firmware Interface
После подачи питания на аппаратную платформу ЭВМ должна произойти корректная инициализация оборудования и выбор загрузчика операционной системы. Если раньше эту функцию выполнял всем известный BIOS Legacy, то со временем производители аппаратных платформ пришли к использованию более усовершенствованной технологии — UEFI.
Некоторые отличия между BIOS Legacy и UEFI отражены в сравнительной таблице.
BIOS Legacy | UEFI | |
---|---|---|
Поддерживаемые режимы работы процессора | Режим реальных адресов | Режим реальных адресов, защищенный режим работы процессора |
Поддержка виртуальной памяти | Не поддерживает | Поддерживает |
Объем используемой оперативной памяти | 1 Мбайт | Не ограничен |
Пространство опционального ПЗУ (Option ROM) | 1 Мбайт | Не ограничено |
Доступ к регистрам | 16-битный | 16-битный, 32-битный, 64-битный |
Независимость от архитектуры | Не обеспечивает | Обеспечивает |
Язык программирования | Ассемблер | Си/ассемблер |
Функция безопасной загрузки | Отсутствует | Присутствует |
Таблица разделов жесткого диска | MBR | GPT |
Спецификация UEFI описывает интерфейс между операционной системой и программным обеспечением аппаратной платформы во время загрузки. Основная идея, заложенная в спецификации, — сделать прошивку модульной и расширяемой.
Изменениями в спецификации управляет сообщество Unified Extensible Firmware Interface Forum, основная задача которого — расширять и улучшать существующую спецификацию, добавляя новые функциональные возможности и исправляя текущие недостатки. Для разработчиков программных компонентов спецификация UEFI предоставляет возможности повторного использования кода, расширяемости, модульности, а также легкого прототипирования в процессе разработки.
Хорошо это или плохо? С одной стороны, расширяемость и модульность позволяют разработчикам наделять прошивку дополнительными функциональными возможностями. С другой — обилие программного кода делает ее более уязвимой. Так как низкоуровневое программное обеспечение прошивки UEFI первостепенно в системе, уровень защищенности прошивки играет основную роль при оценке защищенности всей ЭВМ.
Аппаратное расположение прошивки UEFI
Совокупность всех прошивок в системе называют прошивкой платформы. Как правило, она расположена во флеш-памяти SPI (Flash memory). В этой же памяти располагается прошивка UEFI.
Для того чтобы система могла различать прошивки между собой, флеш-память делится на регионы. Доступ к регионам разграничивает встроенный в чипсет PCH SPI-контроллер (в современных системах PCH играет роль южного моста). На скриншоте представлен дамп флеш-памяти SPI в утилите UEFITool.
Описание некоторых регионов флеш-памяти SPI:
- Flash Descriptor (Descriptor region) — регион дескриптора, содержащий основные смещения и настройки флеш-памяти SPI;
- GbE region — регион, содержащий основные настройки сетевой карты;
- ME region — регион, содержащий прошивку ME (ME выполняет функции управления энергопотреблением, функции инициализации и запуска основного процессора);
- UEFI region (BIOS region) — содержит прошивку UEFI;
- PDR region — регион, предназначенный для описания возможностей, зависящих от платформы.
Образы и протоколы в UEFI
Спецификация UEFI позволяет расширять прошивку через загрузку образов. Образ в UEFI может быть представлен в виде отдельного драйвера или приложения (наглядный пример — приложение UEFI Shell). Структура любого образа описывается форматом PE32/PE32+.
Расширение, а также идентификация компонентов UEFI выполняется с помощью GUID-записей. GUID представляет собой уникальный 128-битный идентификатор, соответствующий тому или иному компоненту прошивки.
Любое устройство или образ в UEFI имеют собственный протокол обработки. Каждый протокол состоит из GUID и структуры интерфейса протокола. Структура интерфейса протокола содержит функции и данные, которые используются для доступа к тому или иному устройству. Управление протоколами обеспечивают специальные службы UEFI (LocateProtocol, OpenProtocol и другие).
Основные фазы функционирования ПО UEFI
Работу ПО UEFI условно можно разбить на два этапа:
- во время инициализации платформы;
- во время загрузки и работы ОС.
Ниже описано, за что отвечает каждая фаза.
SEC:
- обработка всех событий перезапуска платформы;
- создание хранилища временной памяти;
- проверка целостности и подлинности элементов прошивки;
- подготовка и передача необходимой информации в следующую фазу.
PEI:
- инициализация постоянной памяти;
- описание памяти в специальных структурах Hand-Off Blocks (HOBs);
- описание адресов размещений микропрограмм в HOBs-структурах;
- передача управления в фазу среды выполнения драйверов.
DXE:
- инициализация служб загрузки, служб реального времени выполнения и служб DXE (DXE Services);
- обнаружение и выполнение драйверов DXE;
- инициализация процессора, набора микросхем и компонентов платформы.
BDS:
- инициализация консольных устройств;
- загрузка драйверов устройств;
- попытка загрузки в ОС;
- если попытка не удалась, повторно выполняется фаза DXE.
В качестве отдельной фазы выделяют режим системного управления (SMM).
Режим системного управления (SMM)
SMM — специальный режим работы, в который процессор переходит через вызов прерывания управления системой (SMI). Среда выполнения кода SMM инициализируется драйверами служб загрузки и находится в специально выделенной привилегированной области памяти SMRAM (System Management Random Access Memory).
Разработчики аппаратных платформ могут использовать данный режим для любых целей (управление питанием, обработка системных ошибок, обеспечение безопасности прошивки и другие).
Режим системного управления считается режимом –2 кольца защиты процессора, а в связи с тем, что SMI-прерывание легко вызвать из ядра ОС, он представляет особый интерес для злоумышленника.
SMRAM
SMRAM — это защищенная на уровне PCH область физической памяти, которая представляет собой адресное пространство при входе в SMM-режим. Эта область памяти содержит код и данные обработчиков SMI-прерываний, а также сохраненный контекст процессора и операционной системы перед вызовом SMI-прерывания.
Адреса внутри SMRAM представляют собой смещения относительно значения SMBASE, которое, в свою очередь, является внутренним регистром процессора, содержащим базовый адрес SMRAM. Отмечу, что SMBASE — это один из MSR-регистров.
Параметры управления доступом к SMRAM прописаны в конфигурации 8-битного регистра SMRAM Control.
SMI-прерывания
SMI (System Management Interrupt) — единственный способ перевести процессор в режим системного управления. SMI представляет собой внешнее прерывание, работающее независимо от механизма обработки прерываний и исключений процессора.
SMI-прерывания бывают аппаратными, системными и программными. Со списком всевозможных событий, генерирующих SMI-прерывания, можно ознакомиться здесь (Power Management).
Вызвать программное SMI-прерывание можно при помощи записи в порт ввода-вывода APM (Advanced Power Management). Данный порт имеет два регистра: APM_CNT (0xB2) и APM_STS (0xB3).
APM_CNT (0xB2) — управляющий регистр. Для вызова SMI-прерывания необходимо записать в этот регистр байт — номер (код) прерывания.
APM_STS (0xB3) — регистр статуса. Запись в данный регистр не вызывает SMI-прерывание, тем не менее, он может быть использован для передачи дополнительной информации обработчику SMI-прерывания. SMI-прерывание будет сгенерировано только в том случае, если в конфигурационном регистре APM установлен бит APMC_EN.
Обработчики SMI-прерываний
Обработчики SMI-прерываний вызываются процессором при возникновении соответствующего SMI-прерывания и возвращаются в операционную систему с помощью специальной инструкции RSM. Основным механизмом передачи информации и регулирования деятельности обработчиков SMI-прерываний в режиме SMM является SMST (System Management System Table). SMST обеспечивает доступ к службам режима SMM.
CHIPSEC
Что такое CHIPSEC?
CHIPSEC представляет собой фреймворк, написанный на Python. Его можно использовать как из командной строки, так и в качестве импортируемых Python-модулей, что позволяет с легкостью писать собственные тесты для оценки защищенности. Первая версия фреймворка была представлена в 2014 году на конференции CanSecWest.
CHIPSEC можно использовать из операционной системы Windows, Linux, macOS или вообще без операционной системы. В таком случае понадобится UEFI Shell — стандартное UEFI-приложение.
[box type=»info» align=»» class=»» width=»»]Последние наборы системной логики (Intel 300 Series) и процессоры Core 8-го поколения (Coffee Lake) не поддерживаются CHIPSEC.[/box]
Установка
Разберем установку фреймворка в ОС Linux и Windows. Будем следовать инструкциям из мануала. Там же можно найти инструкции по установке CHIPSEC в macOS и по запуску в UEFI Shell.
[box type=»warning» align=»» class=»» width=»»]Настоятельно рекомендуется устанавливать и использовать фреймворк только на тестовых ЭВМ.[/box]
Linux
1. Устанавливаем зависимости.
Для систем с пакетным менеджером yum:
1 2 |
> yum install kernel kernel-devel-$(uname -r) python python-devel gcc nasm redhat-rpm -config > pip install setuptools |
Для систем с пакетным менеджером apt:
1 2 |
> apt-get install build-essential python-dev python gcc \ linux-headers-$(uname -r) nasm > pip install setuptools |
2. Устанавливаем CHIPSEC.
Проще всего установить из PyPI:
1 |
> pip install chipsec |
Кроме того, установить можно вручную:
1 2 |
> git clone https://github.com/chipsec/chipsec > python setup.py install |
3. Отключаем безопасную загрузку для нормальной работы драйвера.
Windows
Для установки CHIPSEC в Windows необходимо вручную собрать и подписать (только на Windows x64) драйвер для взаимодействия фреймворка с компонентами аппаратной платформы.
- Устанавливаем необходимые пакеты.
12> pip install setuptools> pip install pywin32 - Собираем и подписываем драйвер по отдельной инструкции.
- Копируем подписанный драйвер в директорию <CHIPSEC_DIRECTORY>\chipsec\helper\win\win7_amd64 или <CHIPSEC_DIRECTORY>\chipsec\helper\win\win7_ x86 в зависимости от архитектуры процессора на тестируемой системе.
- Только после того, как драйвер окажется в нужной директории, устанавливаем CHIPSEC.
1> pip install chipsec
В данном случае подписанный драйвер следовало положить сюда: \Lib\site-packages\chipsec\helper\win\win7_amd64\chipsec_hlpr.sys.
При корректной установке в директории \Scripts должны появиться исполняемые файлы с двумя основными модулями CHIPSEC.
- Отключаем обязательную проверку подписи драйверов. Это необходимо делать после каждого перезапуска системы.
- Переходим в меню дополнительных параметров загрузки:
1> shutdown /r /t 0 /o - «Поиск и устранение неисправностей → Дополнительные параметры → Параметры загрузки → Перезагрузить».
- В появившемся после перезагрузки списке команд выбираем «Отключить обязательную проверку подписи драйверов».
Данный способ (пункт 5) работает без отключения опции безопасной загрузки. В качестве альтернативы можно отключить безопасную загрузку и выполнить следующие команды в CMD (Administrator):
1 2 |
> BcdEdit /set noIntegrityChecks ON > BcdEdit /set loadoptions DISABLE_INTEGRITY_CHECKS |
После чего устанавливаем тестовый режим работы системы и перезагружаемся.
1 2 |
> BcdEdit /set TESTSIGNING ON > shutdown -r -t 0 |
Для того чтобы вернуть систему в первоначальное состояние, выполняем обратные команды и перезагружаемся:
1 2 3 |
> BcdEdit /set noIntegrityChecks OFF > BcdEdit /set loadoptions ENABLE_INTEGRITY_CHECKS > BcdEdit /set TESTSIGNING OFF |
Описание модулей CHIPSEC
Две основные утилиты в CHIPSEC — chipsec_main и chipsec_util. Утилита chipsec_util содержит набор инструментов для взаимодействия с аппаратной платформой. Чтобы просмотреть весь набор функций, предоставляемый данной утилитой, достаточно выполнить команду chipsec_util в командной строке (результат).
Утилита chipsec_main запускает тесты. Чтобы запустить стандартный набор тестов для оценки защищенности аппаратной платформы, выполняем chipsec_main в командной строке (полный лог).
Для запуска отдельного модуля с тестом можно воспользоваться опцией -m или —module.
Полный список опций и формат возвращаемого значения chipsec_main можно посмотреть здесь. Список поддерживаемых CHIPSEC аппаратных платформ — здесь. Если платформа не поддерживается, пользоваться фреймворком все равно можно. В таком случае CHIPSEC пропустит все тесты, специфичные для конкретных аппаратных платформ.
Каждый из отдельных тестов CHIPSEC создан на основании реальных атак или рекомендаций по безопасной разработке прошивки. В таблице описаны основные тесты защищенности UEFI. За более подробной информацией можно обратиться к мануалу CHIPSEC.
Общая оценка защищенности
Название модуля | Описание модуля |
---|---|
common.secureboot.variables | Проверяет переменные безопасной загрузки UEFI (Secure Boot key/whitelist/blacklist), производит собственные попытки модификации переменных (переменные должны иметь правильные атрибуты доступа и защиту от несанкционированной модификации) |
common.uefi.access_uefispec | Проверяет атрибуты доступа глобальных переменных UEFI на соответствие спецификации |
tools.uefi.blacklist | Проверяет компоненты прошивки UEFI (прошивка не должна содержать вредоносных компонентов, определенных в специальном конфигурационном файле) |
tools.uefi.whitelist | Генерирует устойчивый список образов UEFI, после чего сверяет новые состояния образов с данным списком во избежание модификаций |
common.bios_kbrd_buffer | Проверяет буфер клавиатуры BIOS на наличие паролей BIOS/HDD/pre-bot, чтобы предотвратить атаку, описанную здесь (в идеале буфер клавиатуры BIOS должен быть пустым) |
common.uefi.s3bootscript | Проверяет безопасность сценария загрузки из режима сна S3, реализованного на базе прошивок UEFI. Основанием для создания данного модуля послужила уязвимость CVE-2014-8274. Примеры атак: 1, 2, 3 |
common.ia32cfg | Проверяет архитектурные особенности IA32/IA64 (архитектурные особенности, включая регистры MSR, должны быть правильно сконфигурированы и заблокированы) |
tools.smm.rogue_mmio_bar | Пытается создать поддельные диапазоны MMIO (Memory-mapped I/O) в памяти, переместить аппаратное обеспечение MMIO BAR (MMIO Basic) в поддельную область памяти, после чего наблюдает за изменениями, сделанными обработчиками SMI-прерываний в перемещенном диапазоне MMIO |
Оценка защищенности флеш-памяти SPI
Название модуля | Описание модуля |
---|---|
common.bios_wp | Проверяет возможность модификации флеш-памяти SPI. Тест основан на проверке регистров чипсета PR0-PRN и флагов регистра BIOS_CNTL |
common.bios_smi | Проверяет конфигурацию событий SMI. Если конфигурация событий SMI настроена неправильно, теряет смысл защита от перезаписи, основанная на флагах регистра BIOS_CNTL |
common.spi_desc | Считывает SPI Flash Descriptor, проверяет программную возможность его модификации |
common.spi_fdopss | Проверяет значение бита FDOPSS регистра HSFS контроллера SPI, сигнализирующего о перезаписи SPI Flash Descriptor |
common.spi_lock | Проверяет значение бита FLOCKDN регистра HSFS контроллера SPI. Если FLOCKDN не установлен, конфигурация SPI-контроллера, включая PR-регистры, может быть модифицирована злоумышленником |
common.bios_ts | Проверяет блокировку региона с UEFI во флеш-памяти SPI |
Оценка защищенности SMM
Название модуля | Описание модуля |
---|---|
common.smm | Проверяет конфигурацию регистра SMRAM Control во избежание доступа к SMRAM |
remap | Проверяет возможность переназначения SMRAM |
common.smrr | Проверяет возможность реализации атаки SMM cache poisoning |
tools.smm.smm_ptr | Проверяет возможность реализации атаки типа проверки указателей |
smm_dma | Проверяет возможность реализации DMA-атаки |
CHIPSEC в Python
Утилиты chipsec_main и chipsec_util можно использовать не только из командной строки, но и в качестве импортируемых Python-модулей. Это позволяет создавать собственные сценарии для оценки защищенности аппаратной платформы или просто использовать функциональность chipsec_util в Python.
Чтобы выполнить необходимую команду, передаем массив с опциями в функцию chipsec_main.main() или chipsec_util.main(). Например, так можно вызвать программное SMI-прерывание с кодом 0x0E:
В качестве примера использования CHIPSEC в Python был написан скрипт chipsec_script.py. Он запускает определенный набор модулей CHIPSEC и делает вывод о состоянии защищенности аппаратной платформы.
Практические результаты
Мы протестировали данным скриптом четыре аппаратные платформы. Результаты получились следующие (логи):
Аппаратная платформа | FAIL (защита не обеспечена) | WARNING (необходимы дополнительные проверки) |
---|---|---|
Mobile 2rd Generation Core Processor (Sandy Bridge CPU / Cougar Point PCH) | common.bios_wp, common.bios_smi | common.bios_kbrd_buffer |
Mobile 3rd Generation Core Processor (Ivy Bridge CPU / Panther Point PCH) | common.bios_wp, common.bios_smi | common.bios_kbrd_buffer |
Mobile 7th Generation Core Processor (Kabylake U) | common.spi_desc | common.uefi.s3bootscript |
Unknown Platform | — | smm_dma |
Видим, что платформы c архитектурами Intel Mobile 2rd Generation и Mobile 3rd Generation уязвимы перед модификацией флеш-памяти SPI:
- проигнорирована защита, основанная на использовании регистров PR0-PRN;
- не выставлены флаги регистра BIOS_CNTL;
- неправильно настроена конфигурация событий SMI, что позволяет подавить вызов SMI-прерывания при попытке модификации флеш-памяти SPI.
У платформы c архитектурой Intel Mobile 7th Generation отсутствует защита от программной модификации SPI Flash Descriptor.
Заключение
UEFI — это root of trust всей электронно-вычислительной системы, и безопасность прошивки UEFI заслуживает отдельного внимания.
Практика показала, что существующие защитные решения и рекомендации по безопасной разработке могут обеспечить должный уровень защищенности компонентов аппаратной платформы, но зачастую производители игнорируют их.
CHIPSEC Framework позволяет понять, насколько прошивка UEFI соответствует рекомендациям по безопасной разработке, а также оценить ее устойчивость перед известными атаками.