首页 科技内容详情
深入考察微软Windows地址簿中的堆缓冲区溢露马脚

深入考察微软Windows地址簿中的堆缓冲区溢露马脚

分类:科技

网址:

反馈错误: 联络客服

点击直达

2022世界杯比分

www.22223388.com)实时更新发布最新最快最有效的2022世界杯比分资讯。

,

概述 

在本文中,我们将深入剖析微软Windows地址簿中的堆缓冲区溢露马脚。现实上,微软已经在2021年2月的周二补丁中宣布了关于这个破绽的通告。这篇文章将详细先容什么是微软Windows地址簿,破绽自己的情形,以及若何组织一个导致含有该破绽的应用程序溃逃的PoC。

Windows地址簿

Windows地址簿是微软Windows操作系统的一部门,是一项为用户提供集中式联系人列表的服务,微软和第三方应用程序都可以接见和修改地址簿。现实上,Windows地址簿维护着一个内陆数据库和接口,用于查找和编辑联系人的信息,并可以使用轻量级目录接见协议(LDAP)查询网络目录服务器。Windows地址簿于1996年推出,厥后在Windows Vista中被Windows Contacts所取代,随后,后者在Windows 10中被People App所取代。

Windows地址簿提供了一个API,以便于其他应用程序能够直接使用其数据库和用户界面服务,同时,也利便种种服务接见和修改联系信息。虽然微软已经替换了提供地址簿功效的应用程序,但较新的替换程序仍然行使了原来的功效并确保向后兼容。Windows地址簿功效存在于几个Windows库中,这些库被Windows 10应用程序使用,包罗Outlook和Windows Mail。通过这种方式,现代应用程序仍然可以使用Windows地址簿,甚至可以从旧版本的Windows中导入地址簿。

CVE-2021-24083

详细来说,这个堆缓冲区溢露马脚位于SecurityCheckPropArrayBuffer()函数,该函数隶属于wab32.dll;当这个函数处置联系人的嵌套属性,就有可能触发该破绽。通过网络攻击该缓冲区溢露马脚时,只需诱使用户打开一个全心制作的.wab文件即可;为此,攻击者可以在该文件的WAB纪录中包罗一个恶意的复合属性。

破绽剖析

下面的破绽剖析是基于在Windows 10 x64上运行的10.0.19041.388版本的Windows Address Book Contacts DLL(wab32.dll)的。

Windows Address Book Contacts DLL(即wab32.dll)提供对地址簿API的接见功效;许多应用程序都是借助于这个库与Windows地址簿举行互动的。这个Contacts DLL用于处置与联系人和身份治理有关的操作。其中,Contacts DLL能够导入从早期版本的Windows地址簿中导出的地址簿(即WAB文件)。

现实上,早期版本的Windows地址簿是以.wab文件的形式来维护身份和联系人的数据库的。虽然当前版本的Windows默认不再使用.wab文件,但它们仍然允许从早期安装的Windows地址簿中导入WAB文件。

虽然将WAB文件导入Windows地址簿的方式有多种,但据考察,应用程序通常都是依赖Windows Contacts Import Tool(即C:\Program Files\Windows Mail\wabmig.exe)来导入地址簿的。这个导入工具会加载wab32.dll来处置WAB文件的加载历程,提取相关的联系人,并将其导入到Windows地址簿中。

WAB文件的花样

WAB文件花样(通常被称为Windows地址簿或Outlook地址簿)是一种尚未公然的专有文件花样,群主包罗小我私人身份信息。同时,身份又可能包罗联系人,而每个联系人还可能包罗一个或多个属性。

只管该花样尚未公然,但已经有人对该文件花样举行了部门的逆向剖析。下面的结构是从一个公然的第三方应用程序和wab32.dll的反汇编代码中获得的。因此,在结构界说、字段名和字段类型方面可能存在禁绝确之处。

WAB文件具有以下结构:


除非尚有说明,否则所有多字节字段都以小端字节顺序示意。同时,所有字符串字段都是使用Unicode字符,并以UTF16-LE花样编码。

