快捷搜索:  as  创意文化园  1874  2035  test  1967  1962  1833

choi baccarat:关于CVE-2020-17087破绽的研究之旅

USDT第三方支付API接口

菜宝钱包(caibao.it)是使用TRC-20协议的Usdt第三方支付平台,Usdt收款平台、Usdt自动充提平台、usdt跑分平台。免费提供入金通道、Usdt钱包支付接口、Usdt自动充值接口、Usdt无需实名寄售回收。菜宝Usdt钱包一键生成Usdt钱包、一键调用API接口、一键无实名出售Usdt。

2020年10月22日,谷歌的平安研究人员在bug.chromium.org上公布了一个有趣的平安讲述,标题为“Issue 2104: Windows Kernel cng.sys pool-based buffer overflow”,大致意思是在CNG.sys中发现了一个平安破绽,而CNG.sys是一个Windows驱动程序,其IOCTL 0x390400导致一个函数容易受到16位整数溢出的影响。现实上,泛起平安问题函数是cng!CfgAdtpFormatPropertyBlock,而且,该函数并未被导出。这引起了我的极大兴趣,缘故原由如下所示:

· 该破绽存在于微软的一个名为CNG.sys的驱动程序

· 影响所有版本的Windows

· 具有WHQL数字署名

· 讲述指出“我们有证据解释,这个破绽正在被用于野外。”

最后一点告诉我,这可以用来实现沙箱逃逸,并将权限升级到System级别。接下来,让我们深入研究一下这个破绽。

破绽剖析

这个破绽是由j00ru公布的,并提供了许多有用的信息,我们可以行使这些信息进一步追查这个破绽。详细来说,他为我们提供的信息包罗:挪用客栈、代码片断,以及部门的!analyze下令。下面显示的就是我们将要使用的挪用客栈信息:

cng!CngDispatch
cng!CngDeviceControl
cng!ConfigIoHandler_Safeguarded
cng!ConfigFunctionIoHandler
cng!_ConfigurationFunctionIoHandler
cng!BCryptSetContextFunctionProperty
cng!CfgAdtReportFunctionPropertyOperation
cng!CfgAdtpFormatPropertyBlock

他还公布了一个PoC,详细地址为https://bugs.chromium.org/p/project-zero/issues/attachmentText?aid=472684。我的设计是举行响应的动态剖析,以确定PoC中种种值的详细寄义,并更好地明白其运行机制,这样的话,当我最先修改或添加一些器械时,我就不会损坏任何器械,或改变通往易受攻击的函数的路径了。为了调试CNG,请执行以下下令:

verifier /flags 1 /driver cng.sys

首先,我们可以为cng!CngDispatch设置一个断点,这样当我到达cng!CfgAdtpFormatPropertyBlock时,我就知道它需要的参数是什么。这对于本文后面先容的针对该破绽的防御方式时,将稀奇有用。

需要注重的是,这是Windows的下一代加密驱动,以是系统中的所有器械都市用到它。因此,在为这个驱动程序设置断点后,就会经常触发这个断点,因此请确保仅在PoC上下文中设置断点,否则就会与其他系统线程发生竞争:

ba e1 /p eprocess cng!CngDispatch

一旦断点被触发,而且当前位于CngDispatch中的话,我们就以行使跟踪挪用手艺,停靠到cng!CngDeviceControl上,这是挪用栈的下一项。现实上,CngDispatch的作用是凭据IRP_MAJOR_FUNCTION来剖析IOCTL信息。由于我们使用的是一个IRP_MJ_DEVICE_CONTROL,以是,我们只关注这部门特殊的代码即可。下面是相关的代码,我们已经使用IDA对其举行了响应的处置,使之更容易明白,更容易操作:

