[하루한줄] AMD atdcm64a.sys의 arbitrary pointer dereference으로 인한 LPE 취약점
URL
Target
- Radeon Software Revision Number Adrenalin Edition 18.12.1.1 Optional
Explain
취약점은 총 2개이고 AMD Adrenalin Edition의 atdcm64a.sys 드라이버에서 발생합니다.
First Vulnerability
Irp를 통해 특정 IOCTL 코드를 처리하는 로직으로 들어가면 유저로부터 전달되는 데이터를 그대로 사용합니다.
systemBuffer_9 = Irp->AssociatedIrp.SystemBuffer;
v80 = __readmsr(*(_DWORD *)systemBuffer_9); // arbitrary read msr
*(QWORD *)((char *)systemBuffer_9 + 12) = v80;
*(QWORD *)((char *)systemBuffer_9 + 4) = v80;
첫 번째 취약점은 사용자가 전달한 systembuffer를 포인터로 사용해 임의의 MSR 읽습니다.
MSR(Model-Specific Register)은 CPU 내부에 존재하는 특수한 레지스터로, 프로세서의 특정한 하드웨어 동작을 제어하거나 상태 정보를 읽기 위해 사용됩니다. MSR은 CPU 아키텍처에 따라 다르며, 특정 기능을 제어하는 레지스터 세트입니다. 주로 커널 드라이버와 같은 낮은 레벨의 소프트웨어가 MSR을 통해 프로세서의 고급 기능을 다루거나 상태를 모니터링하는 데 사용됩니다.
MSR은 CPU 내부의 다양한 설정과 상태를 저장하는 레지스터이므로, 특정 MSR을 읽거나 조작할 경우 시스템 보안 및 안정성에 영향을 줄 수 있습니다. 다음은 MSR에서 읽을 경우 위험할 수 있는 주요 정보들입니다.
- Time Stamp Counter (TSC) MSR
- MSR 주소: 0x10
- 설명: 이 레지스터는 CPU 클록 카운트를 제공하며, 이를 통해 높은 정밀도의 타이밍 정보를 얻을 수 있습니다.
- 위험성: 공격자가 TSC 정보를 활용해 사이드 채널 공격을 수행할 수 있습니다. 예를 들어, 특정 코드가 실행되는 시간을 정밀하게 측정하여 CPU 캐시 상태를 추측하거나 암호화 작업의 타이밍 정보를 얻어내는 등 간접적으로 민감한 정보를 추출할 수 있습니다.
- IA32_SYSENTER_EIP, IA32_SYSENTER_CS, IA32_SYSENTER_ESP
- MSR 주소: 0x176, 0x174, 0x175
- 설명: 이 레지스터들은 시스템 호출을 처리하는 엔트리 포인트 주소와 관련된 정보를 저장합니다.
- 위험성: 커널의 엔트리 포인트에 대한 정보는 커널의 제어 흐름을 이해하는 데 도움이 되므로, 공격자가 이를 이용해 ROP(Return-Oriented Programming) 등의 공격을 수행할 가능성이 높습니다. 특히 엔트리 포인트 주소가 유출되면, 주소 공간 배치 난수화(ASLR)를 우회하는 데 악용될 수 있습니다.
- IA32_DEBUGCTL MSR
- MSR 주소: 0x1D9
- 설명: 디버그와 관련된 설정을 저장하는 레지스터로, 프로세서의 디버깅 기능을 제어합니다.
- 위험성: 디버깅 정보는 시스템의 세부적인 동작을 알 수 있게 하므로, 공격자는 이를 통해 메모리 접근 패턴이나 컨텍스트 스위칭을 추적할 수 있습니다. 이는 커널에서 민감한 작업을 하는 경우 그 과정을 간접적으로 알아내는 데 사용될 수 있습니다.
- IA32_MTRR (Memory Type Range Registers)
- MSR 주소: 0x200~0x20F (범위)
- 설명: 메모리 영역의 캐시 특성을 정의하는 레지스터입니다.
- 위험성: 공격자가 MTRR 정보를 통해 메모리 매핑을 파악하고 특정 메모리 영역의 캐시 특성을 조작하면, 메모리 접근 속도를 제어하여 타이밍 공격이나 캐시 충돌 공격을 수행할 수 있습니다. 메모리 접근 방식을 최적화하여 보안 우회 기법에 사용될 수도 있습니다.
- IA32_PERF_STATUS, IA32_PERF_CTL (Performance Monitoring)
- MSR 주소: 0x198, 0x199
- 설명: CPU의 성능 상태와 성능 제어와 관련된 레지스터입니다.
- 위험성: 성능 카운터 정보를 통해 CPU의 상태와 명령어 실행 빈도 등을 알 수 있습니다. 이를 통해 타이밍 공격이나 사이드 채널 공격을 수행할 수 있습니다. 예를 들어, 특정 함수가 실행될 때 CPU 사용량 변화를 추적하여 암호화 작업과 같은 민감한 정보가 발생하는 시점을 분석할 수 있습니다.
- APIC MSR (Advanced Programmable Interrupt Controller)
- MSR 주소: 0x1B, 0xFEE00000~0xFEEFFFFF (APIC MMIO 주소 범위)
- 설명: APIC은 프로세서 간 인터럽트를 제어하는 데 사용되며, 로컬 APIC과 관련된 설정을 포함하고 있습니다.
- 위험성: 공격자가 APIC를 통해 CPU 간 인터럽트 통신을 조작하거나 APIC의 설정을 바꾸면, 시스템의 인터럽트 핸들링을 방해하여 서비스 거부(Denial of Service, DoS) 공격을 유발할 수 있습니다. 잘못된 APIC 설정으로 인한 시스템 불안정성도 큰 위험 요소입니다.
- IA32_FEATURE_CONTROL MSR
- MSR 주소: 0x3A
- 설명: VT-x(가상화)와 같은 기능을 활성화하거나 비활성화하는 데 사용됩니다.
- 위험성: 이 레지스터를 통해 가상화 기능을 제어할 수 있습니다. 공격자가 이 값을 악의적으로 조작하면 하드웨어 가상화 기반의 보안 기능을 우회하거나 비정상적인 가상화 환경을 구성할 수 있습니다.
- Firmware Control MSR (ACPI 관련 MSR)
- 설명: ACPI와 같은 펌웨어 관련 설정 정보를 포함하고 있습니다.
- 위험성: ACPI나 전원 관리와 관련된 설정을 악용하면 시스템의 전원 관리 정책을 제어하여 과열 문제를 유도하거나 시스템의 동작 상태를 조작할 수 있습니다.
Second Vulnerability
드라이버에서는 InnerIrpIoCtlHandler() 및 InnerIrpIoCtlHandler()를 거져 callDriver()라는 함수까지 유저 버퍼를 그대로 전달합니다. callDriver()에서는 인자로 전달된 유저 버퍼를 IoGetAttachedDeviceReference()에 전달하고 반환된 값을 IofCallDriver()에 전달합니다.
int64 __fastcall callDriver(PDEVICE_OBJECT DeviceObject, int64 a2, int a3, UNICODE_STRING *a4, int a5, int a6)
{
_DEVICE_OBJECT *AttachedDeviceReference; // rdi
PIRP Irp; // rax
unsigned int Status; // ebx
struct _IO_STACK_LOCATION *CurrentStackLocation; // rcx
struct _IO_STATUS_BLOCK IoStatusBlock; // [rsp+40h] [rbp-38h] BYREF
struct _KEVENT event; // [rsp+50h] [rbp-28h] BYREF
KeInitializeEvent(&event, NotificationEvent, 0);
AttachedDeviceReference = IoGetAttachedDeviceReference(DeviceObject); [1]
Irp = IoBuildSynchronousFsdRequest(IRP_MJ_PNP, AttachedDeviceReference, 0LL, 0, 0LL, &event, &IoStatusBlock);
if (Irp) {
CurrentStackLocation = Irp->Tail.Overlay.CurrentStackLocation;
CurrentStackLocation[-1].Parameters.FileSystemControl.OutputBufferLength = 0;
CurrentStackLocation[-1].Parameters.FileSystemControl.InputBufferLength = a4;
*(_QWORD *)&CurrentStackLocation[-1].MinorFunction = (a3 != 0) + 15;
CurrentStackLocation[-1].Parameters.FileSystemControl.FsControlCode = a5;
CurrentStackLocation[-1].Parameters.FileSystemControl.Type3InputBuffer = a6;
Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
Status = IofCallDriver(AttachedDeviceReference, Irp); [2]
if (Status == STATUS_PENDING) {
KeWaitForSingleObject(&event, Executive, 0, 0, 0LL);
Status = IoStatusBlock.Status;
}
}
else {
Status = STATUS_INSUFFICIENT_RESOURCES;
}
ObDereferenceObject(AttachedDeviceReference);
return Status;
}
DeviceObject의 DriverObject 부분의 MajorFunction 배열 내부에 있는 함수 포인터를 호출합니다. DeviceObject를 제어할 수 있으므로 MajorFunction 배열 내부의 함수 포인터를 제어할 수 있습니다.
{
_IO_STACK_LOCATION *v2; // rax
__int64 MajorFunction; // r8
if ( IopDispatchCallDriver )
{
if ( IopDispatchCallDriver == 3 )
return IopPerfCallDriver((PADAPTER_OBJECT)DeviceObject);
else
return IovCallDriver(DeviceObject);
}
else
{
if ( --Irp->CurrentLocation <= 0 )
KeBugCheckEx(0x35u, (ULONG_PTR)Irp, 0LL, 0LL, 0LL);
v2 = Irp->Tail.Overlay.CurrentStackLocation - 1;
Irp->Tail.Overlay.CurrentStackLocation = v2;
MajorFunction = v2->MajorFunction;
v2->DeviceObject = DeviceObject;
if ( (_BYTE)MajorFunction == 22 && (unsigned __int8)(v2->MinorFunction - 2) <= 1u )
return IopPoHandleIrp(Irp);
else
return ((__int64 (__fastcall *)(PDEVICE_OBJECT))DeviceObject->DriverObject->MajorFunction[MajorFunction])(DeviceObject); [3]
}
}
본 글은 CC BY-SA 4.0 라이선스로 배포됩니다. 공유 또는 변경 시 반드시 출처를 남겨주시기 바랍니다.