幻数字段包罗以下16个字节:9c cb cb 8d 13 75 d2 11 91 58 00 c0 4f 79 56 a4。虽然有些资料将81 32 84 C1 85 05 D0 11 B2 90 00 AA 00 3C F6 76的字节序列列为WAB文件的有用幻数,但通过实验发现,替换该字节序列会故障Windows地址簿处置该文件。

编号为1到6的六个表形貌符字段都具有以下结构:


下面是一些已知的表形貌符类型的示例:

文本纪录(类型:0x84d0):一个包罗Unicode字符串的纪录。

索引纪录(类型:0xFA0):可能包罗WAB纪录的多个形貌符的纪录。

每个文本纪录都具有以下结构:


同样,所有索引纪录则具有以下结构:


索引纪录中的每个条目(即延续存放的索引纪录结构)都有一个偏移量,指向一个WAB纪录。

WAB纪录

WAB记任命于形貌联系人。它包罗存储在属性中的电子邮件地址和电话号码等字段,这些字段可以是种种类型,如字符串、整数、GUID和时间戳。每个WAB纪录都具有以下结构:


以下字段是相关的:

RecordProperties字段含有一系列纪录属性结构。

PropertyCount字段示意RecordProperties字段内属性的数目。

需要注重的是,纪录属性可以是简朴的,也可以是复合的。

简朴属性

简朴属性具有以下结构:


简朴属性的标签小于0x1000,并包罗以下内容:


不外,这里需要注重以下几点:

◼前面列出的属性清单并不完整。关于更多的属性标签界说,请看这里。

◼PtypBinary的值前有一个COUNT字段,用于对16位字举行计数。

◼除上述内容外,还存在以下属性;它们在WAB中的用法尚不清晰:

        ◾PtypEmbeddedTable(0x0000000D):该属性值是一个组件工具模子(COM)工具。

        ◾PtypNull (0x00000001):None:该属性是一个占位符。

        ◾PtypUnspecified (0x00000000):Any:该属性的类型值匹配任何类型。

复合属性

复合属性具有以下结构:


复合属性的标签大于或即是0x1000,并包罗以下内容:


每个复合属性的Value字段包罗NestedPropCount数目的响应类型的简朴属性。

若是是牢靠长度的属性(PtypMultipleInteger16、PtypMultipleInteger32、PtypMultipleGuid和PtypMultipleTime),那么,复合属性的Value字段则包罗NestedPropCount数目的响应简朴属性的Value字段。

例如,在一个NestedPropCount为4的PtypMultipleInteger32结构中:

◼Size总是16。

◼Value包罗四个32位的整数。

对于长度可变的属性(如PtypMultipleString8、PtypMultipleBinary和PtypMultipleString),那么复合属性的Value字段包罗NestedPropCount数目的响应简朴属性的Size和Value字段。

例如,在一个NestedPropCount为2的PtypMultipleString结构中,包罗Unicode字符串“foo”和“bar”:

◼Size为14 00 00 00。

◼Value字段由以下两个字节字符勾通接而成:

        ◾“foo”以四个字节的长度举行编码:06 00 00 00 66 00 6f 00 6f 00。

        ◾“bar”以四个字节的长度举行编码:06 00 00 00 62 00 61 00 72 00。

手艺细节

新2手机代理管理端

www.x2w0000.com)实时更新发布最新最快的新2手机代理管理端、新2会员线路、新2备用登录网址、新2手机管理端、新2手机版登录网址、新2皇冠登录网址。

当以WAB文件形式导入畸形的Windows地址簿时,就会触发这个破绽。当用户试图将WAB文件导入Windows地址簿时,会挪用WABObjectInternal::Import()方式,该方式又会挪用ImportWABFile()方式。对于WAB文件中的每个联系人,ImportWABFile()方式都市执行以下嵌套挪用:ImportContact()、CWABStorage::ReadRecord()、ReadRecordWithoutLocking()以及HrGetPropArrayFromFileRecord(). 后面这个函数会吸收一个文件的指针作为参数,读取联系人头部,并提取PropertyCount和DataLen字段。函数HrGetPropArrayFromFileRecord()则继续挪用SecurityCheckPropArrayBuffer()对导入的文件举行平安检查,并挪用HrGetPropArrayFromBuffer()将联系人属性读入一个属性数组。