case IRP_MJ_DEVICE_CONTROL:
    ULONG ulIoControlCode = currentStackLocation->Parameters.Read.ByteOffset.LowPart;
    if (METHOD_FROM_CTL_CODE(ulIoControlCode) == METHOD_OUT_DIRECT &&
        currentStackLocation->Parameters.Read.Length)
    {
        --snipped because not important--
    }
    else
    {
        MappedSystemVa = Irp->AssociatedIrp.MasterIrp;
        lpInBuffer = MappedSystemVa;
 
        NumberOfBytes = currentStackLocation->Parameters.Read.Length;
    }
 
    Irp->IoStatus.Status = CngDeviceControl(
        lpInBuffer,
        currentStackLocation->Parameters.Create.Options,
        MappedSystemVa,
        &nOutBufferSize,
        dwIoControlCode,
        Irp->RequestorMode
    );
    Irp->IoStatus.Information = nOutBufferSize;
    break;

简朴来说,上面的代码的作用是检查来自控制代码的方式是否是METHOD_OUT_DIRECT类型,而且验证来自用户模式的变量长度是否为NULL。存在平安问题的IOCTL是0x390400,它使用的是METHOD_BUFFERED方式,以是该IF语句内里的所有内容对我们都不适用,我们可以直接跳转到ELSE语句内里的代码。

现在,我们最先跟踪POC代码。准确来说,是考察第159行之后的代码:

if ( dwIoControlCode == 0x390400 )
    return ConfigIoHandler_Safeguarded(a1, a2, a3);
goto LABEL_58;

由于需要查看a1、a2和a3的值,为此,我们可以借助于调试器:

kd> tc
cng!CngDeviceControl 0x8a:
fffff801`4dc361da e85d0a0000      call    cng!ConfigIoHandler_Safeguarded (fffff801`4dc36c3c)
kd> r rcx, rdx, r8
rcx=ffff8909a52d2000 rdx=0000000000003aab r8=ffff8909a52d2000
kd> db ffff8909a52d2000
ffff8909`a52d2000  4d 3c 2b 1a 00 04 01 00-01 00 00 00 00 00 00 00  M< .............
ffff8909`a52d2010  00 01 00 00 00 00 00 00-03 00 00 00 00 00 00 00  ................
ffff8909`a52d2020  00 02 00 00 00 00 00 00-00 03 00 00 00 00 00 00  ................
ffff8909`a52d2030  00 04 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
ffff8909`a52d2040  00 05 00 00 00 00 00 00-00 06 00 00 00 00 00 00  ................
ffff8909`a52d2050  ab 2a 00 00 00 00 00 00-00 10 00 00 00 00 00 00  .*..............

现在,我们知道a1和a3是指向输入缓冲区的指针,a2似乎是示意变量长度,其值为0x2aab 0x1000。

同时,cng!ConfigIoHandler_Safeguarded还会举行一系列的检查,并用BCryptAlloc分配了两块内存,并将输入缓冲区的内容复制到新分配的一块内存中。

BCryptAlloc是CNG内部的一个函数,它是ExAllocatePoolWithTag/SkAllocatePool的封装器,使用的标签为“bgnC”。

然后,它继续将第二个内存块初始化为零,但我感兴趣的是后面挪用的函数,即IoUnpack_SG_ParamBlock_Header:

__int64 __fastcall IoUnpack_SG_ParamBlock_Header(PDWORD a1, unsigned int a2, PDWORD a3, _QWORD *a4)
{
--snipped--
  if ( a1   2 > (a1   a2) || *a1 != 0x1A2B3C4D )
    return 1i64;
--snipped--
}

我从中获得的主要部门是0x1a2b3c4d这个值,这一定是某个魔术值。若是不是这个值,这个函数就会失败,从而无法到达易受攻击的代码路径。

接下来是cng!ConfigFunctionIoHandler函数,它接收了六个参数,我可以通过调试器识别它们:

kd> tc
cng!ConfigIoHandler_Safeguarded 0xd4:
fffff801`4dc36d10 e81b010000      call    cng!ConfigFunctionIoHandler (fffff801`4dc36e30)
kd> r rcx, rdx, r8, r9
rcx=0000000000010400 rdx=ffff8909a5919000 r8=0000000000003aab r9=ffff8909a52d2000
kd> db ffff8909a5919000
ffff8909`a5919000  4d 3c 2b 1a 00 04 01 00-01 00 00 00 00 00 00 00  M db ffff8909a52d2000
ffff8909`a52d2000  4d 3c 2b 1a 00 04 01 00-01 00 00 00 00 00 00 00  M dqs rsp   20 l2
ffffd904`f7bbf0f0  ffffd904`f7bbf150
ffffd904`f7bbf0f8  ffff8909`a5846000

