首页
社区
课程
招聘
CVE-2020-17087整数溢出漏洞分析
发表于: 2022-3-15 14:04 22827

CVE-2020-17087整数溢出漏洞分析

2022-3-15 14:04
22827

看到GPZ很久之前披露的一个在野cng.sys驱动漏洞,本菜鸟尝试对其进行简单分析,希望能从中学习到一些知识

漏洞概述:

内核驱动模块cng.sys中存在整数溢出漏洞,利用此漏洞进行越界读写,最终可实现本地提取,此漏洞被黑客用于Chrome Sandbox Escape(CVE-2020-15999)

影响的windows版本:

Windows 10 2004以及之前的版本

Windows版本:win10 1903 10.0.18362.836

根据GPZ给出的Poc,可以定位到漏洞函数cng!CfgAdtpFormatPropertyBlocksize可以被用户控制,6 * size的结果强行转换为无符号的16位整数,0x10000 // 0x6的结果为0x2aa,0x10000 / 6的结果为10922.666666666666,假设控制size的大小为0x2ab,将其乘上6就溢出为2,BCryptAlloc函数内部会调用ExAllocatePoolWithTag或者SkAllocatePool在类型为NonPagedPoolNx池动态申请池空间,此时NumberOfBytes为2,申请的空间就十分小,然而do-while循环的次数为size(即0x2ab),每次向池空间写入6字节,最终导致越界写入,触发BSOD

从驱动的cng!DriverEntry入口函数入手,发现好几个MajorFunction都设置为cng!CngDispatch

跟进cng!CngDispatch函数,可以看到switch中以Major Function Code作为条件,当前案例中关注的是case 0xe,宏IRP_MJ_DEVICE_CONTROL的值为0xe,用户态调用DeviceIoControl这个API最终执行到内核驱动模块中的DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL]

IoCode即为IO控制码,从崩溃的栈帧回溯的执行路径获悉IoCode为0x390400,但是很遗憾,我的分析属于一种根据结果来推断过程(根据崩溃的栈帧),这也是四哥痛骂的那波人之一(我太菜了),代码审计的逻辑是从cng!CngDispatch函数开始,进入到cng!CngDeviceControl函数中根据不同IoCode进行递归,然后进行代码审计。本身来说这个漏洞成因相对简单,需要学习的是寻找触发漏洞路径

在cng!CngDispatch函数中下断点,然后查看一下cng!CngDeviceControl函数的6个参数,分别为输入缓冲区、输入缓冲区长度、输出缓冲区、输出缓冲区长度的指针、IO控制码、请求模式

CVE-2020-17087-1.png

从上图的第5个参数可以看到传入的IoCode为0x390400,因为抵达漏洞函数需要执行过函数cng!ConfigIoHandler_Safeguarded,cng!CngDeviceControl函数中存在这样的判断

cng!ConfigIoHandler_Safeguarded使用的参数为cng!CngDeviceControl前四个参数:rcx,rdx,r8,r9

跟进cng!ConfigIoHandler_Safeguarded函数,可以看到函数根据输入缓冲区的长度申请了两个大小相同的池块,姑且称为pool1、pool2,将输入缓冲区的内容拷贝至pool1,pool2初始化为0,在后续的分析过程,将pool1的首地址称为pool_ptr1,pool2的首地址称为pool_ptr2

在调试器中查看执行memmove前后的变化,验证以上说法

CVE-2020-17087-2.png