现实上,函数HrGetPropArrayFromBuffer()在很洪水平上依赖于SecurityCheckPropArrayBuffer()执行的检查的准确性。然而,该函数未能对某些属性类型实行平安检查。详细来说,SecurityCheckPropArrayBuffer()可能会跳过检查属性标签未知的嵌套属性的内容,而函数HrGetPropArrayFromBuffer()则继续处置所有的嵌套属性,而不管平安检查效果若何。因此,攻击者可以行使这一点来诱骗函数HrGetPropArrayFromBuffer()剖析一个未通过检查的联系人属性。而剖析这样的属性的效果是,函数HrGetPropArrayFromBuffer()可能导致堆缓冲区溢出。

代码剖析

下面的代码块显示了与此破绽相关的方式的受影响部门。代码片断用[N]示意的引用符号来举行划分。与此破绽无关的行被[Truncated]符号所取代。

以下是函数HrGetPropArrayFromFileRecord的伪代码:

[1]
if ( !(unsigned int)SecurityCheckPropArrayBuffer(wab_buffer_full, HIDWORD(uBytes[1]), wab_buffer[3]) )
  {
[2]
    result = 0x8004011b;        // Error
    goto LABEL_25;              // Return prematurely
  }
[3]
  result = HrGetPropArrayFromBuffer(wab_buffer_full, HIDWORD(uBytes[1]), wab_buffer[3], 0, a7);

在[1]处挪用函数SecurityCheckPropArrayBuffer()时,会对收到的缓冲区和其中包罗的属性举行一系列的平安检查。若是检查顺遂通过,那么就以为输入是可以信托的,并通过挪用HrGetPropArrayFromBuffer()在[3]处举行响应的处置。否则,该函数在[2]处返回一个错误。