其中,RCX被设置为该PoC的第二个DWORD,R8被设置为size 0x1000,RDX和R9是分配的缓冲区,内里存放的是我们的数据。对于另外两个值,我无需体贴,并继续向下跟踪,即switch语句。

这里需要注重的是,必须确保我们进入的路径是cng!_ConfigurationFunctionIoHandler函数。您可能已经注重到了,它取的是RCX的高位字,并据此跳转到响应的case x分支。RCX存放的是PoC的第二个DWORD,即0x10400。若是这个寄存器的值不是0x10400,路径就会发生变化,我们就无法到达这个代码路径。另外,switch语句中的其他函数,对于我们来说用处不大,至少从破绽行使的角度来看是这样。下面,我们最先进入BCrypt*函数。

在ConfigurationFunctionIoHandler中,其实有很多与加密设置相关的事情要做,好比建立上下文、删除上下文、设置上下文、设置等等,然则,当loword即是0x10400或者0x400时,就可以直接把我们带到我们感兴趣的地方:

if ( a1 == 0x400 )
    return BCryptSetContextFunctionProperty(
            dwTable,
            pszContext,
            dwInterface,
            pszFunction,
            pszProperty,
            cbValue,
            pbValue
        );

对于这个函数,稍后还会对其举行探讨。下一个函数用于处置加密的事情,但我们在击中了第192行的cng!CfgAdtReportFunctionPropertyOperation。从现在最先,真正有趣的器械就最先涌现了!

