
Prova de conceito de exploit para CVE-2022-42046 demonstrando escalonamento local de privilégios via manipulação direta de objetos do kernel dos flags Protection e SignatureLevel do EPROCESS no driver wfshbr64.sys.
CVE-2022-42046 Prova de conceito de escalonamento de privilégio local do wfshbr64.sys
O payload especialmente criado do wfshbr64.sys e wfshbr32.sys permite que um usuário arbitrário realize operações bit a bit com deslocamento de EPROCESS arbitrário e valor de flags para propositalmente elevar o processo do jogo para a proteção CodeGen Full ao manipular as flags EPROCESS.Protection e EPROCESS.SignatureLevel (brecha de segurança como funcionalidade).
O driver é assinado pela editora de compatibilidade de hardware da Microsoft, submetido através do Programa de Hardware da Microsoft.
Este projeto foi pesquisado em conjunto com @DoranekoSystems
MIT. Veja LICENSE
ObRegisterCallbacks em vez de elevar forçosamente a proteção do processo realizando manipulação direta de objetos do kernel. Há um bom exemplo aqui.Não referencie IRP após a conclusão. Se você tiver o verificador de driver habilitado, será detectado.
IofCompleteRequest(Irp, IO_NO_INCREMENT); // IRP is freed here
return Irp->IoStatus.Status;
Em vez disso, você deve usar uma variável local.
NTSTATUS status = STATUS_SUCCESS;
Irp->IoStatus.Status = status;
IofCompleteRequest(Irp, IO_NO_INCREMENT); // IRP is freed here
return status;
Parece que você está verificando ponteiro nulo contra o valor de retorno de IoGetCurrentProcess, mas ele nunca retorna ponteiro nulo por design, então você não precisa verificá-lo.
PEPROCESS CurrentProcess = IoGetCurrentProcess();
if ( !CurrentProcess ) // no need to check for null pointer
break;
Algum tempo após o relatório, o desenvolvedor implementou uma "verificação adicional" sorrateira para derrotar nossa primeira PoC, em vez de desistir de criar brechas de segurança como funcionalidade.
Verificações adicionadas a:
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;
Esta rotina é projetada para ser chamada toda vez que o cliente solicita modificação do EPROCESS, e realiza a verificação do Offset fornecido pelo campo ArbitraryEProcessOffset nesta PoC ― e também das Flags fornecidas pelo campo DesiredFlags nesta PoC.
A verificação é bastante simples: conta os bits 1 em cada campo de bits das flags fornecidas e, se a contagem for maior que oito, falha.
O mapa de padrões de flags possíveis é apenas quatro:
22 00 00 0000 22 00 0000 00 22 0000 00 00 22Dito isso, realizar as seguintes operações 4 vezes pode garantir que pelo menos uma tentativa seja bem-sucedida:
ArbitraryEProcessOffset pelo índice: offset - index,DesiredFlags pelo índice: flag << (index * 8).O deslocamento é decrementado, então o ajuste do campo de bits faria o deslocamento se ajustar nos operadores bit a bit.
*(ULONG*)(IoGetCurrentProcess() + offset) |= flags;
*(ULONG*)(IoGetCurrentProcess() + offset) &= ~flags;
Adicionamos as funções WfsProtectProcessSupreme e WfsUnprotectProcessSupreme que realizam a tentativa e derrotam o novo truque.
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;
}