下面是函数SecurityCheckPropArrayBuffer()的伪代码:

    __int64 __fastcall SecurityCheckPropArrayBuffer(unsigned __int8 *buffer_ptr, unsigned int buffer_length, int header_dword_3)
    {
      unsigned int security_check_result; // ebx
      unsigned int remaining_buffer_bytes; // edi
      int l_header_dword_3; // er15
      unsigned __int8 *ptr_to_buffer; // r9
      int current_property_tag; // ecx
      __int64 c_dword_2; // r8
      unsigned int v9; // edi
      int VA; // ecx
      int VB; // ecx
      int VC; // ecx
      int VD; // ecx
      int VE; // ecx
      int VF; // ecx
      int VG; // ecx
      int VH; // ecx
      signed __int64 res; // rax
      _DWORD *ptr_to_dword_1; // rbp
      unsigned __int8 *ptr_to_dword_0; // r14
      unsigned int dword_2; // eax
      unsigned int v22; // edi
      int v23; // esi
      int v24; // ecx
      unsigned __int8 *c_ptr_to_property_value; // [rsp+60h] [rbp+8h]
      unsigned int v27; // [rsp+68h] [rbp+10h]
      unsigned int copy_dword_2; // [rsp+70h] [rbp+18h]
 
      security_check_result = 0;
      remaining_buffer_bytes = buffer_length;
      l_header_dword_3 = header_dword_3;
      ptr_to_buffer = buffer_ptr;
      if ( header_dword_3 )                     
      {
        while ( remaining_buffer_bytes > 4 )       
        {
 
[4]
 
          if ( *(_DWORD *)ptr_to_buffer & 0x1000 ) 
          {
 
[5]
 
            current_property_tag = *(unsigned __int16 *)ptr_to_buffer;
            if ( current_property_tag == 0x1102 ||                   
                 (unsigned int)(current_property_tag - 0x101E) < = 1 )
            {                        
 
[6]
                                      
              ptr_to_dword_1 = ptr_to_buffer + 4;                    
              ptr_to_dword_0 = ptr_to_buffer;
              if ( remaining_buffer_bytes < 0xC )                    
                return security_check_result;                        
              dword_2 = *((_DWORD *)ptr_to_buffer + 2);
              v22 = remaining_buffer_bytes - 0xC;
              if ( dword_2 > v22 )                                    
                return security_check_result;                        
              ptr_to_buffer += 12;
              copy_dword_2 = dword_2;
              remaining_buffer_bytes = v22 - dword_2;
              c_ptr_to_property_value = ptr_to_buffer;               
              v23 = 0;                                               
              if ( *ptr_to_dword_1 > 0u )
              {
                while ( (unsigned int)SecurityCheckSingleValue(
                                        *(_DWORD *)ptr_to_dword_0,
                                        &c_ptr_to_property_value,
                                        ©_dword_2) )
                {
                  if ( (unsigned int)++v23 >= *ptr_to_dword_1 )      
                  {                                                  
                    ptr_to_buffer = c_ptr_to_property_value;
                    goto LABEL_33;
                  }
                }
                return security_check_result;
              }
            }
            else                                                       
            {
             
[7]
 
              if ( remaining_buffer_bytes < 0xC )
                return security_check_result;
              c_dword_2 = *((unsigned int *)ptr_to_buffer + 2);      
              v9 = remaining_buffer_bytes - 12;
              if ( (unsigned int)c_dword_2 > v9 )                    
                return security_check_result;                        
              remaining_buffer_bytes = v9 - c_dword_2;
              VA = current_property_tag - 0x1002;                    
              if ( VA )
              {
                VB = VA - 1;
                if ( VB && (VC = VB - 1) != 0 )
                {
                  VD = VC - 1;
                  if ( VD && (VE = VD - 1) != 0 && (VF = VE - 1) != 0 && (VG = VF - 13) != 0 && (VH = VG - 44) != 0 )
                    res = VH == 8 ? 16i64 : 0i64;
                  else
                    res = 8i64;
                }
                else
                {
                  res = 4i64;
                }
              }
              else
              {
                res = 2i64;
              }
              if ( (unsigned int)c_dword_2 / *((_DWORD *)ptr_to_buffer + 1) != res )
                return security_check_result;                                       
                                                                                      
 
              ptr_to_buffer += c_dword_2 + 12;
            }
          }
          else                                   
          {                                       
 
[8]
 
            if ( remaining_buffer_bytes < 4 )      
              return security_check_result;
            v24 = *(_DWORD *)ptr_to_buffer;        
            c_ptr_to_property_value = ptr_to_buffer + 4;// new exe: v13 = buffer_ptr + 4;
            v27 = remaining_buffer_bytes - 4;      
            if ( !(unsigned int)SecurityCheckSingleValue(v24, &c_ptr_to_property_value, &v27) )
              return security_check_result;
            remaining_buffer_bytes = v27;
            ptr_to_buffer = c_ptr_to_property_value;
          }
    LABEL_33:
          if ( !--l_header_dword_3 )
            break;
        }
      }
      if ( !l_header_dword_3 )
        security_check_result = 1;
      return security_check_result;
    }

在[4]处,将看待处置的属性的标签举行响应的平安检查。这里执行的检查,主要取决于在每个迭代中处置的属性是一个简朴属性照样一个复合属性。对于简朴属性(即标签低于0x1000的属性),在[8]处继续执行。对简朴属性举行以下检查:

◼若是缓冲区内的剩余字节数少于4,该函数将返回一个错误。

◼获得一个指向属性值的指针,并挪用SecurityCheckSingleValue()函数对简朴属性及其值举行平安检查。函数SecurityCheckSingleValue()会执行平安检查,并增添指针的值,使其指向缓冲区中的下一个属性,这样SecurityCheckPropArrayBuffer()函数就可以在下一次迭代中检查下一个属性了。

