CVE-2022-42046 关于wfshbr64.sys通过DKOM进行本地提权的POC

项目地址

https://github.com/kkent030315/CVE-2022-42046

邪恶的Wfshbr

CVE-2022-42046 wfshbr64.sys 本地权限提升的概念证明

wfshbr64.sys 和 wfshbr32.sys 特制的有效负载允许任意用户使用任意 EPROCESS 偏移量和标志值执行按位运算,通过操纵 EPROCESS.ProtectionEPROCESS.SignatureLevel 标志(安全漏洞作为一项功能)有意将游戏进程提升到 CodeGen 完全保护。

该驱动程序由通过 Microsoft 硬件程序提交的 Microsoft 硬件兼容性发布者签名。

该项目与@DoranekoSystems 共同研究

这里 有一个丰富的 Rust CLI 版本

建议(针对开发者)

  1. 使用 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;

我们添加了 执行尝试并击败新技巧的功能 WfsProtectProcessSupremeWfsUnprotectProcessSupreme

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;
}

 

请登录后发表评论

    没有回复内容