从伪代码中可以得知调用cng!IoUnpack_SG_ParamBlock_Header的返回值必须为0,否则函数直接返回,于是继续跟进cng!IoUnpack_SG_ParamBlock_Header,函数首先进行pool_ptr1 + 2 < pool_ptr1的判断,然后又进行pool_ptr1 + 2 > (_DWORD )((char )pool_ptr1 + size这样的判断,不太清楚这样做的意义,获取pool1的前4字节,与0x1A2B3C4D进行比较,因此输入缓冲区的前4字节设置为0x1A2B3C4D,第3个参数所指内存会保存pool1的第二个4字节,数值为0x10400,for循环内会将pool2的前8字节置位

查看pool2在执行完循环前后的变化

回到cng!ConfigIoHandler_Safeguarded函数,函数继续调用cng!ConfigFunctionIoHandler函数,第一个参数传入的值为0x10400,HIWORD(a1)返回的结果为1,就会进入到cng!ConfigurationFunctionIoHandler函数,a2、a3、a4、a5、a6对应的是pool_ptr1、InputLen、OutputBuffer、&OutputLen、pool_ptr2

在调试器中查看cng!ConfigurationFunctionIoHandler函数的参数

CVE-2020-17087-3.png

cng!ConfigurationFunctionIoHandler函数开头调用cng!IoUnpack_SG_Configuration_ParamBlock,从函数名跟上文的分析来看,该函数会对池块的某些部分进行一些必要的检查并写入一些值,函数的返回值依然需要为0

CVE-2020-17087-4.png

跟进cng!IoUnpack_SG_Configuration_ParamBlock函数,先对pool1的大小与8进行对比,由于我们使用的大小为0x3aab,还不能直接返回0,将pool1的首地址加上0x68得到v18,当前实例中保证pool1 < v18 < (pool1 + poolsize),紧接着就是一连串的判断后决定是否调用cng!IoUnpack_SG_SzString、cng!IoUnpack_SG_ContextFunctionConfig、cng!IoUnpack_SG_Buffer函数,由于a4、a6、a7等都为变量的地址(不为0),故都会进入分支执行这些函数

CVE-2020-17087-5.png

在调试器中查看一下第一次执行完cng!IoUnpack_SG_SzString函数后的变化

CVE-2020-17087-6.png

在调试器中查看一下最后一次执行完cng!IoUnpack_SG_ContextFunctionConfig函数后的变化

CVE-2020-17087-8.png

在调试器中查看一下执行完cng!IoUnpack_SG_Buffer函数后的变化

CVE-2020-17087-7.png

从上面几张图片可以看到,执行完cng!IoUnpack_SG_SzString、cng!IoUnpack_SG_ContextFunctionConfig、cng!IoUnpack_SG_Buffer函数后,缓冲区偏移0x10、0x18、0x20、0x28、0x30、0x40、0x48、0x58的内容在原有的基础上加上了pool1的首地址

对cng!IoUnpack_SG_Buffer函数进行简单的分析,可以发现函数内向pool_ptr2偏移0x58字节写入8字节的0xff,在pool_ptr2偏移poi(pool_ptr1 + 0x58)(当前数值为0x1000)处开始写入poi(pool_ptr1 + 0x50)(当前数值为0x2ab)字节的0xff

在调试器中查看pool2的内容进行验证

执行完以上部分,紧接着就是一组赋值操作,然后返回0

返回到cng!ConfigurationFunctionIoHandler函数后,由于输入缓冲区的第二个四字节0x10400被截断为0x400,就会执行到这样的分支

跟进cng!BCryptSetContextFunctionProperty函数,继续分析此函数,cng!ValidateTableId所在的分支无法满足条件,利用cbValue和pbValue的值初始化DestinationString,利用此DestinationString调用cng!CfgReg_Acquire

进一步跟进cng!CfgReg_Acquire,发现cng!VerifyRegistryAccess函数尝试对System\CurrentControlSet\Control\Cryptography\Configuration\Local注册表项执行操作,cng!VerifyRegistryAccess内部调用cng!KeRegOpenKey尝试打开注册表表项

CVE-2020-17087-9.png

根据cng!VerifyRegistryAccess的返回值为5得知打开失败,函数将5返回给cng!BCryptSetContextFunctionProperty

CVE-2020-17087-10.png

紧接着在cng!BCryptSetContextFunctionProperty的执行流程大体如下,分别使用pool1_ptr+0x100、pool1_ptr+0x200、pool1_ptr+0x400处的字符初始化三个新的UnicodeString

在初始化三个UnicodeString后,进入下面的分支调用cng!CfgAdtReportFunctionPropertyModification函数

CVE-2020-17087-11.png

在调试器中查看一下函数的参数,最少有10个,IDA反编译出来的结果中有11个

CVE-2020-17087-12.png

从上图中可以看到,在rsp+40的位置存储了0x2aab,这个数值来自于pool_ptr1 + 0x50的位置,查看一下寄存器r14w的数据来源,可以看到函数序言部分将pool_ptr1 + 0x50处的值赋给r14d

CVE-2020-17087-13.png

cng!CfgAdtReportFunctionPropertyModification函数内部调用最终的漏洞函数cng!CfgAdtpFormatPropertyBlock,调试器中查看一下cng!CfgAdtpFormatPropertyBlock的参数,可以看到第二个参数来自于cng!CfgAdtReportFunctionPropertyModification的第九个参数,数值为0x2aab

CVE-2020-17087-14.png

由于漏洞原理在开篇提及,此处就对一些关键位置查看下。查看一下cng!BCryptAlloc的参数,果然溢出为2

CVE-2020-17087-15.png

调用完该函数申请到的池块大小为0x20字节

CVE-2020-17087-16.png

然而循环的次数为0x2aab次,每次向池块中写入6字节,最终就会覆写到相邻的内存,最终导致BSOD

对比一下补丁前后的文件变化,可以看到只有cng!CfgAdtpFormatPropertyBlock函数发生了变化

CVE-2020-17087-17.png

查看一下函数的前后变化,发现补丁后函数调用cng!RtlUShortMult函数对传进来的size进行了处理,然后再传给cng!BCryptAlloc函数申请池空间

CVE-2020-17087-18.png

cng!RtlUShortMult函数十分简短,就是对size * 6的结果进行溢出判断(与0xffff比较)

CVE-2020-17087-19.png

 
 
 
 
__int64 __fastcall CfgAdtpFormatPropertyBlock(char *a1, unsigned __int16 size, __int64 a3)
{
  unsigned int v3; // ebx
  char *v6; // r14
  __int16 v7; // di
  _WORD *pool_ptr; // rax
  _WORD *v9; // rdx
  _WORD *pool_ptr_w; // rcx
  __int64 count; // r8
  _WORD *v12; // rcx
  char v13; // al
 
  v3 = 0;
  v6 = a1;
  if ( a1 && size && a3 )
  {
    v7 = 6 * size;
    pool_ptr = BCryptAlloc((unsigned __int16)(6 * size));// 6 * 0x2aab = 2
    v9 = pool_ptr;
    if ( pool_ptr )
    {
      pool_ptr_w = pool_ptr;
      if ( size )
      {
        count = size;
        do
        {
          // store 6 bytes every time
          *pool_ptr_w = (unsigned __int8)a0123456789abcd[(unsigned __int64)(unsigned __int8)*v6 >> 4];
          v12 = pool_ptr_w + 1;
          v13 = *v6++;
          *v12++ = (unsigned __int8)a0123456789abcd[v13 & 0xF];
          *v12 = 0x20;
          pool_ptr_w = v12 + 1;
          --count;
        }
        while ( count );
      }
      *(_QWORD *)(a3 + 8) = v9;
      *(_WORD *)(a3 + 2) = v7;
      *(_WORD *)a3 = v7 - 2;
    }
    else
    {
      return 0xC000009A;
    }
  }
  else
  {
    return 0xC000000D;
  }
  return v3;
}
 
PVOID __fastcall BCryptAlloc(SIZE_T NumberOfBytes)
{
  char DeviceContext; // al
 
  DeviceContext = (char)WPP_MAIN_CB.Queue.Wcb.DeviceContext;
  if ( !LODWORD(WPP_MAIN_CB.Queue.Wcb.DeviceContext) )
    DeviceContext = GetTrustedEnvironment();
  if ( (DeviceContext & 2) != 0 )
    return (PVOID)SkAllocatePool(0x200i64, NumberOfBytes, 'bgnC');// NonPagedPoolNx
  else
    return ExAllocatePoolWithTag((POOL_TYPE)0x200, NumberOfBytes, 'bgnC');// NonPagedPoolNx
}
__int64 __fastcall CfgAdtpFormatPropertyBlock(char *a1, unsigned __int16 size, __int64 a3)
{
  unsigned int v3; // ebx
  char *v6; // r14
  __int16 v7; // di
  _WORD *pool_ptr; // rax
  _WORD *v9; // rdx
  _WORD *pool_ptr_w; // rcx
  __int64 count; // r8
  _WORD *v12; // rcx
  char v13; // al
 
  v3 = 0;
  v6 = a1;
  if ( a1 && size && a3 )
  {
    v7 = 6 * size;
    pool_ptr = BCryptAlloc((unsigned __int16)(6 * size));// 6 * 0x2aab = 2
    v9 = pool_ptr;
    if ( pool_ptr )
    {
      pool_ptr_w = pool_ptr;
      if ( size )
      {
        count = size;
        do
        {
          // store 6 bytes every time
          *pool_ptr_w = (unsigned __int8)a0123456789abcd[(unsigned __int64)(unsigned __int8)*v6 >> 4];
          v12 = pool_ptr_w + 1;
          v13 = *v6++;
          *v12++ = (unsigned __int8)a0123456789abcd[v13 & 0xF];
          *v12 = 0x20;
          pool_ptr_w = v12 + 1;
          --count;
        }
        while ( count );
      }
      *(_QWORD *)(a3 + 8) = v9;
      *(_WORD *)(a3 + 2) = v7;
      *(_WORD *)a3 = v7 - 2;
    }
    else
    {
      return 0xC000009A;
    }
  }
  else
  {
    return 0xC000000D;
  }
  return v3;
}
 
PVOID __fastcall BCryptAlloc(SIZE_T NumberOfBytes)
{
  char DeviceContext; // al
 
  DeviceContext = (char)WPP_MAIN_CB.Queue.Wcb.DeviceContext;
  if ( !LODWORD(WPP_MAIN_CB.Queue.Wcb.DeviceContext) )
    DeviceContext = GetTrustedEnvironment();
  if ( (DeviceContext & 2) != 0 )
    return (PVOID)SkAllocatePool(0x200i64, NumberOfBytes, 'bgnC');// NonPagedPoolNx
  else
    return ExAllocatePoolWithTag((POOL_TYPE)0x200, NumberOfBytes, 'bgnC');// NonPagedPoolNx
}
DriverObject->MajorFunction[0] = (PDRIVER_DISPATCH)CngDispatch;
DriverObject->MajorFunction[2] = (PDRIVER_DISPATCH)CngDispatch;
DriverObject->MajorFunction[3] = (PDRIVER_DISPATCH)CngDispatch;
DriverObject->MajorFunction[4] = (PDRIVER_DISPATCH)CngDispatch;
DriverObject->MajorFunction[5] = (PDRIVER_DISPATCH)CngDispatch;
DriverObject->MajorFunction[0xA] = (PDRIVER_DISPATCH)CngDispatch;
DriverObject->MajorFunction[0xE] = (PDRIVER_DISPATCH)CngDispatch;
DriverObject->MajorFunction[0] = (PDRIVER_DISPATCH)CngDispatch;
DriverObject->MajorFunction[2] = (PDRIVER_DISPATCH)CngDispatch;
DriverObject->MajorFunction[3] = (PDRIVER_DISPATCH)CngDispatch;
DriverObject->MajorFunction[4] = (PDRIVER_DISPATCH)CngDispatch;
DriverObject->MajorFunction[5] = (PDRIVER_DISPATCH)CngDispatch;
DriverObject->MajorFunction[0xA] = (PDRIVER_DISPATCH)CngDispatch;
DriverObject->MajorFunction[0xE] = (PDRIVER_DISPATCH)CngDispatch;
 
switch ( CurrentStackLocation->MajorFunction )
{
    ...
    case 0xEu:
        IoCode = CurrentStackLocation->Parameters.Read.ByteOffset.LowPart;
        if ( (IoCode & 3) == 2 && CurrentStackLocation->Parameters.Read.Length )
        {
            ...
        }
        else
        {
            OutputBuffer = irp->AssociatedIrp.MasterIrp;
            InputBuffer = OutputBuffer;
            OutputLen = CurrentStackLocation->Parameters.Read.Length;
        }
        irp->IoStatus.Status = CngDeviceControl(
            InputBuffer,
            CurrentStackLocation->Parameters.Create.Options,// InputLen
            OutputBuffer,
            &OutputLen,
            IoCode,
            irp->RequestorMode);
        irp->IoStatus.Information = OutputLen;
        break;
}
switch ( CurrentStackLocation->MajorFunction )
{
    ...
    case 0xEu:
        IoCode = CurrentStackLocation->Parameters.Read.ByteOffset.LowPart;
        if ( (IoCode & 3) == 2 && CurrentStackLocation->Parameters.Read.Length )
        {
            ...
        }
        else
        {
            OutputBuffer = irp->AssociatedIrp.MasterIrp;
            InputBuffer = OutputBuffer;
            OutputLen = CurrentStackLocation->Parameters.Read.Length;
        }
        irp->IoStatus.Status = CngDeviceControl(
            InputBuffer,
            CurrentStackLocation->Parameters.Create.Options,// InputLen
            OutputBuffer,
            &OutputLen,
            IoCode,
            irp->RequestorMode);
        irp->IoStatus.Information = OutputLen;
        break;
}
1: kd> ba e1 /w "@$curprocess.Name == \"poc.exe\"" cng!CngDispatch+85
1: kd> ba e1 /w "@$curprocess.Name == \"poc.exe\"" cng!CngDispatch+85
 
if ( IoCode == 0x390400 )
      return ConfigIoHandler_Safeguarded(InputBuffer, NumberOfBytes, (IRP *)OutputBuffer, OutputLen);
if ( IoCode == 0x390400 )
      return ConfigIoHandler_Safeguarded(InputBuffer, NumberOfBytes, (IRP *)OutputBuffer, OutputLen);
 
__int64 __fastcall ConfigIoHandler_Safeguarded(
        PVOID InputBuffer,
        SIZE_T InputLen,
        PVOID OutputBuffer,
        ULONG *OutputLen)
{
 ...
 
  size = InputLen;
  if ( OutputBuffer )
    v7 = *OutputLen; // v7 = 8
  else
    v7 = 0;
  *OutputLen = 0;
  v8 = 8;
  len = v7; // len = 8
  if ( v7 < 8 )
  {
    if ( v7 )
      memset(OutputBuffer, 0, v7);
    return 0xC0000023;
  }
  else
  {
    v9 = (unsigned int)InputLen;
    pool_ptr1 = BCryptAlloc((unsigned int)InputLen); // allocate pool1
    v11 = BCryptAlloc(v9); // allocate pool2
    pool_ptr2 = v11;
    if ( pool_ptr1 && v11 )
    {
      memmove(pool_ptr1, InputBuffer, v9); // copy InputBuffer to pool1
      memset(pool_ptr2, 0, v9); // clear pool2
      v13 = IoUnpack_SG_ParamBlock_Header(pool_ptr1, size, &v19, pool_ptr2);
      if ( v13 )
      {
        v15 = WinErrorToNtStatus(v13);
      }
      else
      {
        v14 = ConfigFunctionIoHandler(
                v19,
                (int)pool_ptr1,
                size,
                (struct _CRYPT_CONTEXT_FUNCTION_PROVIDERS *)OutputBuffer,
                &len,
                (__int64)pool_ptr2);
        ...
 
      }
      ...
    }
    ...
}
__int64 __fastcall ConfigIoHandler_Safeguarded(
        PVOID InputBuffer,
        SIZE_T InputLen,
        PVOID OutputBuffer,
        ULONG *OutputLen)
{
 ...
 
  size = InputLen;
  if ( OutputBuffer )
    v7 = *OutputLen; // v7 = 8
  else
    v7 = 0;
  *OutputLen = 0;
  v8 = 8;
  len = v7; // len = 8
  if ( v7 < 8 )
  {
    if ( v7 )
      memset(OutputBuffer, 0, v7);
    return 0xC0000023;
  }
  else
  {
    v9 = (unsigned int)InputLen;
    pool_ptr1 = BCryptAlloc((unsigned int)InputLen); // allocate pool1
    v11 = BCryptAlloc(v9); // allocate pool2
    pool_ptr2 = v11;
    if ( pool_ptr1 && v11 )
    {
      memmove(pool_ptr1, InputBuffer, v9); // copy InputBuffer to pool1
      memset(pool_ptr2, 0, v9); // clear pool2
      v13 = IoUnpack_SG_ParamBlock_Header(pool_ptr1, size, &v19, pool_ptr2);
      if ( v13 )
      {
        v15 = WinErrorToNtStatus(v13);
      }
      else
      {
        v14 = ConfigFunctionIoHandler(
                v19,
                (int)pool_ptr1,
                size,
                (struct _CRYPT_CONTEXT_FUNCTION_PROVIDERS *)OutputBuffer,
                &len,
                (__int64)pool_ptr2);
        ...
 
      }
      ...
    }
    ...
}
 
__int64 __fastcall IoUnpack_SG_ParamBlock_Header(_DWORD *pool_ptr1, unsigned int size, _DWORD *a3, _QWORD *pool_ptr2)
{
  ...
  v4 = 0;
  if ( !pool_ptr1 )
    return 1i64;
  if ( pool_ptr1 + 2 < pool_ptr1 )
    return 1i64;
  v5 = size;
  if ( pool_ptr1 + 2 > (_DWORD *)((char *)pool_ptr1 + size) || *pool_ptr1 != 0x1A2B3C4D )
    return 1i64;
  if ( a3 )
    *a3 = pool_ptr1[1];
  if ( !pool_ptr2 )
    return 0i64;
  v6 = pool_ptr2 + 1;
  if ( pool_ptr2 + 1 >= pool_ptr2 )
  {
    if ( v6 <= (_QWORD *)((char *)pool_ptr2 + size) )
    {
      v7 = 0;
      for ( i = pool_ptr2; !*i; ++i )
      {
        if ( (unsigned int)++v7 >= 8 )
        {
          *pool_ptr2 = 0xFFFFFFFFFFFFFFFFui64;
          return 0i64;
        }
        ...
      }
      ...
    }
    ...
  }
  ...
}
__int64 __fastcall IoUnpack_SG_ParamBlock_Header(_DWORD *pool_ptr1, unsigned int size, _DWORD *a3, _QWORD *pool_ptr2)
{
  ...
  v4 = 0;
  if ( !pool_ptr1 )
    return 1i64;
  if ( pool_ptr1 + 2 < pool_ptr1 )
    return 1i64;
  v5 = size;
  if ( pool_ptr1 + 2 > (_DWORD *)((char *)pool_ptr1 + size) || *pool_ptr1 != 0x1A2B3C4D )
    return 1i64;
  if ( a3 )
    *a3 = pool_ptr1[1];
  if ( !pool_ptr2 )
    return 0i64;
  v6 = pool_ptr2 + 1;
  if ( pool_ptr2 + 1 >= pool_ptr2 )
  {
    if ( v6 <= (_QWORD *)((char *)pool_ptr2 + size) )
    {
      v7 = 0;
      for ( i = pool_ptr2; !*i; ++i )
      {
        if ( (unsigned int)++v7 >= 8 )
        {
          *pool_ptr2 = 0xFFFFFFFFFFFFFFFFui64;
          return 0i64;
        }
        ...
      }
      ...
    }
    ...
  }
  ...
}
1: kd> dq @rax l4
ffffbb0f`7e160000  00000000`00000000 00000000`00000000
ffffbb0f`7e160010  00000000`00000000 00000000`00000000
1: kd> dq @r9 l4
ffffbb0f`7e160000  ffffffff`ffffffff 00000000`00000000
ffffbb0f`7e160010  00000000`00000000 00000000`00000000
1: kd> dq @rax l4
ffffbb0f`7e160000  00000000`00000000 00000000`00000000
ffffbb0f`7e160010  00000000`00000000 00000000`00000000
1: kd> dq @r9 l4
ffffbb0f`7e160000  ffffffff`ffffffff 00000000`00000000
ffffbb0f`7e160010  00000000`00000000 00000000`00000000
NTSTATUS __fastcall ConfigFunctionIoHandler(
        unsigned int a1,
        int a2,
        unsigned int a3,
        struct _CRYPT_CONTEXT_FUNCTION_PROVIDERS *a4,
        _DWORD *a5,
        __int64 a6)
{
  switch ( HIWORD(a1) )
  {
    case 0u:
      return RegistrationFunctionIoHandler(a1, a2, a3, (int)a4, (ULONG)a5, a6);
    case 1u:
      return ConfigurationFunctionIoHandler(a1, a2, a3, a4, a5, a6);
    case 2u:
      return ResolutionFunctionIoHandler(a1, a2, a3, (int)a4, (ULONG)a5, a6);
  }
  return 0xC00000AF;
}
NTSTATUS __fastcall ConfigFunctionIoHandler(
        unsigned int a1,
        int a2,
        unsigned int a3,
        struct _CRYPT_CONTEXT_FUNCTION_PROVIDERS *a4,
        _DWORD *a5,
        __int64 a6)
{
  switch ( HIWORD(a1) )
  {
    case 0u:
      return RegistrationFunctionIoHandler(a1, a2, a3, (int)a4, (ULONG)a5, a6);
    case 1u:
      return ConfigurationFunctionIoHandler(a1, a2, a3, a4, a5, a6);
    case 2u:
      return ResolutionFunctionIoHandler(a1, a2, a3, (int)a4, (ULONG)a5, a6);
  }
  return 0xC00000AF;
}
 
 
 

[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

最后于 2022-3-16 07:53 被Jimpp编辑 ,原因:
上传的附件:
收藏
免费 3
支持
分享
最新回复 (2)
雪    币: 958
活跃值: (389)
能力值: ( LV9,RANK:141 )
在线值:
发帖
回帖
粉丝
2
并不觉得这个洞能轻易rce啊,因为能写的内容非常有限,而且不能连续写,并且会溢出超长内容,如何控制这个真是问题
2022-3-16 16:58
0
雪    币: 50
活跃值: (1055)
能力值: ( LV2,RANK:15 )
在线值:
发帖
回帖
粉丝
3
0x10000/0x6的结果为0x2aaa,假设控制size的大小为0x2aab,将其乘上6就溢出为2
2022-11-10 09:10
0
游客
登录 | 注册 方可回帖
返回
//