◼属性总数将被递减并与零举行对照。若是即是零,那么函数乐成返回。若是差异,则循环的下一次迭代会检查下一个属性。

同样,对于复合属性(即标签即是或高于0x1000的属性),将在[5]处继续执行,并举行以下操作。

对于可变长度的复合属性(若是属性标签即是0x1102(PtypMultipleBinary)或即是或小于0x101f(PtypMultipleString)),则在[6]处的代码将举行下面的处置:

◼缓冲区内剩余的可读字节数与0xC举行对照,以阻止缓冲区越界。

◼属性的Size字段与剩余的缓冲区长度举行对照,以阻止缓冲区越界。

◼对于每个嵌套的属性,都市挪用函数SecurityCheckSingleValue()举行如下处置:

     ◾对嵌套属性举行平安检查;

     ◾将指针移动到由挪用者持有的缓冲区,以便指向下一个嵌套属性;

◼循环运行,直到联系中人的总属性数(在每次迭代时都市递减)为零为止。

对于牢靠长度的复合属性(若是有关的属性标签差异于0x1102(PtypMultipleBinary),而且大于0x101f(PtypMultipleString)),从[7]处最先执行下列操作:

1、将缓冲区内剩余的读取字节数与0xC对照,以阻止缓冲区越界。

2、将Size与剩余的缓冲区长度举行对照,以阻止缓冲区的越界。

3、每个嵌套属性的长度,只取决于属性标签,而且通过父属性标签举行盘算。

4、Size除以NestedPropCount,获得每个嵌套属性的巨细。

5、若是盘算出的子属性长度与从父属性标签推导出的属性巨细差异,该函数会返回错误。

6、缓冲区指针凭证父属性值的长度递增,以指向下一个属性。

将未知或不能处置的属性类型的嵌套属性长度指定为0x0。

凭证我们的考察,若是盘算出的属性长度为0,缓冲区指针就会按头部形貌的属性值的长度前移。无论属性长度若何,缓冲区都市被推进,通过推进缓冲区,平安检查允许父属性(可能包罗子属性)的值保持不被检查的状态。为了使平安检查顺遂通过,在步骤4中对牢靠长度的复合属性举行的除法效果必须为零。因此,为了让一个未知的或不能处置的属性通过平安检查,NestedPropCount必须大于Size。请注重,由于任何属性的字节长度至少为2,以是,NestedPropCount必须始终不能大于Size的二分之一,因此,在正常情形下,上述除法的效果绝不能为零。

检查竣事后,若是检查失败,该函数返回0,若是检查通过,则返回1。

随后,函数HrGetPropArrayFromFileRecord()将挪用HrGetPropArrayFromBuffer()函数,其目的是将属性网络到_SPropValue结构数组中,并将其返回给挪用者。在这里,_SPropValue数组的长度即是属性的数目(由联系人头部给出),并通过挪用LocalAlloc()函数在堆中分配内存空间。另外,属性的数目乘以sizeof(_SPropValue),就能得出总的缓冲区巨细,详细如下所示:

    if ( !property_array_r )
    {
        ret = -2147024809;
        goto LABEL_71;
    }
    *property_array_r = 0i64;
    header_dword_3_1 = set_to_zero + header_dword_3;
 
[9]
 
    if ( (unsigned int)header_dword_3_1 < header_dword_3      
      || (unsigned int)header_dword_3_1 > 0xAAAAAAA           
      || (v10 = (unsigned int)header_dword_3_1,                              
          property_array = (struct _SPropValue *)LocalAlloc(
                                                   0x40u,
                                                   0x18 * header_dword_3_1),
                                                   // sizeof(_SPropValue) * n_properties_in_binary
        (*property_array_r = property_array) == 0i64) )
    {
        ERROR_INSUFICIENT_MEMORY:
        ret = 0x8007000E;
        goto LABEL_71;
    }

