【技术分享】针对Windows图像解析组件的模糊测试

百家 作者:唯品会安全 2021-04-16 20:07:41

鸣  谢

VSRC感谢业界小伙伴——熙熙,投稿翻译稿件。VSRC欢迎精品原创类文章投稿,优秀文章一旦采纳发布,将有好礼相送,我们已为您准备好了丰富的奖品!活动最终解释权归VSRC所有)



针对Windows图像解析组件的模糊测试

Part Two:未初始化内存漏洞


在这篇文章中,我们将继续考察Windows系统中的图像解析漏洞;这一次,我们要讨论的漏洞类型相对来说并不是非常流行:未初始化内存漏洞。具体来说,我们将考察Windows系统内置的图像解析器在使用未初始化内存方面的安全漏洞。


01. 漏洞类型:未初始化内存



在非托管类型的编程语言中,如C或C++语言,变量在默认情况下是不进行初始化的。但是,使用未初始化的变量却会导致未定义的行为,甚至可能导致代码崩溃。总的来说,未初始化内存漏洞大致有两种变体:


直接使用未初始化的内存:在执行读取或写入操作时使用未初始化的指针或索引。这可能会导致崩溃。


通过使用未初始化的内存导致信息泄露:未初始化的内存中的内容可以跨越安全边界进行访问。例如,一个未初始化的内核缓冲区可以从用户模式进行访问,从而导致信息泄露。


在这篇文章中,我们将仔细研究Windows图像解析器中未初始化内存漏洞的第二种变体,这种漏洞将导致信息泄露,比如在Web浏览器中,攻击者可以使用JavaScript读取解码后的图像。

02. 检测未初始化的内存漏洞

与堆溢出、UAF漏洞等内存损坏漏洞相比,未初始化的内存漏洞本身并不会导致内存的越界访问。这使得对这些漏洞的检测比内存损坏漏洞要更困难一些。虽然未初始化内存的直接使用会导致崩溃,并且很容易被检测到,但信息泄露通常不会导致任何崩溃。为了检测后一种情况,通常需要借助于编译器工具(如MemorySanitizer)或二进制插桩/反编译工具(如Valgrind)。


03. Detour:检测Linux

      系统中未初始化的内存

下面,让让我们先绕个小弯子,即先来看看在Linux系统中检测未初始化内存的方法,并与Windows的内置功能进行比较。即使编译器会对一些未初始化的变量发出警告,但大多数复杂的未初始化内存使用情况在编译时都很难被检测出来。对此,我们可以借助于运行时检测机制。MemorySanitizer是一个适用于GCC和Clang等编译器的工具,通过它,可以检测针对未初始化内存的读取操作。展示了这个工具的使用方法。


上下拉动翻看


$ cat sample.cc

#include <stdio.h>


int main()

{

    int *arr = new int[10];

    if(arr[3] == 0)

    {

         printf("Yay!\n");

    }

    printf("%08x\n", arr[3]);

    return 0;

}


$ clang++ -fsanitize=memory -fno-omit-frame-pointer -g sample.cc


$ ./a.out

==29745==WARNING: MemorySanitizer: use-of-uninitialized-value

    #0 0x496db8  (/home/dan/uni/a.out+0x496db8)

    #1 0x7f463c5f1bf6  (/lib/x86_64-linux-gnu/libc.so.6+0x21bf6)

    #2 0x41ad69  (/home/dan/uni/a.out+0x41ad69)


SUMMARY: MemorySanitizer: use-of-uninitialized-value (/home/dan/uni/a.out+0x496db8)

Exiting


利用MemorySanitizer检测未初始化的内存


同样,Valgrind也可以用来检测运行时未初始化的内存。


04. 在Windows系统中

       检测未初始化的内存

与Linux系统相比,Windows系统并没有提供检测未初始化内存使用情况的内置工具。虽然Visual Studio和Clang-cl最近引入了AddressSanitizer,但直至撰写本文时,仍未实现MemorySanitizer或类似的未初始化内存检测工具。


不过,在检测内存损坏漏洞方面,Windows系统中还是提供了一些有用的工具的,如PageHeap,但是,它们却无法用于检测未初始化的内存。相反,PageHeap的用途是用某些模式来填充分配的内存空间,这实际上就是对内存进行初始化。


另外,各种第三方工具(包括Dr.Memory在内)基本上都不支持通过二进制插桩技术来检测堆溢出、使用未初始化的内存、释放后使用等内存安全问题。


