1. SMM 드라이버 취약점 (Ring -2)
CVE-2025-7026
- NVD:https://nvd.nist.gov/vuln/detail/CVE-2025-7026
- Gigabyte 펌웨어에서 발견된 취약점으로, SW SMI 핸들러가 CPU 레지스터를 읽어들일 때, 공격자가 제어 가능한 주소 값을 그대로 사용하여 SMRAM 영역을 오염시킬 수 있는 문제이다.
분석
취약점이 존재하는 SwSmiHandler 함수의 전체 코드
EFI_STATUS __cdecl SwSmiHandler(
EFI_HANDLE DispatchHandle,
const void *Context,
EFI_SMM_SW_CONTEXT *CommBuffer,
UINTN *CommBufferSize)
{
UINTN SwSmiCpuIndex;
INT32 Result;
UINT32 RbxRegister;
UINT32 RcxRegister;
UINTN Value;
LODWORD(Value) = 0;
if ( CommBuffer && CommBufferSize )
SwSmiCpuIndex = CommBuffer->SwSmiCpuIndex;
else
SwSmiCpuIndex = Value;
if ( SwSmiCpuIndex != -1 )
{
// 1. read buffer address in RbxRegister
gEfiSmmCpuProtocol->ReadSaveState(
gEfiSmmCpuProtocol,
4,
EFI_SMM_SAVE_STATE_REGISTER_RBX,
SwSmiCpuIndex,
&RbxRegister);
// 2. read command in RcxRegister
gEfiSmmCpuProtocol->ReadSaveState(
gEfiSmmCpuProtocol,
4,
EFI_SMM_SAVE_STATE_REGISTER_RCX,
SwSmiCpuIndex,
&RcxRegister);
if ( RcxRegister )
{
if ( RcxRegister != 1 )
{
LODWORD(Value) = 0x8004;
_WriteRbx:
gEfiSmmCpuProtocol->WriteSaveState(
gEfiSmmCpuProtocol,
4,
EFI_SMM_SAVE_STATE_REGISTER_RBX,
SwSmiCpuIndex,
&Value);
return 0;
}
Result = CommandRcx1(RbxRegister);
}
else
{
// vulnerable function
Result = CommandRcx0(RbxRegister);
}
LODWORD(Value) = Result;
if ( (Result - 0x9001) <= 1 )
{
gEfiSmmCpuProtocol->WriteSaveState(
gEfiSmmCpuProtocol,
4,
EFI_SMM_SAVE_STATE_REGISTER_RCX,
SwSmiCpuIndex,
&Value);
LODWORD(Value) = 0xFFFF;
}
goto _WriteRbx;
}
return 0;
}
- RcxRegister 변수에 EFI_SMM_SAVE_STATE_REGISTER_RCX 값 로드
- RbxRegister 변수에 EFI_SMM_SAVE_STATE_REGISTER_RBX 값 로드
- RcxRegister 값에 따라 CommandRcx1 또는 CommandRcx0 실행
임의 쓰기 발생 지점
CommandRcx0는 위 핸들러에서 호출되는 함수로, 인자로 전달된 RbxRegister 포인터에 대한 검증 없이 값을 사용한다.
INT32 CommandRcx0(BIOS_SETTINGS_DATA_HEADER *RbxRegister)
{
SetupDataSize = 0xD6C;
ResultStatus = 0;
if ( gRT->GetVariable(L"Setup", &EFI_SETUP_VARIABLE_GUID, 0, &SetupDataSize, SetupData) != EFI_SUCCESS )
return 0x9001;
GetSetupXtuBufferAddress(&SetupXtuBufferAddress);
SetMem(Buffer, 0xBA, 0);
CopyMem(Buffer, (SetupXtuBufferAddress + 12), 0xBA);
...
if ( RbxRegister->Signature == '2DB$' )
{
Length = RbxRegister->Length;
if ( Length == End )
{
if ( RbxRegister->MajorRev == 2 || !RbxRegister->MinorRev )
{
// SMRAM write
RbxRegister->Count = Index;
// SMRAM write
sub_175D4(v11, RbxRegister);
v10 = gGlobalStructureSmm;
*(gGlobalStructureSmm + 0x61E8) = 4;
*(v10 + 25056) = 8;
sub_153D0(v10);
return 0;
}
else
{
return 0x8006;
}
}
else if ( Length >= 0xC )
{
// SMRAM write
RbxRegister->Signature = '2DB$';
*&RbxRegister->MajorRev = 2;
RbxRegister->Length = End;
RbxRegister->Count = Index;
if ( Length >= End )
{
if ( Length > End )
{
ResultStatus = 2;
// SMRAM write
sub_175D4(v11, RbxRegister);
}
return ResultStatus;
}
else
{
return 0x8002;
}
}
else
{
return 0x8003;
}
}
else if ( RbxRegister->Signature == '$DB$' )
{
// SMRAM write
RbxRegister->Length = End;
Result = 1;
RbxRegister->Signature = '2DB$';
*&RbxRegister->MajorRev = 2;
RbxRegister->Count = Index;
}
else
{
return 0x8001;
}
return Result;
}
RbxRegister 변수는 공격자가 SMM 실행 직전에 CPU의 RBX 레지스터에 미리 주입해 둔 값을 그대로 담고 있다.
SMM은 OS와 격리된 보안 영역이지만, 데이터를 주고받을 때 CPU 레지스터를 하는 구조 탓에 공격자가 입력값을 제어할 수 있다.
그런데 위 코드를 보면 RbxRegister가 가리키는 주소가 안전한지 검증하는 과정이 전혀 없으며, 따라서 공격자가 이 주소를 SMRAM 내부나 중요 영역으로 지정할 경우 검증 없이 값을 쓰게 되어 SMRAM 오염이 발생
물론 Signature == '2DB$'와 같은 검사 로직이 존재하지만 공격자가 해당 검사 코드가 위치한 메모리 영역 자체를 덮어쓰는 방식으로 이를 우회할 수 있어 취약점을 근본적으로 막지는 못함
2. DXE 런타임 취약점 (Ring 0)
CVE-2025-3052
- NVD: https://nvd.nist.gov/vuln/detail/CVE-2025-3052
- Microsoft 3rd Party UEFI CA로 서명된 드라이버에서 발견된 취약점으로, 공격자가 조작한 NVRAM 변수 내용을 검증 없이 메모리 포인터로 사용하여 임의 주소 쓰기를 유발하고, Secure Boot를 우회할 수 있음
- Microsoft UEFI CA 2011는 리눅스용 Shim 부트로더 서명에 사용되는 것과 동일한 키로, 사실상 전 세계 대부분의 PC가 이 드라이버를 신뢰하고 실행
- Secure Boot가 활성화된 환경에서도 손쉽게 취약한 드라이버를 로드
공격 조건
OS 관리자 권한을 가지고 NVRAM 변수를 수정할 수 있어야 함
분석
VulnerableFunction 함수코드
__int16 **VulnerableFunction()
{
EFI_RUNTIME_SERVICES *RuntimeServices; // r10
__int16 **v1; // rsi
char v2; // al
char v3; // al
__int64 param3_low; // rdx
char param1; // r11
H2oIhisiParams *v6; // rbx
H2oIhisiParams *v7; // rcx
__int64 v8; // rbx
__int64 (__fastcall **i)(); // rdi
__int64 v11; // [rsp+40h] [rbp+8h] BYREF
RuntimeServices = gST->RuntimeServices;
v1 = 0LL;
v11 = 8LL;
if ( (RuntimeServices->GetVariable)(L"IhisiParamBuffer", &H2O_IHIS_PARAM_BUFFER_GUID, 0LL, &v11, &ParamBuffAddr) < 0 )
{
v2 = byte_F795;
}
else
{
if ( dword_70C >= 7 )
sub_DB0C(L"mIsParamsBufferSupported: 0x%x\n", byte_F795);
v2 = 1;
byte_F795 = 1;
}
if ( !v2 )
{
v3 = sub_F6D2(&dword_F790, &qword_F798);
param3_low = dword_F790;
param1 = v3;
goto LABEL_10;
}
v6 = ParamBuffAddr;
v7 = ParamBuffAddr;
// MULTIPLE ARBITRARY WRITE
ParamBuffAddr->param3 = 0LL;
v7->param5 = 0LL;
v7->param6 = 0LL;
v7->param1 = 0x83EFLL;
v7->param2 = '$H2O';
v7->param4 = 0xB2LL;
sub_F6FB(v7);
param1 = v6->param1;
if ( !param1 )
{
param3_low = LODWORD(v6->param3);
dword_F790 = v6->param3;
qword_F798 = v6->param4;
LABEL_10:
if ( !param1 )
{
if ( dword_70C >= 7 )
{
sub_DB0C(L"CmdBufAddress: 0x%x\n", param3_low);
if ( dword_70C >= 7 )
sub_DB0C(L"CmdBufSize: 0x%x\n", qword_F798);
}
byte_F794 = 1;
}
}
v8 = 0LL;
for ( i = off_6C8; !*i || (*i)() != 1; i += 5 )
{
v8 = (v8 + 1);
if ( v8 )
return v1;
}
v1 = &(&off_6C0)[5 * v8];
if ( v1 && dword_70C >= 7 )
sub_DB0C(L"Platform = %s\n", *v1);
return v1;
}
// 1.공격자의 입력값을 NVRAM에서 읽어옴
(RuntimeServices->GetVariable)(L"IhisiParamBuffer", ..., &ParamBuffAddr);
// 검증 로직 없음
// 2. 읽어온 값을 포인터로 변환
v6 = ParamBuffAddr;
v7 = ParamBuffAddr;
// 3. 공격자가 지정한 주소에 값을 씀
ParamBuffAddr->param3 = 0LL; // Target Address + Offset에 0 쓰기
v7->param5 = 0LL; // Target Address + Offset에 0 쓰기
v7->param1 = 0x83EFLL; // 특정 값 쓰기
- RuntimeServices를 통해 "IhisiParamBuffer"라는 이름의 NVRAM 변수 값을 읽어 ParamBuffAddr에 저장
- 별도의 검증 과정 없이 ParamBuffAddr 값을 포인터(v6, v7)로 사용
- 해당 포인터가 가리키는 메모리 주소에 0LL 등의 값을 씀
문제점
예를 들어, 공격자가 NVRAM 변수를 gSecurity2와 같은 이미지 검증 프로토콜의 주소로 설정할 경우, 드라이버가 실행될 때 해당 프로토콜 포인터가 0(NULL)으로 초기화돼버린다.
위 코드에서는 읽어온 값이 유효한 메모리 영역인지 검증하는 과정이 없다. 즉, NVRAM에 적힌 값을 무조건 포인터로 신뢰하고 데이터를 덮어쓰기 때문에, 결과적으로 UEFI 시스템은 서명 검증을 수행할 객체가 사라졌다고 판단하게 된다. 이로 인해 Secure Boot 검증 절차가 생략되고, 서명되지 않은 악성 부트로더가 실행될 수 있다.
해결 방안
해당 드라이버는 정식 서명되었으므로 단순히 삭제하는 것으로는 부족하다. UEFI 펌웨어의 DBX에 해당 파일의 해시를 등록하여 로드를 원천 차단해야함
'CS > Graduation project' 카테고리의 다른 글
| [UEFI] 리눅스(WSL) 환경에서 EDK2 빌드 및 실행 (0) | 2026.02.14 |
|---|---|
| [UEFI] EDK2를 활용한 Stack Buffer Overflow 실습 (0) | 2026.02.02 |
| CVE-2023-45230: UEFI DHCPv6 드라이버 힙 오버플로우 (0) | 2026.01.19 |
| [UEFI] UEFI DRIVERS (1) | 2026.01.12 |
| [UEFI] Driver Execution Environment (DXE) (0) | 2026.01.04 |