在[9]处,可以看到分配的内存长度为sizeof(_SPropValue) * n_properties_in_binary。紧接着,每个属性结构都市被初始化,其属性标签成员被设置为1。在初始化之后,已经对其举行了平安检查的缓冲区,将对各个属性举行响应的处置:让属性的指针指向一个属性,其属性头部和值的巨细由相关的属性提供。

若是特定的循环迭代处置的属性是一个简朴属性,那么将执行以下代码:

    if ( !_bittest((const signed int *)¤t_property_tag, 0xCu) )
    {
      if ( v16 < 4 )
        break;
      dword_1 = wab_ulong_buffer_full[1];
      ptr_to_dword_2 = (char *)(wab_ulong_buffer_full + 2);
      v38 = v16 - 4;
      if ( (unsigned int)dword_1 > v38 )
        break;
      current_property_tag = (unsigned __int16)current_property_tag;
      if ( (unsigned __int16)current_property_tag > 0xBu )
      {
 
[10]
 
        v39 = current_property_tag - 0x1E;
        if ( !v39 )
          goto LABEL_79;
        v40 = v39 - 1;
        if ( !v40 )
          goto LABEL_79;
        v41 = v40 - 0x21;
        if ( !v41 )
          goto LABEL_56;
        v42 = v41 - 8;
        if ( v42 )
        {
          if ( v42 != 0xBA )
            goto LABEL_56;
          v43 = dword_1;
          (*property_array_r)[p_idx].Value.bin.lpb = (LPBYTE)LocalAlloc(0x40u, dword_1);
          if ( !(*property_array_r)[p_idx].Value.bin.lpb )
            goto ERROR_INSUFICIENT_MEMORY;
          (*property_array_r)[p_idx].Value.l = dword_1;
          v44 = *(&(*property_array_r)[p_idx].Value.at + 1);
        }
        else
        {
    LABEL_79:
 
[11]
                                           
          v43 = dword_1;
          (*property_array_r)[p_idx].Value.cur.int64 = (LONGLONG)LocalAlloc(0x40u, dword_1);
          v44 = (*property_array_r)[p_idx].Value.dbl;
          if ( v44 == 0.0 )
            goto ERROR_INSUFICIENT_MEMORY;
        }
        memcpy_0(*(void **)&v44, ptr_to_dword_2, v43);
        wab_ulong_buffer_full = (ULONG *)&ptr_to_dword_2[v43];
      }
      else
      {
    LABEL_56:              
 
[12]
           
        memcpy_0(&(*property_array_r)[v15].Value, ptr_to_dword_2, dword_1);
        wab_ulong_buffer_full = (ULONG *)&ptr_to_dword_2[dword_1];
      }
      remaining_bytes_to_process = v38 - dword_1;
      goto NEXT_PROPERTY;
    }
 
[Truncated]
 
    NEXT_PROPERTY:
        ++p_idx;
        processed_property_count = (unsigned int)(processed_property_count_1 + 1);
        processed_property_count_1 = processed_property_count;
        if ( (unsigned int)processed_property_count >= c_header_dword_3 )
          return 0;
      }

在[10]处,属性标签被提取出来并与几个常量举行对照。若是属性标签是0x1e (PtypString8)、0x1f (PtypString)或0x48 (PtypGuid), 那么,将从[11]处继续执行。若是属性标签是0x40(PtypTime)或者无法识别,则从[12]处继续执行。不外,在[12]处的memcpy挪用很容易发生堆溢出。

反之,若是在特定的循环迭代中被处置的属性并非简朴属性,则执行下面的代码。值得注重的是,当下面的代码被执行时,指针DWORD* wab_ulong_buffer_full指向被处置的属性标签。无论哪个复合属性被处置,在属性标签被识别之前,缓冲区都市提前指向属性值的起始地址,即在第4个32位整数处。

[13]
 
    if ( v16 < 4 )
    break;
    c_dword_1 = wab_ulong_buffer_full[1];
    v19 = v16 - 4;
    if ( v19 < 4 )
    break;
    dword_2 = wab_ulong_buffer_full[2];
    wab_ulong_buffer_full += 3;                    
 
    remaining_bytes_to_process = v19 - 4;
 