05. 在图像解码组件中

       检测未初始化的内存

在Windows系统中检测未初始化的内存时,通常需要借助于二进制插桩技术,特别是当我们无法访问源代码时。具体到图像解码组件来说,我们可以用来检测未初始化内存使用情况的指标之一,就是对图像进行解码后得到的像素。


当图像被解码时,将生成一组原始像素。如果图像解码使用了任何未初始化的内存,那么,得到的部分或全部像素就可能变成一些随机值。更简单的说,如果使用了未初始化的内存,那么,当对同一个图像进行多次解码时,每次的输出结果都是不一样的。这种输出的差异可以用来检测未初始化的内存,并帮助编写针对Windows图像解码器的模糊测试harness。图2给出了一个模糊测试harness的例子。


上下拉动翻看

#define ROUNDS 20


unsigned char* DecodeImage(char *imagePath)

{

      unsigned char *pixels = NULL;    


      // use GDI or WIC to decode image and get the resulting pixels

      ...

      ...    


      return pixels;

}


void Fuzz(char *imagePath)

{

      unsigned char *refPixels = DecodeImage(imagePath);    


      if(refPixels != NULL)

      {

            for(int i = 0; i < ROUNDS; i++)

            {

                  unsigned char *currPixels = DecodeImage(imagePath);

                  if(!ComparePixels(refPixels, currPixels))

                  {

                        // the reference pixels and current pixels don't match

                        // crash now to let the fuzzer know of this file

                        CrashProgram();

                  }

                  free(currPixels);

            }

         free(refPixels);

      }

}

用于生成不同解码结果的harness

这个模糊测试harness背后的想法并不是全新的,之前,lcamtuf就是用类似的想法来检测开源图像解析器中的未初始化内存漏洞,并通过网页来展示像素的差异。


06. 模糊测试

准备好生成不同像素的harness后,我们就可以继续寻找受支持的图像格式并收语料库(corpuses)了。由于互联网上的图像文件几乎是取之不尽用之不竭的,因此,收集图像文件作为语料库并非难事,但同时要在数以百万计的具有独特代码覆盖率的文件中找到好的语料库仍然还是比较困难的。Windows图像解析组件的代码覆盖率信息是从WindowsCodecs.dll中追踪到的。


请注意,与常规的Windows模糊测试不同,这次我们不会启用PageHeap,因为PageHeap会用某种模式来“初始化”分配的堆内存。


07. 测试结果

在对Windows内置的图像解析器进行模糊测试过程中,我发现了三个使用未初始化的内存的漏洞;并且在下文中,我们要对其中的两个漏洞进行详细的介绍。另外,对未初始化的内存使用情况进行根本原因分析并非易事,因为我们没有崩溃位置可以回溯,所以,必须使用结果像素缓冲区来进行回溯才能找到根本原因,或者使用其他巧妙的技巧来找出其中的原因。


08.CVE-2020-0853漏洞 

在分析这个漏洞的根本原因之前,让我们先看看PoC文件的渲染结果。为此,我们将借助于lcamtuf的HTML代码,它将重复加载PoC图像,并将像素与参考像素进行比较。

CVE-2020-0853漏洞


从生成的图像来看,每次解码输出的变化还是很大的,据此可以判断:这个PoC泄漏了大量未初始化的内存。


为了找出这些漏洞的根本原因,我大量使用了时空旅行调试技术(Time Travel Debugging,TTD)。众所周知,追溯执行过程和跟踪内存地址是一项非常繁琐的工作,但TTD通过保持地址和值不变,并提供无限次的向前和向后执行,可以给我们带来较大的帮助。


经过一番调试跟踪后,我终于在windowscodecs!CFormatConverter::Initialize中找到了未初始化内存的源。虽然找到了源,但最初并不清楚为什么这块内存在计算像素的过程中没有被覆盖。为了解开这个谜团,我们通过对比PoC执行跟踪和正常的TIFF文件解码进行了额外的调试工作。在下一节中,我们将为读者详细未初始化值的分配、复制到像素计算代码中的情况,以及该漏洞的根因分析。


09.未初始化内存的分配和使用情况

windowscodecs!CFormatConverter::Initialize将分配一段长度为0x40字节的内存:

 

上下拉动翻看

0:000> r

rax=0000000000000000 rbx=0000000000000040 rcx=0000000000000040

rdx=0000000000000008 rsi=000002257a3db448 rdi=0000000000000000

rip=00007ffaf047a238 rsp=000000ad23f6f7c0 rbp=000000ad23f6f841

 r8=000000ad23f6f890  r9=0000000000000010 r10=000002257a3db468

r11=000000ad23f6f940 r12=000000000000000e r13=000002257a3db040

r14=000002257a3dbf60 r15=0000000000000000

iopl=0         nv up ei pl zr na po nc

cs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246

windowscodecs!CFormatConverter::Initialize+0x1c8:

00007ffa`f047a238 ff15ea081200    call    qword ptr [windowscodecs!_imp_malloc (00007ffa`f059ab28)] ds:00007ffa`f059ab28={msvcrt!malloc (00007ffa`f70e9d30)}

0:000> k

 # Child-SP          RetAddr               Call Site

00 000000ad`23f6f7c0 00007ffa`f047c5fb     windowscodecs!CFormatConverter::Initialize+0x1c8

01 000000ad`23f6f890 00007ffa`f047c2f3     windowscodecs!CFormatConverter::Initialize+0x12b

02 000000ad`23f6f980 00007ff6`34ca6dff     windowscodecs!CFormatConverterResolver::Initialize+0x273


//Uninitialized memory after allocation:

0:000> db @rax

00000225`7a3dbf70  d0 b0 3d 7a 25 02 00 00-60 24 3d 7a 25 02 00 00  ..=z%...`$=z%...

00000225`7a3dbf80  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................

00000225`7a3dbf90  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................

00000225`7a3dbfa0  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................

00000225`7a3dbfb0  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................

00000225`7a3dbfc0  00 00 00 00 00 00 00 00-64 51 7c 26 c3 2c 01 03  ........dQ|&.,..

00000225`7a3dbfd0  f0 00 2f 6b 25 02 00 00-f0 00 2f 6b 25 02 00 00  ../k%...../k%...