if ( a1 && u16Length && usDestination )
{
    v7 = 6 * u16Length;
    v8 = BCryptAlloc((6 * u16Length));
    v9 = v8;
    if ( v8 )
    {
--snipped--

这里就是j00ru所说的破绽的发源地。对于uint16,并没有举行任何类型的输入验证,以验证变量是否被溢出。当需要接受用户的输入时,最好举行需要的平安检查;然而,微软并没有这样做!总之,这里只要将用户界说的值乘以6,就很可能会令这种数据类型发生溢出。例如,0x2aab * 6是0x10002,但在内存中被看作是“2”。

上面就是在平安讲述中指出的易受攻击的函数,下面,我们不妨试一下PoC,看看有没有新的发现。平安研究人员提供的PoC会触发一个PAGE_FAULT_IN_NONPAGED_AREA错误。据我所知,PoC中输入缓冲区的前两个参数是不能修改的,否则会进入差别的代码路径。我还发现,通过修改其他的一些值,也可以进入差别的代码路径,或者直接失败。我虽然知道输入的内容将会存储到缓冲区中,但不知道详细的位置,但实验差别的输入长度之后,发现能够触发缓冲区溢出。无论如何,我仍然无法用受控的数据触发溃逃,以是我想到了一个问题:“我真的需要借助于PoC吗?有没有其他方式来触发这个破绽?”。

意外的发现

于是,我最先检查CNG的导出函数,看看是否有什么器械可以为我所用,效果找到了BCryptSetProperty函数;然则,我没有在导出函数中发现BCryptSetContextFunctionProperty。之后,我最先在MSDN中查找BCryptSetProperty方面的资料,但我看到了更好的器械……详细来说,就是位于统一个挪用栈中的另一个函数:BCryptSetContextFunctionProperty,其原型如下所示:

NTSTATUS BCryptSetContextFunctionProperty(
  ULONG   dwTable,
  LPCWSTR pszContext,
  ULONG   dwInterface,
  LPCWSTR pszFunction,
  LPCWSTR pszProperty,
  ULONG   cbValue,
  PUCHAR  pbValue
);

其中,我最感兴趣的是cbValue和pbValue:

· cbValue:示意pbValue缓冲区的巨细(以字节为单元),也就是将要存储的数据简直切字节数。若是该属性值为字符串,则应将长度增添一个字符,以在需要时存储终止符null。

· pbValue:示意存放新属性值的缓冲区的地址。

我要做的就是插入API所需的值,并实验通过将cbValue设置为一个“不怀好意的”长度来使系统发生溃逃。 

*******************************************************************************
*                                                                             *
*                        Bugcheck Analysis                                    *
*                                                                             *
*******************************************************************************
 
SYSTEM_SERVICE_EXCEPTION (3b)
An exception happened while executing a system service routine.
Arguments:
Arg1: 00000000c0000005, Exception code that caused the bugcheck
Arg2: fffff8041d2d17c0, Address of the instruction which caused the bugcheck
Arg3: fffff80422182920, Address of the context record for the exception that caused the bugcheck
Arg4: 0000000000000000, zero.
 
Debugging Details:
------------------
--snipped--
 
READ_ADDRESS:  ffff810b6dcf5000 Special pool
 
MM_INTERNAL_CODE:  2
 
IMAGE_NAME:  cng.sys
 
MODULE_NAME: cng
 
FAULTING_MODULE: fffff8035ea30000 cng
 
PROCESS_NAME:  CVE-2020-17087.exe
 
TRAP_FRAME:  ffffbb8cdd49e770 -- (.trap 0xffffbb8cdd49e770)
NOTE: The trap frame does not contain all registers.
Some register values may be zeroed or incorrect.
rax=0000000000000020 rbx=0000000000000000 rcx=ffff810b6dcf5000
rdx=ffff810b6dcf4ff0 rsi=0000000000000000 rdi=0000000000000000
rip=fffff8035ea92503 rsp=ffffbb8cdd49e900 rbp=0000000000002aab
 r8=0000000000002aa9  r9=0000000000000002 r10=fffff8035eac7e70
r11=ffffbb8cdd49e850 r12=0000000000000000 r13=0000000000000000
r14=0000000000000000 r15=0000000000000000
iopl=0         nv up ei ng nz ac po nc
cng!CfgAdtpFormatPropertyBlock 0xa7:
fffff803`5ea92503 668901          mov     word ptr [rcx],ax ds:ffff810b`6dcf5000=????
Resetting default scope

太好了,机械果真蓝屏了,但我很快就发现挪用栈与我所期望的差别:

cng!CfgAdtpFormatPropertyBlock 0xa7
cng!CfgAdtReportFunctionPropertyOperation 0x22d
cng!BCryptSetContextFunctionProperty 0x3a5
cng!_ConfigurationFunctionIoHandler 0x3f34a
cng!ConfigFunctionIoHandler 0x4f
cng!ConfigIoHandler_Safeguarded 0xd9
cng!CngDeviceControl 0x8f
ksecdd!KsecDispatch 0x220
nt!IofCallDriver 0x55
nt!IopSynchronousServiceTail 0x1a8
nt!IopXxxControlFile 0x5e5
nt!NtDeviceIoControlFile 0x56
nt!KiSystemServiceCopyEnd 0x25
ntdll!NtDeviceIoControlFile 0x14
bcrypt!IoCallKernelDriver 0xb5
bcrypt!BCryptSetContextFunctionProperty 0x1a6
CVE_2020_17087 0x23eb

下面看一下执行流程:从BCryptSetContextFunctionsProperty最先,控制权传递给NtDeviceIoControlFile中的内核代码,跳入KsecDispatch,然后传递给Cng,然后在易受攻击的函数处溃逃,只不外,这一次是我的数据导致溃逃的:

0: kd> db ffff810b`6dcf5000 - 0n16
ffff810b`6dcf4ff0  34 00 31 00 20 00 34 00-31 00 20 00 34 00 31 00  4.1. .4.1. .4.1.

或者试图从一个不存在但指向我的数据的地址处执行读取操作时触发溃逃:

,

usdt收款平台

菜宝钱包(caibao.it)是使用TRC-20协议的Usdt第三方支付平台,Usdt收款平台、Usdt自动充提平台、usdt跑分平台。免费提供入金通道、Usdt钱包支付接口、Usdt自动充值接口、Usdt无需实名寄售回收。菜宝Usdt钱包一键生成Usdt钱包、一键调用API接口、一键无实名出售Usdt。

,
CONTEXT:  fffff80422182920 -- (.cxr 0xfffff80422182920)
rax=0031003400200031 rbx=0000000000000001 rcx=00000000f27e7ffd
rdx=0031003400200031 rsi=ffffae0feebecfe0 rdi=ffffae0feebe7000
rip=fffff8041d2d17c0 rsp=ffff85045597ee70 rbp=ffffae0fe6602280
 r8=0000000004060002  r9=412fae0e1b5fe029 r10=0000000000000000
r11=ffffae0fe6602290 r12=0000000000000000 r13=0000000000000000
r14=ffffae0feebece10 r15=0000000000000003
iopl=0         nv up ei pl nz na pe nc
cs=0010  ss=0018  ds=002b  es=002b  fs=0053  gs=002b             efl=00050202
nt!RtlpHpVsFreeChunkInsert 0x170:
fffff804`1d2d17c0 8b4af8          mov     ecx,dword ptr [rdx-8] ds:002b:00310034`00200029=????????
Resetting default scope
 
PROCESS_NAME:  CVE-2020-17087.exe

无论哪种情形,我们都能取得控制权!

最后,在实验了差别的长度值之后,我触发了以下错误信息:

KERNEL_SECURITY_CHECK_FAILURE (139)
A kernel component has corrupted a critical data structure.  The corruption
could potentially allow a malicious user to gain control of this machine.
Arguments:
Arg1: 000000000000001d, Type of memory safety violation
Arg2: fffffe8e45e3ec30, Address of the trap frame for the exception that caused the bugcheck
Arg3: fffffe8e45e3eb88, Address of the exception record for the exception that caused the bugcheck
Arg4: 0000000000000000, Reserved
--snipped--
rax=0031003400200030 rbx=0000000000000000 rcx=000000000000001d
--snipped--
PROCESS_NAME:  CVE-2020-17087.exe
ERROR_CODE: (NTSTATUS) 0xc0000409 - The system detected an overrun of a stack-based buffer in this application. This overrun could potentially allow a malicious user to gain control of this application.
--snipped--

太好了。

遗憾的是,除此之外,在这个破绽的行使方面,我没有取得任何希望。说老实话,这个破绽真是把我难住了!不外,既然我无法行使它,那么,是否能够找到防止这个破绽被行使的方式呢?

防御措施

这个破绽是在10月22日被披露的,其中一条谈论提到,直到11月10日,也就是位于当月的第二个星期二,才气获得修复,但若是您想珍爱自己组织免受该破绽的扰乱的话,该怎么做呢?若是您的用户喜欢点击来路不明的链接,只管举行了多次平安意识培训也没有效果的话,那该咋整?无论如何,您自己必须提供一个解决方案……

行动设计

我们知道,这个破绽的基本缘故原由是整数溢出,那么有什么设施举行防御呢?下面是我马上想到的几个思绪:

· 钩住CNG的IRP_MJ_DEVICE_CONTROL表

· 通过内联钩子钩住易受攻击的函数自己

若是钩住调剂例程函数,则需要重新实现整个函数。就钩子手艺而言,这将是最简朴的门路,但只要搞砸了哪怕一次,了局也会异常糟糕,由于这会扰乱系统的加密功效。除此之外,钩住CNG的调剂表只能防御Project Zero提供的PoC,由于这需要剖析DeviceIoControl参数并提取其长度。虽然这种方式能够完美地防御上面提到的PoC,但攻击者使用BCrypt函数触发破绽时,效果就不理想了,稀奇是当攻击者使用BCrypt函数时,由于所接纳的代码路径差别,这种方式将彻底失效。

若是我在易受攻击的函数上放置一个内联钩子,就会大大降低我搞砸的几率,由于我是在给函数自己打补丁,即添加了一个针对长度检查。这样做的瑕玷是,CfgAdtpFormatPropertyBlock并非导出函数,因此若是我想动态剖析它,则必须为其建立署名。您可以将偏移量添加到基址上,但我小我私家不喜欢静态的器械。

最终我选择了内联方式钩住CfgAdtpFormatPropertyBlock函数,由于这样的话,就无需体贴PoC选择了哪条代码路径,更为主要的是,这种方式失足的机率要更小。

现在的行动设计是建立一个驱动程序,以完成下列义务:

1.挟制CNG.sys的装备工具,以利便剖析其基址。

2.为CfgAdtpFormatPropertyBlock建立一个署名。

3.建立钩子存根。

4.动态剖析CfgAdtpFormatPropertyBlock。

5.打补丁。

6.若是驱动程序被卸载,则排除该函数的钩子。

我建立的署名是基于20h2的CNG:

这些字节足以用来唯一识别cng!CfgAdtpFormatPropertyBlock函数。

我使用的钩子存根代码如下所示:

UINT8 __stub_detour[] =
{
    0x48, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,   /// mov rax, address
    0xff, 0xd0,                        /// call rax
    0x0f, 0x1f, 0x00                       /// filler
};

上面的代码异常简朴。简朴来说,钩子的地址会在运行时被复制到八个空字节中,然后放到RAX寄存器中,最后将被挪用。我选择使用CALL指令的缘故原由很简朴,由于我可以很容易地使用RET指令返回原来的函数。无论如何,我最终获得了12个字节,但由于它只笼罩了一条指令的大部门内容,最后不得不填充上三个字节。

接下来,我必须设计好如何以平安的方式修补这个函数。就这里来说,CNG.sys只提供了读权限,而没有提供写权限,那么,怎样才气在不引起蓝屏的情形下执行写入操作呢?一种方式是获取Control Register 0的值,并禁用写珍爱位(CR0[WP])。这样做的瑕玷是,这是基于每个CPU状态举行的,也就是说,若是我在处置器0上禁用了CR0[WP],而一个线程迁徙到处置器1上并试图修补cng!CfgAdtpFormatPropertyBlock的话,就会蓝屏,由于CR0[WP]在处置器1上并没有被禁用。因此,我们必须防止线程迁徙到另一个CPU上,这样才可以正常修补CfgAdtpFormatPropertyBlock补丁,然后重新启用CR0[WP];然而,这涉及到修改内核组件,这正是我试图制止的。

我也可以实验禁用Page Table Entry中的Read Only位,但这也需要修改内核组件。除此之外,我们还使用内存形貌符列表(MDL)对内存举行双映射。这似乎完全符合我为自己设定的目的,以是,我就选择了这个方式。另外,这样做会加倍平安!

PMDL pmdl = IoAllocateMdl(targetAddress, length, FALSE, FALSE, NULL);
if (pmdl != NULL)
{
    __try
    {
        MmProbeAndLockPages(pmdl, KernelMode, IoModifyAccess);
    }
    __except (EXCEPTION_EXECUTE_HANDLER)
    {
        /// error handling
    }
    PVOID mappedPage = MmMapLockedPagesSpecifyCache(pmdl, KernelMode, MmNonCached, NULL, FALSE, NormalPagePriority);
    if (mappedPage != NULL)
    {
        /// write to memory and clean up
    }
    else
    {
        /// error handling
    }
}

这样做的目的,是用cng!CfgAdtpFormatPropertyBlock的地址分配一个MDL,其长度为x字节,也就是我们的挂钩存根代码的水平。同时,我使用MmProbeAndLockPages来分页并锁定这部门内存,最后,使用MmMapLockedPagesSpecifyCache来映射用IoAllocateMdl建立的MDL所形貌的物理页面。简朴来说,我所做的事情,就是将cng!CfgAdtpFormatPropertyBlock的虚拟地址重新映射到一个内存区间,该区间存放具有读写权限的统一组物理页面。

现在,MDL已经映射好了,那么,我们的目的是否已经达成了呢?看看吧。

在确认我们简直能够对内核内存执行写入操作后,接下来我们要做的事情,就是开发现实的代码来提防这个破绽。下面,我们给出响应的代码:

.code
 
__detoured_function proc
        add rsp, 8         ; adjust rsp to load the correct values
        mov rax, rsp           ; restore the first overwritten instruction
        sub rsp, 8         ; readjust for the ret
        mov qword ptr [rax   8h], rbx  ; restore the second overwritten instruction
        mov qword ptr [rax   10h], rbp ; restore the third overwritten instruction
        mov qword ptr [rax   18h], rsi ; restore the fourth overwritten instruction
        cmp dx, 2aabh
        jge status_unsuccessful
        ret
 
status_unsuccessful:
        xor rcx, rcx           ; force it to fail
        ret
__detoured_function endp
 
end

首先,我们需要恢复被笼罩的指令,以使这个钩子可以正常事情,同时,由于这里使用了CALL指令,因此,也需要对RSP稍作修改。主要的是,我不仅知道使用哪种类型的钩子,还知道寄存器在期待什么样的值。接下来,让我们看看函数正常挪用时,客栈的结构情形如下所示:

1: kd> dps rsp l5
ffffd586`90a26978  fffff802`44291e39 cng!CfgAdtReportFunctionPropertyOperation 0x22d
ffffd586`90a26980  00000000`00000000
ffffd586`90a26988  00000000`00000001
ffffd586`90a26990  000000ed`00000000
ffffd586`90a26998  ffffd586`90a26a80

就这里来说,只要把cng!CfgAdtReportFunctionPropertyOperation 0x22d放到RAX中,就可以了。在第4行到第6行,我们通过8对RSP举行响应的调整,获得的正是RAX的期望值,然后,还需要重新调整举行响应的调整。主要的部门是第10行和第11行。此时,寄存器的当前状态如下所示:

1: kd> r
rax=0000000000000000 rbx=0000000000000000 rcx=ffffc206e9a85000
rdx=0000000000002aab rsi=ffffc206e9a85000 rdi=0000000000000001
rip=fffff8024429245c rsp=ffffd58690a26978 rbp=ffffd58690a26a80
 r8=ffffd58690a269c8  r9=ffffd58690a26ed0 r10=0000000000000004
r11=0000000000000000 r12=ffffc206e9a85000 r13=ffffd58690a26f38
r14=0000000000000003 r15=ffffd58690a26f28

在RDX寄存器中,保留的是分配空间的巨细。若是DX大于或即是0x2aab,我们将跳转到status_unsuccessful。原始的cng!CfgAdtpFormatPropertyBlock函数会举行响应的检查,以确保源缓冲区、长度和目的字符串参数不是NULL;否则的话,就会返回STATUS_INVALID_PARAMETER或STATUS_INVALID_RESOURCES。现实上,最简朴的做法就是在第一次检查时失败,直接以STATUS_INTEGER_OVERFLOW这种方式失败,然后退出。我的做法是将RCX设置为NULL,迫使程序因STATUS_INVALID_PARAMETER检查失败而退出。

经由一番折腾,我们终于在遥远的补丁程序公布之前建好了自己的一道篱笆!

小结

在本文中,我们先容了Project Zero披露的CNG破绽,并研究了PoC的内部机制。同时,我们找到了一种差别的方式来触发这个破绽:我们能够控制触发溃逃的数据。然后,我们先容了针对该破绽的防御措施。总的来说,虽然这里没有乐成行使该破绽,但我们乐成实行了一个针对讲述提供的PoC的防御措施。

本文翻译自:https://ch3rn0byl.com/2021/02/a-look-at-cve-2020-17087/
发表评论
sunbet声明:该文看法仅代表作者自己,与本平台无关。请自觉遵守互联网相关的政策法规,严禁发布色情、暴力、反动的言论。
评价:
表情:
用户名: 验证码:点击我更换图片

您可能还会对下面的文章感兴趣: