
CVE-2022-42046 Prova di concetto dell'elevazione locale dei privilegi di wfshbr64.sys tramite DKOM
CVE-2022-42046 Proof of Concept di elevazione locale dei privilegi di wfshbr64.sys
Il payload appositamente creato di wfshbr64.sys e wfshbr32.sys consente a un utente arbitrario di eseguire operazioni bitwise con offset EPROCESS arbitrari e valori di flag per elevare deliberatamente il processo di gioco alla protezione CodeGen Full manipolando i flag EPROCESS.Protection e EPROCESS.SignatureLevel (buco di sicurezza come funzionalità).
Il driver è firmato da Microsoft Hardware Compatibility Publisher, presentato tramite il Microsoft Hardware Program.
Questo progetto è stato oggetto di una ricerca congiunta con @DoranekoSystems
MIT. Vedi LICENSE
ObRegisterCallbacks invece di elevare con la forza la protezione del processo eseguendo una manipolazione diretta degli oggetti del kernel. C'è un buon esempio qui.Non fare riferimento all'IRP dopo il completamento. Se hai il driver verifier abilitato verrai scoperto.
IofCompleteRequest(Irp, IO_NO_INCREMENT); // IRP is freed here
return Irp->IoStatus.Status;
Dovresti invece usare una variabile locale.
NTSTATUS status = STATUS_SUCCESS;
Irp->IoStatus.Status = status;
IofCompleteRequest(Irp, IO_NO_INCREMENT); // IRP is freed here
return status;
Sembra che tu stia verificando un puntatore nullo rispetto al valore restituito da IoGetCurrentProcess, ma per progettazione non restituisce mai un puntatore nullo, quindi non devi controllarlo.
PEPROCESS CurrentProcess = IoGetCurrentProcess();
if ( !CurrentProcess ) // no need to check for null pointer
break;
Qualche tempo dopo il report, lo sviluppatore ha implementato una subdola "verifica aggiuntiva" per sconfiggere la nostra prima PoC invece di rinunciare a trasformare i buchi di sicurezza in una funzionalità.
Controlli aggiunti 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;
Questa routine è progettata per essere chiamata ogni volta che il client richiede una modifica dell'EPROCESS ed esegue la verifica dell'Offset fornito dal campo ArbitraryEProcessOffset in questa PoC ― e anche dei Flags forniti dal campo DesiredFlags in questa PoC.
La verifica è piuttosto semplice: conta i bit 1 in ogni campo di bit dei flag forniti e se il conteggio è maggiore di otto fallisce.
La mappa dei possibili pattern di flag è solo quattro:
22 00 00 0000 22 00 0000 00 22 0000 00 00 22Detto questo, eseguire le seguenti operazioni 4 volte può garantire che almeno uno dei tentativi abbia successo:
ArbitraryEProcessOffset per indice: offset - index,DesiredFlags per indice: flag << (index * 8).L'offset viene decrementato, quindi la regolazione del campo di bit farebbe sì che l'offset si adatti negli operatori bitwise.
*(ULONG*)(IoGetCurrentProcess() + offset) |= flags;
*(ULONG*)(IoGetCurrentProcess() + offset) &= ~flags;
Abbiamo aggiunto le funzioni WfsProtectProcessSupreme e WfsUnprotectProcessSupreme, che eseguono il tentativo e hanno sconfitto il nuovo trucco.
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;
}