00000225`7a3dbfe0  60 00 3d 7a 25 02 00 00-60 00 3d 7a 25 02 00 00  `.=z%...`.=z%...



由于这段内存从未被写入任何的值,因此,未初始化的值将在windowscodecs!CLibTiffDecoderBase::HrProcessCopy中被反转,并在windowscodecs!GammaConvert_16bppGrayInt_128bppRGBA以及后面调用的缩放函数中做进一步的处理。

 

由于在HrProcessCopy函数之前,并没有对未初始化的内存进行读写操作,所以,我选择从HrProcessCopy函数开始追溯执行,并将执行轨迹与正常的tiff解码轨迹进行了对比,发现windowscodecs!CLibTiffDecoderBase::UnpackLine函数对PoC文件的处理方式与普通tiff文件的处理方式存在差异,并且UnpackLine函数中的一个函数参数是指向未初始化缓冲区的指针。

 

UnPackLine函数中使用了许多Switch-Case语句,用于处理TIFF图像的BPS(bits per sample,BPS)。在我们的PoC TIFF文件中,BPS值是0x09(UnpackLine函数并不支持这个值),并且控制流永远都不会到达写入缓冲区的代码路径。这就是未初始化内存的根本原因,这段内存会被后面的代码做进一步的处理,并最后显示为像素数据。


10.漏洞的修复

在微软收到我提交的分析报告后,他们决定对该漏洞进行修复,方法是将不支持BPS值的文件列为无效文件。这样就避免了所有的解码处理,并在文件加载的早期阶段拒绝该文件。

CVE-2020-1397

CVE-2020-1397的渲染图

与前一个漏洞不同,这个漏洞中输出的不同之处是相当少的,具体如图5所示。比较产生两个不同输出的代码的执行轨迹是一种相对简单的根本原因分析技术,可以用来找出特定类型的未初始化内存使用情况。当一个未初始化的变量导致程序中的控制流改变,从而导致输出产生差异时,这种技术会特别有用。为此,我们编写了一个二进制插桩脚本,用于记录指令执行过程中涉及的所有寄存器的值以及访问过的内存的值。


通过比较指令指针(RIP)的值,我发现了两个不同的执行轨迹,说明windowscodecs!CCCITT::Expand2DLine函数的控制流发生了变化,原因是它使用了一个未初始化的值。使用TTD trace对未初始化的值进行回溯,对于找到根本原因来说是异常有用的。在下一节中,我们将介绍该未初始化值对应内存的分配、填充和使用情况,正是它导致了控制流的变化和像素输出的偏差。



11. 相关内存的分配

windowscodecs!TIFFReadBufferSetup函数将分配长度为0x400字节的一段内存:


上下拉动翻看

windowscodecs!TIFFReadBufferSetup:

    ...

    allocBuff = malloc(size);

    *(v3 + 16) |= 0x200u;

    *(v3 + 480) = allocBuff;


0:000> k

 # Child-SP          RetAddr           Call Site

00 000000aa`a654f128 00007ff9`4404d4f3 windowscodecs!TIFFReadBufferSetup

01 000000aa`a654f130 00007ff9`4404d3c9 windowscodecs!TIFFFillStrip+0xab

02 000000aa`a654f170 00007ff9`4404d2dc windowscodecs!TIFFReadEncodedStrip+0x91

03 000000aa`a654f1b0 00007ff9`440396dd windowscodecs!CLibTiffDecoderBase::ReadStrip+0x74

04 000000aa`a654f1e0 00007ff9`44115fca windowscodecs!CLibTiffDecoderBase::GetOneUnpackedLine+0x1ad

05 000000aa`a654f2b0 00007ff9`44077400 windowscodecs!CLibTiffDecoderBase::HrProcessCopy+0x4a

06 000000aa`a654f2f0 00007ff9`44048dbb windowscodecs!CLibTiffDecoderBase::HrReadScanline+0x20

07 000000aa`a654f320 00007ff9`44048b40 windowscodecs!CDecoderBase::CopyPixels+0x23b

08 000000aa`a654f3d0 00007ff9`44043c95 windowscodecs!CLibTiffDecoderBase::CopyPixels+0x80

09 000000aa`a654f4d0 00007ff9`4404563b windowscodecs!CDecoderFrame::CopyPixels+0xb5




After allocation:

0:000> !heap -p -a @rax

    address 0000029744382140 found in

    _HEAP @ 29735190000

              HEAP_ENTRY Size Prev Flags            UserPtr UserSize - state

        0000029744382130 0041 0000  [00]   0000029744382140    00400 - (busy)

          unknown!noop


//Uninitialized memory after allocation       

0:000> db @rax

00000297`44382140  40 7c 5e 97 29 5d 5f ae-73 31 98 70 b8 4f da ac  @|^.)]_.s1.p.O..

00000297`44382150  06 51 54 18 2e 2a 23 3a-4f ab 14 27 e9 c6 2c 83  .QT..*#:O..'..,.

00000297`44382160  3a 25 b2 f6 9d e7 3c 09-cc a5 8e 27 b0 73 41 a9  :%....<....'.sA.

00000297`44382170  fb 9b 02 b5 81 3e ea 45-4c 0f ab a7 72 e3 21 e7  .....>.EL...r.!.

00000297`44382180  c8 44 84 3b c3 b5 44 8a-c9 6e 4b 2e 40 31 38 e0  .D.;..D..nK.@18.

00000297`44382190  85 f0 bd 98 3b 0b ca b8-78 b1 9d d0 dd 4d 61 66  ....;...x....Maf

00000297`443821a0  16 7d 0a e2 40 fa f8 45-4f 79 ab 95 d8 54 f9 44  .}..@..EOy...T.D

00000297`443821b0  66 26 28 00 b7 96 52 88-15 f0 ed 34 94 5f 6f 94  f&(...R....4._o.



 内存的分配


12.填充部分缓冲区

实际上,有0x10字节的内容是由TIFFReadRawStrip1函数从输入文件复制到这个分配的缓冲区中的;而该缓冲区的其余部分仍用随机值进行未初始化,具体如下。


上下拉动翻看

if ( !TIFFReadBufferSetup(v2, a2, stripCount) ) {

      return 0i64;

}

if ( TIFFReadRawStrip1(v2, v3, sizeToReadFromFile, "TIFFFillStrip") != sizeToReadFromFile )


0:000> r

rax=0000000000000001 rbx=000002973519a7e0 rcx=000002973519a7e0

rdx=0000000000000000 rsi=0000000000000000 rdi=0000000000000010

rip=00007ff94404d58c rsp=000000aaa654f128 rbp=0000000000000000

 r8=0000000000000010  r9=00007ff94416fc38 r10=0000000000000000

r11=000000aaa654ef60 r12=0000000000000001 r13=0000000000000000

r14=0000029744377de0 r15=0000000000000001

iopl=0         nv up ei pl nz na pe nc

cs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202

windowscodecs!TIFFReadRawStrip1:

00007ff9`4404d58c 488bc4          mov     rax,rsp

0:000> k

 # Child-SP          RetAddr           Call Site

00 000000aa`a654f128 00007ff9`4404d491 windowscodecs!TIFFReadRawStrip1

01 000000aa`a654f130 00007ff9`4404d3c9 windowscodecs!TIFFFillStrip+0x49

02 000000aa`a654f170 00007ff9`4404d2dc windowscodecs!TIFFReadEncodedStrip+0x91

03 000000aa`a654f1b0 00007ff9`440396dd windowscodecs!CLibTiffDecoderBase::ReadStrip+0x74

04 000000aa`a654f1e0 00007ff9`44115fca windowscodecs!CLibTiffDecoderBase::GetOneUnpackedLine+0x1ad

05 000000aa`a654f2b0 00007ff9`44077400 windowscodecs!CLibTiffDecoderBase::HrProcessCopy+0x4a

06 000000aa`a654f2f0 00007ff9`44048dbb windowscodecs!CLibTiffDecoderBase::HrReadScanline+0x20

07 000000aa`a654f320 00007ff9`44048b40 windowscodecs!CDecoderBase::CopyPixels+0x23b

08 000000aa`a654f3d0 00007ff9`44043c95 windowscodecs!CLibTiffDecoderBase::CopyPixels+0x80

09 000000aa`a654f4d0 00007ff9`4404563b windowscodecs!CDecoderFrame::CopyPixels+0xb5


0:000> db 00000297`44382140

00000297`44382140  5b cd 82 55 2a 94 e2 6f-d7 2d a5 93 58 23 00 6c  [..U*..o.-..X#.l             // 0x10 bytes from file

00000297`44382150  06 51 54 18 2e 2a 23 3a-4f ab 14 27 e9 c6 2c 83  .QT..*#:O..'..,.             // uninitialized memory

00000297`44382160  3a 25 b2 f6 9d e7 3c 09-cc a5 8e 27 b0 73 41 a9  :%....<....'.sA.

00000297`44382170  fb 9b 02 b5 81 3e ea 45-4c 0f ab a7 72 e3 21 e7  .....>.EL...r.!.

00000297`44382180  c8 44 84 3b c3 b5 44 8a-c9 6e 4b 2e 40 31 38 e0  .D.;..D..nK.@18.

00000297`44382190  85 f0 bd 98 3b 0b ca b8-78 b1 9d d0 dd 4d 61 66  ....;...x....Maf

00000297`443821a0  16 7d 0a e2 40 fa f8 45-4f 79 ab 95 d8 54 f9 44  .}..@..EOy...T.D

00000297`443821b0  66 26 28 00 b7 96 52 88-15 f0 ed 34 94 5f 6f 94  f&(...R....4._o.


 对部分内存进行填充


13. 未初始化内存的使用

上下拉动翻看

0:000> r

rax=0000000000000006 rbx=0000000000000007 rcx=0000000000000200

rdx=0000000000011803 rsi=0000029744382150 rdi=0000000000000000

rip=00007ff94414e837 rsp=000000aaa654f050 rbp=0000000000000001

 r8=0000029744382550  r9=0000000000000000 r10=0000000000000008

r11=0000000000000013 r12=00007ff94418b7b0 r13=0000000000000003

r14=0000000023006c00 r15=00007ff94418bbb0

iopl=0         nv up ei pl nz na po nc

cs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000206

windowscodecs!CCCITT::Expand2DLine+0x253:

00007ff9`4414e837 0fb606          movzx   eax,byte ptr [rsi] ds:00000297`44382150=06             ; Uninitialized memory being accessed




0:000> db 00000297`44382140

00000297`44382140  5b cd 82 55 2a 94 e2 6f-d7 2d a5 93 58 23 00 6c  [..U*..o.-..X#.l             // 0x10 bytes from file

00000297`44382150  06 51 54 18 2e 2a 23 3a-4f ab 14 27 e9 c6 2c 83  .QT..*#:O..'..,.             // uninitialized memory

