项目地址
https://github.com/kkent030315/CVE-2022-42046
邪恶的Wfshbr
CVE-2022-42046 wfshbr64.sys 本地权限提升的概念证明
wfshbr64.sys 和 wfshbr32.sys 特制的有效负载允许任意用户使用任意 EPROCESS 偏移量和标志值执行按位运算,通过操纵 EPROCESS.Protection
和 EPROCESS.SignatureLevel
标志(安全漏洞作为一项功能)有意将游戏进程提升到 CodeGen 完全保护。
该驱动程序由通过 Microsoft 硬件程序提交的 Microsoft 硬件兼容性发布者签名。
该项目与@DoranekoSystems 共同研究
这里 有一个丰富的 Rust CLI 版本
- https://www.virustotal.com/gui/file/b8807e365be2813b7eccd2e4c49afb0d1e131086715638b7a6307cd7d7e9556c
- https://www.virustotal.com/gui/file/89698cad598a56f9e45efffd15d1841e494a2409cc12279150a03842cd6bb7f3
建议(针对开发者)
- 使用
ObRegisterCallbacks
而不是通过执行直接内核对象操作来强制提升进程保护。 这里 有一个很好的例子 。
2.IRP
完成后不要引用 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;
3.上下文过程
看起来您正在根据 的返回值检查空指针 IoGetCurrentProcess
,但它从不按设计返回空指针,因此您不必检查它。
PEPROCESS CurrentProcess = IoGetCurrentProcess();
if ( !CurrentProcess ) // no need to check for null pointer
break;
技巧
报告发布后不久,开发人员实施了偷偷摸摸的“附加验证”来击败我们的第一个 PoC,而不是放弃将安全漏洞作为一项功能。
检查添加到:
IOCTL_WFSHBR_REMOVE_FLAG
IOCTL_WFSHBR_ADD_FLAG
IOCTL_WFSHBR_AND_FLAG
case 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;
KwfsVerifyOffsetAndFlags
该例程旨在每次客户端请求修改 EPROCESS 时调用,并执行验证 Offset
provided by ArbitraryEProcessOffset
field in this PoC ― 以及 Flags
provided by DesiredFlags
field in this PoC。
验证非常简单,因为它对 1
提供的标志的每个位字段中的位进行计数,如果计数大于 8,它将失败。
可能的标志模式图只有四个:
22 00 00 00
00 22 00 00
00 00 22 00
00 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;
}
没有回复内容