
DKOM을 통한 wfshbr64.sys 로컬 권한 상승에 대한 CVE-2022-42046 개념 증명
CVE-2022-42046 wfshbr64.sys 로컬 권한 상승의 개념 증명
wfshbr64.sys와 wfshbr32.sys의 특수 제작된 페이로드를 통해 임의 사용자가 임의의 EPROCESS 오프셋 및 플래그 값으로 비트 연산을 수행하여 EPROCESS.Protection 및 EPROCESS.SignatureLevel 플래그를 조작함으로써 게임 프로세스를 CodeGen Full 보호 수준으로 의도적으로 상승시킬 수 있습니다 (기능으로서의 보안 취약점).
이 드라이버는 Microsoft Hardware Program을 통해 제출된 Microsoft 하드웨어 호환성 게시자에 의해 서명되었습니다.
이 프로젝트는 @DoranekoSystems와 공동 연구되었습니다.
MIT. LICENSE 참조
ObRegisterCallbacks를 사용하세요. 좋은 예제는 여기에 있습니다.완료 후 IRP를 참조하지 마십시오. 드라이버 검증기를 활성화한 경우 적발됩니다.
IofCompleteRequest(Irp, IO_NO_INCREMENT); // IRP is freed here
return Irp->IoStatus.Status;
대신 지역 변수를 사용해야 합니다.
NTSTATUS status = STATUS_SUCCESS;
Irp->IoStatus.Status = status;
IofCompleteRequest(Irp, IO_NO_INCREMENT); // IRP is freed here
return status;
IoGetCurrentProcess의 반환 값에 대해 널 포인터를 확인하고 있는 것으로 보이지만, 설계상 널 포인터를 반환하지 않으므로 확인할 필요가 없습니다.
PEPROCESS CurrentProcess = IoGetCurrentProcess();
if ( !CurrentProcess ) // no need to check for null pointer
break;
보고서 제출 후 얼마 지나지 않아 개발자는 보안 취약점을 기능으로 만드는 것을 중단하는 대신 첫 번째 PoC를 무력화하기 위해 은밀한 '추가 검증'을 구현했습니다.
다음에 검사가 추가되었습니다:
IOCTL_WFSHBR_REMOVE_FLAGIOCTL_WFSHBR_ADD_FLAGIOCTL_WFSHBR_AND_FLAGcase IOCTL_WFSHBR_ADD_FLAG: // 0xAA013884
if ( !KwfsVerifyCaller(Buffer) ) // verify caller
break;
- if ( Buffer->ArbitraryEProcessOffset >= 0x1000 ) // offset limitation check
+ if ( !KwfsVerifyOffsetAndFlags(Buffer->ArbitraryEProcessOffset,
+ Buffer->DesiredFlags) ) // verify the offset and flags
break;
*(ULONG*)(IoGetCurrentProcess() + Buffer->ArbitraryEProcessOffset) |= Buffer->DesiredFlags;
이 루틴은 클라이언트가 EPROCESS 수정을 요청할 때마다 호출되도록 설계되었으며, 이 PoC의 ArbitraryEProcessOffset 필드에서 제공된 Offset과 DesiredFlags 필드에서 제공된 Flags의 검증을 수행합니다.
검증은 제공된 플래그의 모든 비트 필드에서 1 비트를 세는 방식으로 매우 간단하며, 개수가 8을 초과하면 실패합니다.
가능한 플래그 패턴 맵은 단 네 가지입니다:
22 00 00 0000 22 00 0000 00 22 0000 00 00 22즉, 다음 작업을 4번 수행하면 적어도 한 번의 시도는 성공할 것이 보장됩니다:
ArbitraryEProcessOffset 필드를 인덱스만큼 뺍니다: offset - index,DesiredFlags 필드의 비트를 조정합니다: flag << (index * 8).오프셋이 감소하므로 비트필드 조정으로 인해 비트 연산자에서 오프셋이 조정됩니다.
*(ULONG*)(IoGetCurrentProcess() + offset) |= flags;
*(ULONG*)(IoGetCurrentProcess() + offset) &= ~flags;
시도를 수행하고 새로운 트릭을 무력화하는 WfsProtectProcessSupreme 및 WfsUnprotectProcessSupreme 함수를 추가했습니다.
enum KwfsState {
KwfsStateOnceCall = 0,
KwfsStateNeedsValueEquality = 1,
KwfsStateValueHasBeenSet = 2,
};
bool KwfsVerifyOffsetAndFlags(_In_ ULONG offset, _In_ ULONG offset flags)
{
if (KwfsState::KwfsState == KwfsState::KwfsStateOnceCall) {
g_KwfsVerifyState = KwfsState::KwfsStateValueHasBeenSet;
g_KwfsVerifyStateOffset = offset;
g_KwfsVerifyStateFlags = flags;
if (offset < 0x1000) { // offset limitation check moved here
auto bitcount = 0;
for (auto i = 0; i < 32; ++i) { // count `1` bits in flags
if (flags & (1 << i)) {
++bitcount;
}
}
if (bitcount <= 8) { // count must less than nine
g_KwfsVerifyState = 1;
return true;
}
}
}
else
{
if (g_KwfsVerifyState != KwfsState::KwfsStateValueHasBeenSet
|| offset != g_KwfsVerifyStateOffset
|| flags != g_KwfsVerifyStateFlags) {
return false;
}
}
return false;
}