
CVE-2022-42046 Proof of Concept of wfshbr64.sys local privilege escalation via DKOM
CVE-2022-42046 PoC повышения привилегий локально через wfshbr64.sys
wfshbr64.sys и wfshbr32.sys специально сформированный payload позволяет любому пользователю выполнять побитовые операции с произвольным смещением и значениями флагов EPROCESS для намеренного повышения уровня защиты игрового процесса до CodeGen Full путем манипуляции флагами EPROCESS.Protection и EPROCESS.SignatureLevel (дыра в безопасности как функция).
Драйвер подписан издателем оборудования Microsoft, который предоставляется через Microsoft Hardware Program.
Этот проект был совместно исследован с @DoranekoSystems
MIT. См. LICENSE
ObRegisterCallbacks вместо принудительного повышения защиты процессов путем прямого манипулирования объектами ядра. Хороший пример есть здесь.Не ссылайтесь на IRP после его завершения. Если у вас включён верификатор драйверов, вы будете пойманы.
IofCompleteRequest(Irp, IO_NO_INCREMENT); // IRP освобождается здесь
return Irp->IoStatus.Status;
Вместо этого используйте локальную переменную.
NTSTATUS status = STATUS_SUCCESS;
Irp->IoStatus.Status = status;
IofCompleteRequest(Irp, IO_NO_INCREMENT); // IRP освобождается здесь
return status;
Похоже, вы проверяете нулевой указатель для возвращаемого значения IoGetCurrentProcess, но оно никогда не возвращает нулевой указатель по замыслу, так что вам не нужно его проверять.
PEPROCESS CurrentProcess = IoGetCurrentProcess();
if ( !CurrentProcess ) // нет необходимости проверять нулевой указатель
break;
Через некоторое время после отчета разработчик реализовал скрытую «дополнительную проверку», чтобы обойти наш первый PoC, вместо того чтобы отказаться от использования дыр в безопасности как функции.
Проверки добавлены к:
IOCTL_WFSHBR_REMOVE_FLAGIOCTL_WFSHBR_ADD_FLAGIOCTL_WFSHBR_AND_FLAGcase IOCTL_WFSHBR_ADD_FLAG: // 0xAA013884
if ( !KwfsVerifyCaller(Buffer) ) // проверка вызывающего
break;
- if ( Buffer->ArbitraryEProcessOffset >= 0x1000 ) // проверка ограничения смещения
+ if ( !KwfsVerifyOffsetAndFlags(Buffer->ArbitraryEProcessOffset,
+ Buffer->DesiredFlags) ) // проверка смещения и флагов
break;
*(ULONG*)(IoGetCurrentProcess() + Buffer->ArbitraryEProcessOffset) |= Buffer->DesiredFlags;
Эта процедура предназначена для вызова каждый раз, когда клиент запрашивает изменение EPROCESS, и выполняет проверку Offset, предоставленного полем ArbitraryEProcessOffset в этом PoC, а также Flags, предоставленных полем DesiredFlags в этом PoC.
Проверка довольно проста: она подсчитывает биты 1 в каждом битовом поле предоставленных флагов, и если количество больше восьми, проверка не пройдена.
Возможная карта шаблонов флагов состоит всего из четырех:
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) { // проверка ограничения смещения перенесена сюда
auto bitcount = 0;
for (auto i = 0; i < 32; ++i) { // подсчет битов `1` во флагах
if (flags & (1 << i)) {
++bitcount;
}
}
if (bitcount <= 8) { // количество должно быть меньше девяти
g_KwfsVerifyState = 1;
return true;
}
}
}
else
{
if (g_KwfsVerifyState != KwfsState::KwfsStateValueHasBeenSet
|| offset != g_KwfsVerifyStateOffset
|| flags != g_KwfsVerifyStateFlags) {
return false;
}
}
return false;
}