00000297`44382160  3a 25 b2 f6 9d e7 3c 09-cc a5 8e 27 b0 73 41 a9  :%....<....'.sA.

00000297`44382170  fb 9b 02 b5 81 3e ea 45-4c 0f ab a7 72 e3 21 e7  .....>.EL...r.!.

00000297`44382180  c8 44 84 3b c3 b5 44 8a-c9 6e 4b 2e 40 31 38 e0  .D.;..D..nK.@18.

00000297`44382190  85 f0 bd 98 3b 0b ca b8-78 b1 9d d0 dd 4d 61 66  ....;...x....Maf

00000297`443821a0  16 7d 0a e2 40 fa f8 45-4f 79 ab 95 d8 54 f9 44  .}..@..EOy...T.D

00000297`443821b0  66 26 28 00 b7 96 52 88-15 f0 ed 34 94 5f 6f 94  f&(...R....4._o.




0:000> k

 # Child-SP          RetAddr           Call Site

00 000000aa`a654f050 00007ff9`4414df80 windowscodecs!CCCITT::Expand2DLine+0x253

01 000000aa`a654f0d0 00007ff9`4412afcc windowscodecs!CCCITT::CCITT_Expand+0xac

02 000000aa`a654f120 00007ff9`4404d3f0 windowscodecs!CCITTDecode+0x7c

03 000000aa`a654f170 00007ff9`4404d2dc windowscodecs!TIFFReadEncodedStrip+0xb8

04 000000aa`a654f1b0 00007ff9`440396dd windowscodecs!CLibTiffDecoderBase::ReadStrip+0x74

05 000000aa`a654f1e0 00007ff9`44115fca windowscodecs!CLibTiffDecoderBase::GetOneUnpackedLine+0x1ad

06 000000aa`a654f2b0 00007ff9`44077400 windowscodecs!CLibTiffDecoderBase::HrProcessCopy+0x4a

07 000000aa`a654f2f0 00007ff9`44048dbb windowscodecs!CLibTiffDecoderBase::HrReadScanline+0x20

08 000000aa`a654f320 00007ff9`44048b40 windowscodecs!CDecoderBase::CopyPixels+0x23b

09 000000aa`a654f3d0 00007ff9`44043c95 windowscodecs!CLibTiffDecoderBase::CopyPixels+0x80

0a 000000aa`a654f4d0 00007ff9`4404563b windowscodecs!CDecoderFrame::CopyPixels+0xb5


读取未初始化的值


实际上,函数Expand2DLine会根据未初始化的值采取不同的代码路径,从而导致它会输出不同的像素,具体代码如下。

上下拉动翻看


  {

    {

        if ( v11 != 1 || a2 )

        {

            unintValue = *++allocBuffer | (unintValue << 8);          // uninit mem read

        }

        else

        {

            unintValue <<= 8;

            ++allocBuffer;

        }

        --v11;

        v16 += 8;

      }

      v29 = unintValue >> (v16 - 8);

      dependentUninitValue = *(l + 2i64 * v29);                          

      v16 -= *(l + 2i64 * v29 + 1);

      if ( dependentUninitValue >= 0 )             // path 1

        break;

      if ( dependentUninitValue < '\xC0' )

        return 0xFFFFFFFFi64;                     // path 2

  }

  if ( dependentUninitValue <= 0x3F )              // path xx

      break;


未初始化内存的使用情况


14。漏洞的修复 

微软已经通过用calloc函数代替malloc函数修复了这个漏洞,因为malloc会用0来初始化分配的内存。


VSRC招人啦!

欢迎投递!

敢想敢做唯 | 唯品会安全部门期待你的加入!

精彩原创文章投稿有惊喜!

欢迎投稿!
VSRC欢迎精品原创类文章投稿,优秀文章一旦采纳发布,将为您准备的丰富奖金税后1000元现金或等值礼品,上不封顶!如若是安全文章连载,奖金更加丰厚,税后10000元或等值礼品,上不封顶!可点击“VSRC投稿能挣万元?快到碗里来!”了解规则。(最终奖励以文章质量为准。活动最终解释权归VSRC所有)


点击 阅读原文 进入本次分享英文原文


关注公众号:拾黑(shiheibook)了解更多

[广告]赞助链接:

四季很好,只要有你,文娱排行榜:https://www.yaopaiming.com/
让资讯触达的更精准有趣:https://www.0xu.cn/

公众号 关注网络尖刀微信公众号
随时掌握互联网精彩
赞助链接