[14]
 
    if ( (unsigned __int16)current_property_tag >= 0x1002u )
    {
    if ( (unsigned __int16)current_property_tag < = 0x1007u || (unsigned __int16)current_property_tag == 0x1014 )
        goto LABEL_80;
    if ( (unsigned __int16)current_property_tag == 0x101E )
    {
        [Truncated]
        
    }
    if ( (unsigned __int16)current_property_tag == 0x101F )
    {
        [Truncated]       
    }
    if ( ((unsigned __int16)current_property_tag - 0x1040) & 0xFFFFFFF7 )
    {
        if ( (unsigned __int16)current_property_tag == 0x1102 )
        {
        [Truncated]
        }
    }
    else
    {
    LABEL_80:
 
[15]
 
        (*property_array_r)[p_idx].Value.bin.lpb = (LPBYTE)LocalAlloc(0x40u, dword_2);
        if ( !(*property_array_r)[p_idx].Value.bin.lpb )
        goto ERROR_INSUFICIENT_MEMORY;
        (*property_array_r)[p_idx].Value.l = c_dword_1;
        if ( (unsigned int)dword_2 > remaining_bytes_to_process )
        break;
        memcpy_0((*property_array_r)[p_idx].Value.bin.lpb, wab_ulong_buffer_full, dword_2);
        wab_ulong_buffer_full = (ULONG *)((char *)wab_ulong_buffer_full + dword_2);
        remaining_bytes_to_process -= dword_2;
    }
    }
 
    NEXT_PROPERTY:
        ++p_idx;
        processed_property_count = (unsigned int)(processed_property_count_1 + 1);
        processed_property_count_1 = processed_property_count;
        if ( (unsigned int)processed_property_count >= c_header_dword_3 )
        return 0;
    }

缓冲区在[13]处被推进后,属性标签将与[14]处的几个常量举行对照。最后,[15]处的代码片断将试图处置一个复合属性(即>=0x1000),该属性带有前面的常量没有思量到的标签。

只管每种类型的属性的处置逻辑是不相关的,但一个有趣的事实是,若是属性标签无法识别,缓冲区指针仍然被推进到其头部的末尾,而且永远不会被收回。现实上,只要知足以下所有条件就会发生这种情形:

属性标签大于或即是0x1002。

属性标签大于0x1007。

属性标签不即是0x1014。

属性标签不即是0x101e。

属性标签不即是0x101f。

属性标签不即是0x1102。

隶属性标签中减去0x1040,并将效果与0xFFFFFFF7举行逐位AND运算,其效果为非零。

有趣的是,若是知足上述所有条件,复合属性的属性头部就会被跳过,而下一次循环迭代会将其属性体注释为差其余属性。

因此,通过以下考察效果,我们发现可以使HrGetPropArrayFromBuffer()在堆中分配的_SPropValue数组发生溢出:

若是NestedPropCount大于Size,可以通过全心组织一个未知的或不能处置的属性来绕过平安检查。

可以想法让HrGetPropArrayFromBuffer()将一个全心组织的属性的值注释为一个单独的属性。

PoC

为了凭证良性的WAB文件来组织恶意的WAB文件,可以从Windows地址簿的实例中导出一个有用的WAB文件。值得注重的是,Windows XP的Outlook Express提供了将联系人导出为WAB文件的功效。

通过更改WAB文件中的联系人使其具有以下特征,可以修改该良性WAB文件,使其成为恶意文件:

一个包罗以下内容的嵌套属性。

一个未知或不能处置类型的标签,例如标签0x1058,并知足以下条件:

必须大于或即是0x1002。

必须大于0x1007。

必须与0x1014、0x101e、0x101f和0x1102差异。

隶属性标签中减去0x1040,并使其与0xFFFFFFF7举行逐位AND操作的效果是非零的。

