说明

gdrv.sys 是技嘉的一个驱动,存在任意地址读写的漏洞(CVE-2018-19320),https://seclists.org/fulldisclosure/2018/Dec/39。 可以使用其关闭CI!g_CiOptions/nt!g_CiEnabled。

Windows 上的驱动签名校验由单个二进制文件 ci.dll (=> %WINDIR%\System32\) 管理。在 Windows 8 之前,CI 导出一个全局布尔变量 g_CiEnabled,无论是启用签名还是禁用签名,这都是不言自明的。在 Windows 8+ 中,g_CiEnabled 被另一个全局变量 g_CiOptions 替换,它是标志的组合(最重要的是 0x0=禁用、0x6=启用、0x8=测试模式)。

gdrv.sys漏洞利用

由那边公开文章可知,0xC3502808 内置memcpy功能,随意逆向gdrv.sys,找到0xC3502808分支。

把代码梳理下,就变成这样了。

v2 = *(__int64 **)(a2 + 24);
  *(_QWORD *)(a2 + 56) = 0i64;
  if ( !v2 )
    return 3221225485i64;
  v3 = *v2;
  v4 = *((unsigned int *)v2 + 4);
  v5 = v2[1];
  DbgPrint("Dest=%x,Src=%x,size=%d", *v2, v5, (unsigned int)v4);
 if ( (_DWORD)v4 )
  {
    v6 = v5 - v3;
    v7 = v4;
    do
    {
      *(_BYTE *)(v3 - 1) = *(_BYTE *)(v6 + v3);
      v3++;
      --v7;
    }
    while ( v7 );
  }

这段代码作用就是将v5地址的内容拷贝到v3,拷贝大小为v4,而v5来自与v2指针的内容。这样就形成了任意地址读写漏洞,v2、v5、v4都来自与a2,a2是IRP作为参数传递进来的。

再结合上文提到公开文章里面的poc,利用方式就出来了。

定义数据结构
 typedef struct _GIOMEMInput
{
    ULONG_PTR Dst; //目的地址
    ULONG_PTR Src; //原地址
    ULONG Size; //大小
} GIOMEMInput, *PGIOMEMInput;
NtDeviceIoControlFile(DeviceHandle,nullptr,nullptr,nullptr,&IoStatusBlock,IOCTL_GIO_MEMCPY,&MemcpyInput,sizeof(MemcpyInput),nullptr,0);

读取内核地址内容,可以把内核地址的内容写入到变量中。

写入内核地址内容,可以把变量写入内核地址。

bypass dse关闭驱动校验

已经找到一个任意地址写入漏洞,后面就是找到控制驱动校验变量的地址。

在windows10 与window7 变量位置不一样。

windows10 ----> c:\windows\System32\CI.dll!g_CiOptions

windows7 ----> c:\windows\System32toskrnl.exe!g_CiEnabled

这里只说CI.dll!g_CiOptions,不过找ntoskrnl.exe!g_CiEnabled的方法大同小异。

在CI.dll的导出函数CiInitialize中调用了CipInitialize函数。

在CipInitialize中存在 mov cs:g_CiOptions, ecx,可以拿到g_CiOptions在内核的地址,即可以计算出偏移地址。

第一步:将CI.dll加载到内存空间。

第二步:获得原本加载的CI.dll的基址

NtQuerySystemInformation的第一个参数为11,即可遍历系统模块信息找到CI.dll的基址。

第三步: 反汇编机器指令

介绍一个工具HDE64(https://github.com/Cerbersec/HDE64),可以反汇编机器指令.

先看一下,对windows10(18363) CI.dll CiInitialize反汇编结果。

我们要进入的是CipInitialize函数,所以就要注意call(0xE8),因为调用了两次call,所以有用的是第二次调用call。

而在windows10(19044)中,是第三次调用call(0xE8),所以19044和18363代码不同。

而在CipInitialize函数里面,都是一样的流程,都是获取mov cs:g_CiOptions, ecx。

这样便获取到了控制驱动校验变量的地址,后面就直接使用上面提到的任意地址写入漏洞,对变量进行覆盖就可以了。(0代表禁用)

注意:因为 DSE 受 PatchGuard 保护,所以覆盖后将驱动加载进去要尽快还原回来,以免出现蓝屏。

验证截图