必须不即是0x1002、0x1003、0x1004、0x1005、0x1006、0x1007、0x1014、0x1040和0x1048。

NestedPropCount必须大于Size。

复合属性的值是空的。

一个恶意的简朴属性,包罗以下内容:

一个不即是0x1e、0x1f、0x40和0x48的属性标签。例如,标签0x0。

Size值大于0x18 x NestedPropCount,以便溢出_SPropValue数组。

未指定数目的尾部字节,以便溢出_SPropValue数组。

最后,当攻击者诱骗毫无戒心的用户导入特制的WAB文件时,就会触发该破绽,并可能实现代码执行。失败的破绽行使实验很可能会导致Windows地址簿导入工具溃逃。

由于ASLR的存在以及剧本引擎的缺失,我们无法在Windows 10环境中通过这个破绽实现随便代码执行攻击。

小结

在本文中,我们对CVE-2021-24083破绽举行了深入的考察,希望本文对读者领会这个破绽能够有所辅助!

本文翻译自:https://blog.exodusintel.com/2021/08/05/analysis-of-a-heap-buffer-overflow-vulnerability-in-microsoft-windows-address-book/
  • 皇冠管理端登3手机 @回复Ta

    2021-09-05 00:04:09 

    IPFS合租矿机www.ipfs8.vip)是Filecoin致力服务于使用Filecoin存储和检索数据的官方权威平台。IPFS官网实时更新FiLecion(FIL)行情、当前FiLecion(FIL)矿池、FiLecion(FIL)收益数据、各类FiLecion(FIL)矿机出售信息。并开放FiLecion(FIL)交易所、IPFS云矿机、IPFS矿机出售、IPFS矿机合租、IPFS算力合租、IPFS招商等业务。

    我明天还来!

    • USDT充值(www.usdt8.vip) @回复Ta

      2021-09-19 23:27:12 

      欢迎进入欧博亚洲〖zhou〗手机版{ban}下载(www.aLLbetgame.us),欧博官网是欧博‘bo’集团的官方网站。欧博官网开放Allbet注『zhu』册、Allbe代理、Allbet电〖dian〗脑客户端、Allbet手机版下载等业务。又一力作!

  • 欧博官网(www.aLLbetgame.us) @回复Ta

    2021-09-25 00:05:06 

    新2网址(zhi)(www.x2w00.com)实时更新发布最新最快的新2代理线路、新2会员线路、新2备用登录网址、新2手机版登录网址【zhi】、新2皇冠登录网址『zhi』。榜一就是你了

  • 澳洲幸运5开奖网(a55555.net) @回复Ta

    2021-10-01 00:06:21 

    USDT交易平台www.usdt8.vip)是使用TRC-20协议的Usdt官方交易所,开放USDT帐号注册、usdt小额交易、usdt线下现金交易、usdt实名不实名交易、usdt场外担保交易的平台。免费提供场外usdt承兑、低价usdt渠道、Usdt提币免手续费、Usdt交易免手续费。U交所开放usdt otc API接口、支付回调等接口。

    叫上伙伴一起来看。

  • usdt套利(www.usdt8.vip) @回复Ta

    2021-10-17 00:01:57 

    维生素A中毒可能会泛起恶心、吐逆、腹痛、食欲减退、视力模糊、嗜睡、头痛等症状;维生素D中毒可泛起高钙血症、食欲减退、腹泻或便秘、肌无力和疼痛、恶心、吐逆等。看我ID帅吗

  • aLLbet代理开户(www.aLLbetgame.us) @回复Ta

    2021-10-21 00:04:56 

    USDT场外交易平台www.usdt8.vip)是使用TRC-20协议的Usdt官方交易所,开放USDT帐号注册、usdt小额交易、usdt线下现金交易、usdt实名不实名交易、usdt场外担保交易的平台。免费提供场外usdt承兑、低价usdt渠道、Usdt提币免手续费、Usdt交易免手续费。U交所开放usdt otc API接口、支付回调等接口。

    有看点

发布评论