CVE-2019-5786漏洞分析

漏洞公告

按照惯例,先上漏洞通告:72.0.3626.121版本之前的Google Chrome存在漏洞,漏洞源于Blink中的对象生存处理问题。远程攻击者可通过诱导用户点击精心制作的HTML页面来进行超出范围的内存访问,成功利用此漏洞的远程攻击者可执行任意代码(还需结合Win32k内核提权漏洞,从而绕过Chrome沙箱保护)。

补丁比对

注意到以下对FileReaderLoader::ArrayBufferResult函数的修复,这段补丁是72.0.3626.120版本到72.0.3626.121版本的改变中唯一一段看上去是针对Blink的安全修复。下面来分析一下,这个流程主要分为两个状态,finished_loading和unfinished_loading,两段代码只有一处不同。补丁前的代码中,无论是否加载完成,均返回DOMArrayBuffer::Create(raw_data_->ToArrayBuffer());而补丁后的代码在未加载完成时会返回DOMArrayBuffer::Create(ArrayBuffer::Create(raw_data_->Data(), raw_data_->ByteLength()));加载完成后返回DOMArrayBuffer::Create(raw_data_->ToArrayBuffer())。


显然,未加载完成时的FileReaderLoader::ArrayBufferResult函数返回值成为本次研究的重点,从名称上来看,这个函数和File加载时ArrayBuffer的result相关。

补丁前:DOMArrayBuffer::Create(raw_data_->ToArrayBuffer())
补丁后:DOMArrayBuffer::Create(ArrayBuffer::Create(raw_data_->Data(), raw_data_->ByteLength()))

首先分析ToArrayBuffer函数,它返回buffer_或buffer_->Slice(0, bytes_used_):

scoped_refptr ArrayBufferBuilder::ToArrayBuffer() {
  // Fully used. Return m_buffer as-is.
  if (buffer_->ByteLength() == bytes_used_)
    return buffer_;
  return buffer_->Slice(0, bytes_used_);
}

ArrayBuffer::Slice(int begin, int end)函数中又会调用SliceImpl(ClampIndex(begin),ClampIndex(end)),最终调用ArrayBuffer::Create(static_cast(Data()) + begin, size),所以程序在finished_loading和unfinished_loading这两个状态查看ArrayBufferResult时都有可能调用ArrayBuffer::Create函数:

scoped_refptr ArrayBuffer::Slice(int begin, int end) const {
  return SliceImpl(ClampIndex(begin), ClampIndex(end));
}

scoped_refptr ArrayBuffer::SliceImpl(unsigned begin, unsigned end) const {
  size_t size = static_cast(begin <= end ? end - begin : 0);
  return ArrayBuffer::Create(static_cast(Data()) + begin, size);
}

再来看ArrayBuffer::Create函数,创建一个新的ArrayBuffer,然后将source复制进去。现在总结一下,修补后的FileReaderLoader::ArrayBufferResult函数会直接调用DOMArrayBuffer::Create(ArrayBuffer::Create(raw_data_->Data(), raw_data_->ByteLength())),而修补前的程序会在ArrayBufferBuilder::ToArrayBuffer函数中进行判断,如果buffer_->ByteLength() == bytes_used_就直接返回buffer_,否则返回buffer_->Slice(0, bytes_used_),最终还是会调用ArrayBuffer::Create(raw_data_->Data(), raw_data_->ByteLength()),所以问题应该出现在buffer_->ByteLength() == bytes_used_时:

scoped_refptr ArrayBuffer::Create(const void* source, size_t byte_length) {
  ArrayBufferContents contents(byte_length, 1, ArrayBufferContents::kNotShared,ArrayBufferContents::kDontInitialize);
  if (UNLIKELY(!contents.Data()))
    OOM_CRASH();
  scoped_refptr buffer = base::AdoptRef(new ArrayBuffer(contents));
  memcpy(buffer->Data(), source, byte_length);
  return buffer;
}

根据以下代码可知,buffer_属于scoped_refptr类型,至于buffer_->ByteLength()和bytes_used_是什么,就在调试中看吧,不然源码搜索太费劲:

static const int kDefaultBufferCapacity = 32768;

ArrayBufferBuilder::ArrayBufferBuilder()
    : bytes_used_(0), variable_capacity_(true) {
  buffer_ = ArrayBuffer::Create(kDefaultBufferCapacity, 1);
}

调试分析

FileReader对象
根据file_reader_loader文件名可推测这段代码会处理FileReader对象,在MDN中可以搜索该对象的详细使用方法。

FileReader对象可以异步读取用户计算机上存储的文件(或原始数据缓冲区)的内容,使用File或Blob对象指定要读取的文件或数据。Blob对象代表不可变的原始数据的类似文件的对象。它们可以读取为文本或二进制数据,也可以转换为Readable Stream。 Blob可以表示不一定是JavaScript本机格式的数据。 File接口基于Blob,继承了Blob功能并将其扩展为支持用户系统上的文件。File提供有关文件的信息,并允许网页中的JavaScript访问其内容。

FileReader有一个readyState属性,记录其读取时的状态,分别为:EMPTY(还未加载)、LOADING(正在加载)、DONE(加载完成),对应了前面分析的 finished_loading 和 !finished_loading。

FileReader有一些内置事件,包括abort、error、load、loadend、loadstart、progress。可以为这些事件自定义处理函数,其中progress事件在读取数据时定期触发,我们可以注册progress事件的回调函数。如果在这时候去获取result,就会在未加载完成时进入FileReaderLoader::ArrayBufferResult函数。如果将要读取的数据的长度设置的稍微大一点,就会在加载的过程中多次回调这个函数。

FileReader.onprogress
A handler for the progress event. This event is triggered while reading a Blob content.
FileReader.onloadstart
A handler for the loadstart event. This event is triggered each time the reading is starting.

可以通过以上信息构造出一些代码,以供后续调试分析…

下面开始调试啦啦啦啦啦啦:
查找FileReaderLoader::ArrayBufferResult并下断点,由于在这个函数内部调用了ArrayBufferBuilder::ToArrayBuffer函数,可以单步跟踪过去,程序判断一些条件满足之后,就调用了WTF::ArrayBufferBuilder::ToArrayBuffer函数。

0:000> x chrome_child!*FileReaderLoader::ArrayBufferResult
61875480 chrome_child!blink::FileReaderLoader::ArrayBufferResult (void)

下面为WTF::ArrayBufferBuilder::ToArrayBuffer函数中的流程,从eax+8中取出值赋给ebx,值得关注的是0x4c4b400转化为十进制的值为80000000,为测试js中Blob对象字符串的长度,所以有理由怀疑buffer_->ByteLength()返回的是ArrayBuffer对象应有的长度。而后面的0x3f50000转化过来为66387968,说明已经读取了66387968字节,还没有完全加载完成,因而会调用ArrayBuffer::SliceImpl函数:

chrome_child!WTF::ArrayBufferBuilder::ToArrayBuffer:
61bd02d8 55              push    ebp
61bd02d9 89e5            mov     ebp,esp
61bd02db 53              push    ebx
61bd02dc 57              push    edi
61bd02dd 56              push    esi
61bd02de 8b5108          mov     edx,dword ptr [ecx+8]
61bd02e1 8b7508          mov     esi,dword ptr [ebp+8]
61bd02e4 8b4204          mov     eax,dword ptr [edx+4]
61bd02e7 85c0            test    eax,eax
61bd02e9 7405            je      chrome_child!WTF::ArrayBufferBuilder::ToArrayBuffer+0x18 (61bd02f0)
61bd02eb 8b5808          mov     ebx,dword ptr [eax+8] ds:0023:04469ea8=04c4b400
61bd02ee eb02            jmp     chrome_child!WTF::ArrayBufferBuilder::ToArrayBuffer+0x1a (61bd02f2)
0:000> 
eax=04469ea0 ebx=0022e838 ecx=04405090 edx=04404570 esi=0022e838 edi=04588000
eip=61bd02eb esp=0022e820 ebp=0022e82c iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
chrome_child!WTF::ArrayBufferBuilder::ToArrayBuffer+0x13:
61bd02eb 8b5808          mov     ebx,dword ptr [eax+8] ds:0023:04469ea8=04c4b400

0:000> 
eax=04469ea0 ebx=04c4b400 ecx=04405090 edx=04404570 esi=0022e838 edi=04588000
eip=61bd02f2 esp=0022e820 ebp=0022e82c iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
chrome_child!WTF::ArrayBufferBuilder::ToArrayBuffer+0x1a:
61bd02f2 8b39            mov     edi,dword ptr [ecx]  ds:0023:04405090=03f50000

调试WTF::ArrayBufferBuilder::ToArrayBuffer函数就会发现ArrayBuffer::SliceImpl函数的流程位于这个函数中(比较神奇,第一次调还以为被补丁了呢),跟踪多次发现程序都走了else流程,也就是buffer_->Slice(0, bytes_used_),返回的都是最终指向3e00000大小堆的不同blink::DOMArrayBuffer结构。

0:000> dd eax
2510f5a8  6284a020 00000000 04405260 00000000   chrome_child!blink::DOMArrayBuffer::`vftable'
2510f5b8  00000000 00000000 00000000 00000000
2510f5c8  00000000 00000000 00000000 00000000
2510f5d8  00000000 00000000 00000000 00000000
2510f5e8  00000000 00000000 00000000 00000000
2510f5f8  00000000 00000000 00000000 00000000
2510f608  00000000 00000000 00000000 00000000
2510f618  00000000 00000000 00000000 00000000
0:000> dd 4405260
04405260  00000001 0446a320 00000000 00000000
04405270  00000001 0446a340 00000000 00000000
04405280  00000001 0446a360 00000000 00000000
04405290  a0524004 00000000 00000000 00000000
044052a0  b0524004 00000000 00000000 00000000
044052b0  c0524004 00000000 00000000 00000000
044052c0  d0524004 00000000 00000000 00000000
044052d0  e0524004 00000000 00000000 00000000
0:000> dd 0446a320
0446a320  00000001 3ea04000 03e00000 605afe8e
0446a330  00000000 00000000 00000001 00000000
0446a340  00000001 50804000 03c00000 605afe8e
0446a350  00000000 00000000 00000001 00000000
0446a360  00000001 67204000 03d50000 605afe8e
0446a370  00000000 00000000 00000001 00000000
0446a380  a0a34604 00000000 00000000 5ee13660
0446a390  00000000 00000000 00000000 00000000
0:000> dd 3ea04000
3ea04000  41414141 41414141 41414141 41414141
3ea04010  41414141 41414141 41414141 41414141
3ea04020  41414141 41414141 41414141 41414141
3ea04030  41414141 41414141 41414141 41414141
3ea04040  41414141 41414141 41414141 41414141
3ea04050  41414141 41414141 41414141 41414141
3ea04060  41414141 41414141 41414141 41414141
3ea04070  41414141 41414141 41414141 41414141

0:000> dd eax
2510f5b8  6284a020 00000000 04405250 00000000  chrome_child!blink::DOMArrayBuffer::`vftable'
2510f5c8  00000000 00000000 00000000 00000000
2510f5d8  00000000 00000000 00000000 00000000
2510f5e8  00000000 00000000 00000000 00000000
2510f5f8  00000000 00000000 00000000 00000000
2510f608  00000000 00000000 00000000 00000000
2510f618  00000000 00000000 00000000 00000000
2510f628  00000000 00000000 00000000 00000000
0:000> dd 4405250
04405250  00000001 0446a300 00000000 00000000
04405260  00000001 0446a320 00000000 00000000
04405270  00000001 0446a340 00000000 00000000
04405280  00000001 0446a360 00000000 00000000
04405290  a0524004 00000000 00000000 00000000
044052a0  b0524004 00000000 00000000 00000000
044052b0  c0524004 00000000 00000000 00000000
044052c0  d0524004 00000000 00000000 00000000
0:000> dd 0446a300
0446a300  00000001 42a04000 03e00000 605afe8e
0446a310  00000000 00000000 00000001 00000000
0446a320  00000001 3ea04000 03e00000 605afe8e
0446a330  00000000 00000000 00000001 00000000
0446a340  00000001 50804000 03c00000 605afe8e
0446a350  00000000 00000000 00000001 00000000
0446a360  00000001 67204000 03d50000 605afe8e
0446a370  00000000 00000000 00000001 00000000
0:000> dd 42a04000
42a04000  41414141 41414141 41414141 41414141
42a04010  41414141 41414141 41414141 41414141
42a04020  41414141 41414141 41414141 41414141
42a04030  41414141 41414141 41414141 41414141
42a04040  41414141 41414141 41414141 41414141
42a04050  41414141 41414141 41414141 41414141
42a04060  41414141 41414141 41414141 41414141
42a04070  41414141 41414141 41414141 41414141

我们在if命中处下断点,看程序某一时刻是否会执行到这里,命中之后单步调试,发现程序使用edx的值覆写esi指向的内存,通过edx可引用到原始ArrayBuffer。

0:000> p
eax=04469ea0 ebx=04c4b400 ecx=04405090 edx=04404570 esi=0022e838 edi=04c4b400
eip=61bd02fa esp=0022e820 ebp=0022e82c iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
chrome_child!WTF::ArrayBufferBuilder::ToArrayBuffer+0x22:
61bd02fa 8916            mov     dword ptr [esi],edx  ds:0023:0022e838=8aa66acd
0:000> dd esi
0022e838  8aa66acd 0022e83c 8aa66acd 255856d0
0022e848  0022e878 2510ee20 0022e868 612059da
0022e858  5e97584f 8aa66af5 255856d0 0022e878
0022e868  0022e898 60b50c4d 2510ee20 0022e878
0022e878  00000000 00000000 00000000 60b4d292
0022e888  8aa66a05 0344bf60 0344ced8 033e7328
0022e898  0022e8c0 5feb7239 0022e8a8 0022e8b8
0022e8a8  0022e8c8 0022e8dc 00000000 04295321
0:000> dd edx
04404570  00000001 04469ea0 00000000 00000000
04404580  00000006 00000000 00000001 00000007
04404590  00000002 00000000 00000000 00000000
044045a0  00000002 3f800000 ff000000 00000000
044045b0  00000002 00000000 00000000 00000000
044045c0  00000000 00000000 00000000 00000000
044045d0  044681e0 00000000 044046a0 00000000
044045e0  044681e0 25e01000 0001e000 00000000
0:000> dd 04469ea0
04469ea0  00000001 32404000 04c4b400 605afe8e
04469eb0  00000000 00000000 00000001 00000000
04469ec0  00000001 046101e0 00000000 00000000
04469ed0  25446ed0 00000049 00000000 00000000
04469ee0  033dacf8 41c00000 00000000 00000000
04469ef0  00000000 00000000 00000201 00000000
04469f00  00000001 00000000 00000000 04404fb0
04469f10  00000000 00000005 00000006 00000000
0:000> dd 32404000
32404000  41414141 41414141 41414141 41414141
32404010  41414141 41414141 41414141 41414141
32404020  41414141 41414141 41414141 41414141
32404030  41414141 41414141 41414141 41414141
32404040  41414141 41414141 41414141 41414141
32404050  41414141 41414141 41414141 41414141
32404060  41414141 41414141 41414141 41414141
32404070  41414141 41414141 41414141 41414141

后面单步调试,发现程序将该结构的引用加1,并将esi作为函数返回值,此时esi指针指向该结构体。这也证实了在完全加载的情况下,程序直接返回原缓冲区。如果可以获取两个以上对初始ArrayBuffer缓冲区的引用,释放其中一个,并试图通过其他引用访问,则会触发释放后重用。

0:000> 
eax=04469ea0 ebx=04c4b400 ecx=04405090 edx=04404570 esi=0022e838 edi=04c4b400
eip=61bd02fe esp=0022e820 ebp=0022e82c iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
chrome_child!WTF::ArrayBufferBuilder::ToArrayBuffer+0x26:
61bd02fe ff02            inc     dword ptr [edx]      ds:0023:04404570=00000001
0:000> 
eax=04469ea0 ebx=04c4b400 ecx=04405090 edx=04404570 esi=0022e838 edi=04c4b400
eip=61bd0300 esp=0022e820 ebp=0022e82c iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
chrome_child!WTF::ArrayBufferBuilder::ToArrayBuffer+0x28:
61bd0300 eb31            jmp     chrome_child!WTF::ArrayBufferBuilder::ToArrayBuffer+0x5b (61bd0333)
0:000> 
eax=04469ea0 ebx=04c4b400 ecx=04405090 edx=04404570 esi=0022e838 edi=04c4b400
eip=61bd0333 esp=0022e820 ebp=0022e82c iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
chrome_child!WTF::ArrayBufferBuilder::ToArrayBuffer+0x5b:
61bd0333 89f0            mov     eax,esi
0:000> dd edx
04404570  00000002 04469ea0 00000000 00000000
04404580  00000006 00000000 00000001 00000007
04404590  00000002 00000000 00000000 00000000
044045a0  00000002 3f800000 ff000000 00000000
044045b0  00000002 00000000 00000000 00000000
044045c0  00000000 00000000 00000000 00000000
044045d0  044681e0 00000000 044046a0 00000000
044045e0  044681e0 25e01000 0001e000 00000000
0:000> dd esi
0022e838  04404570 0022e83c 8aa66acd 255856d0
0022e848  0022e878 2510ee20 0022e868 612059da
0022e858  5e97584f 8aa66af5 255856d0 0022e878
0022e868  0022e898 60b50c4d 2510ee20 0022e878
0022e878  00000000 00000000 00000000 60b4d292
0022e888  8aa66a05 0344bf60 0344ced8 033e7328
0022e898  0022e8c0 5feb7239 0022e8a8 0022e8b8
0022e8a8  0022e8c8 0022e8dc 00000000 04295321

下面使用这篇文章中的POC进行调试,思路就是FileReader读取长度大的Blob对象,使其在progress阶段多次调用FileReaderLoader::ArrayBufferResult函数,总有一次会出现完全加载的情况,在完全加载之后又会回调onLoadEnd函数,再次调用FileReaderLoader::ArrayBufferResult函数,那么我们就得到原始ArrayBuffer的两个不同引用,然后再通过setUint32修改其中一个原始ArrayBuffer起始处的四字节,如果通过另一处引用发现这四字节改变了就证明存在漏洞,就继续后面的触发测试:

首先我们调试一下漏洞证明阶段,还是看FileReaderLoader::ArrayBufferResult函数,找到原始ArrayBuffer,然后下硬件断点:

0:000> g
Breakpoint 1 hit
eax=0406a7c0 ebx=05000000 ecx=04005410 edx=040054b0 esi=0022e888 edi=05000000
eip=5c8902f8 esp=0022e870 ebp=0022e87c iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
chrome_child!WTF::ArrayBufferBuilder::ToArrayBuffer+0x20:
5c8902f8 85d2            test    edx,edx
0:000> p
eax=0406a7c0 ebx=05000000 ecx=04005410 edx=040054b0 esi=0022e888 edi=05000000
eip=5c8902fa esp=0022e870 ebp=0022e87c iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
chrome_child!WTF::ArrayBufferBuilder::ToArrayBuffer+0x22:
5c8902fa 8916            mov     dword ptr [esi],edx  ds:0023:0022e888=c956a63e

0:000> dd edx
040054b0  00000001 0406a7c0 00000000 00000000
040054c0  30540004 0406a820 00000000 00000000
040054d0  10550004 0406a840 00000000 00000000
040054e0  00000001 0406a860 00000000 00000000
040054f0  00000001 0406a880 00000000 00000000
04005500  00000001 0406a8a0 00000000 00000000
04005510  c0540004 0406a8c0 00000000 00000000
04005520  30550004 00000000 00000000 00000000
0:000> dd 0406a7c0
0406a7c0  00000001 2f004000 05000000 5b26fe8e
0406a7d0  00000000 00000000 00000001 00000000
0406a7e0  20a70604 00000000 00000000 59ad3660
0406a7f0  00000000 00000000 00000000 00000000
0406a800  40a80604 62e04000 04d90000 5b26fe8e
0406a810  00000000 00000000 00000000 00000000
0406a820  e0a70604 34204000 04c00000 5b26fe8e
0406a830  00000000 00000000 00000000 00000000
0:000> dd 2f004000
2f004000  41414141 41414141 41414141 41414141
2f004010  41414141 41414141 41414141 41414141
2f004020  41414141 41414141 41414141 41414141
2f004030  41414141 41414141 41414141 41414141
2f004040  41414141 41414141 41414141 41414141
2f004050  41414141 41414141 41414141 41414141
2f004060  41414141 41414141 41414141 41414141
2f004070  41414141 41414141 41414141 41414141
ba r4 [2f004000]

程序断在Builtins_DataViewPrototypeSetUint32函数中,程序正逐字节将缓冲区前4字节赋值为0x61,对应了DataView(ab1).setUint32(0, flag, true)这句。

5ab95603 8b7df0          mov     edi,dword ptr [ebp-10h]
5ab95606 881407          mov     byte ptr [edi+eax],dl
5ab95609 8b55e8          mov     edx,dword ptr [ebp-18h]
5ab9560c 88540701        mov     byte ptr [edi+eax+1],dl
5ab95610 89f2            mov     edx,esi
5ab95612 88540702        mov     byte ptr [edi+eax+2],dl
5ab95616 89ca            mov     edx,ecx
5ab95618 88540703        mov     byte ptr [edi+eax+3],dl
5ab9561c 8b45ec          mov     eax,dword ptr [ebp-14h] ss:0023:0022e744=00000003

0:000> 
eax=00000000 ebx=032ba188 ecx=00000061 edx=00000061 esi=00000061 edi=2f004000
eip=5ab9561c esp=0022e730 ebp=0022e758 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
chrome_child!Builtins_DataViewPrototypeSetUint32+0x39c:
5ab9561c 8b45ec          mov     eax,dword ptr [ebp-14h] ss:0023:0022e744=00000003
0:000> dd ebp-18h l1
0022e740  00000061
0:000> dd 2f004000
2f004000  61616161 41414141 41414141 41414141
2f004010  41414141 41414141 41414141 41414141
2f004020  41414141 41414141 41414141 41414141
2f004030  41414141 41414141 41414141 41414141
2f004040  41414141 41414141 41414141 41414141
2f004050  41414141 41414141 41414141 41414141
2f004060  41414141 41414141 41414141 41414141
2f004070  41414141 41414141 41414141 41414141

然后程序又通过Builtins_DataViewPrototypeGetUint32函数获取缓冲区前4字节数据,根据POC可知,我们通过ab1的引用来设置缓冲区,然后通过ab2的引用来查看缓冲区是否改变,此时引用的缓冲区前四字节已经变成0x61616161,证明存在漏洞。

5ab91b03 0fb61406        movzx   edx,byte ptr [esi+eax]
5ab91b07 0fb67c0601      movzx   edi,byte ptr [esi+eax+1]   ds:0023:2f004001=61
5ab91b0c 8955f0          mov     dword ptr [ebp-10h],edx
5ab91b0f 0fb6540602      movzx   edx,byte ptr [esi+eax+2]
5ab91b14 0fb6440603      movzx   eax,byte ptr [esi+eax+3]

0:000> g
Breakpoint 2 hit
eax=00000000 ebx=032ba188 ecx=00000001 edx=00000061 esi=2f004000 edi=00000000
eip=5ab91b07 esp=0022e73c ebp=0022e75c iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
chrome_child!Builtins_DataViewPrototypeGetUint32+0x2c7:
5ab91b07 0fb67c0601      movzx   edi,byte ptr [esi+eax+1]   ds:0023:2f004001=61
0:000> dd esi+eax
2f004000  61616161 41414141 41414141 41414141
2f004010  41414141 41414141 41414141 41414141
2f004020  41414141 41414141 41414141 41414141
2f004030  41414141 41414141 41414141 41414141
2f004040  41414141 41414141 41414141 41414141
2f004050  41414141 41414141 41414141 41414141
2f004060  41414141 41414141 41414141 41414141
2f004070  41414141 41414141 41414141 41414141

0:000> 
eax=00000061 ebx=032ba188 ecx=00000001 edx=00000061 esi=2f004000 edi=00000061
eip=5ab91b39 esp=0022e73c ebp=0022e75c iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
chrome_child!Builtins_DataViewPrototypeGetUint32+0x2f9:
5ab91b39 c1e210          shl     edx,10h
0:000> 
eax=00000061 ebx=032ba188 ecx=00000001 edx=00610000 esi=2f004000 edi=00000061
eip=5ab91b3c esp=0022e73c ebp=0022e75c iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
chrome_child!Builtins_DataViewPrototypeGetUint32+0x2fc:
5ab91b3c c1e018          shl     eax,18h
0:000> 
eax=61000000 ebx=032ba188 ecx=00000001 edx=00610000 esi=2f004000 edi=00000061
eip=5ab91b3f esp=0022e73c ebp=0022e75c iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
chrome_child!Builtins_DataViewPrototypeGetUint32+0x2ff:
5ab91b3f c1e708          shl     edi,8
0:000> 
eax=61000000 ebx=032ba188 ecx=00000001 edx=00610000 esi=2f004000 edi=00006100
eip=5ab91b42 esp=0022e73c ebp=0022e75c iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
chrome_child!Builtins_DataViewPrototypeGetUint32+0x302:
5ab91b42 0bd0            or      edx,eax
0:000> 
eax=61000000 ebx=032ba188 ecx=00000001 edx=61610000 esi=2f004000 edi=00006100
eip=5ab91b44 esp=0022e73c ebp=0022e75c iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
chrome_child!Builtins_DataViewPrototypeGetUint32+0x304:
5ab91b44 0bfa            or      edi,edx
0:000> 
eax=61000000 ebx=032ba188 ecx=00000001 edx=61610000 esi=2f004000 edi=61616100
eip=5ab91b46 esp=0022e73c ebp=0022e75c iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
chrome_child!Builtins_DataViewPrototypeGetUint32+0x306:
5ab91b46 8b55f0          mov     edx,dword ptr [ebp-10h] ss:0023:0022e74c=00000061
0:000> 
eax=61000000 ebx=032ba188 ecx=00000001 edx=00000061 esi=2f004000 edi=61616100
eip=5ab91b49 esp=0022e73c ebp=0022e75c iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
chrome_child!Builtins_DataViewPrototypeGetUint32+0x309:
5ab91b49 0bd7            or      edx,edi
0:000> 
eax=61000000 ebx=032ba188 ecx=00000001 edx=61616161 esi=2f004000 edi=61616100
eip=5ab91b4b esp=0022e73c ebp=0022e75c iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
chrome_child!Builtins_DataViewPrototypeGetUint32+0x30b:
5ab91b4b 81faffffff3f    cmp     edx,3FFFFFFFh
0:000> ?edx
Evaluate expression: 1633771873 = 61616161

在 JavaScript 中,各线程之间通过 postMessage 实现数据的发送、通过 onmessage 回调函数实现消息的响应。线程之间的数据传递是通过复制(而不是共享)来实现的,如果要传递的对象实现了 Transferable 接口,那么可以实现数据的高效转移,即并不复制数据,而是通过直接转移所有权来实现传递。对于这种传递方式,因为直接转移了所有权,因此原有线程不再享有对象数据的访问权限。ArrayBuffer 就是以这样的方式转移的。

然后程序调用DedicatedWorker::postMessage函数最终释放这块缓冲区,当程序再次调用DataView(ab2).setUint32(4, 0x42424242, true)尝试访问已经释放的缓存区时,触发此漏洞。

0:000> g
Breakpoint 2 hit
eax=00000000 ebx=2f004000 ecx=00000000 edx=00000000 esi=5dc0938c edi=2f001020
eip=5b26fd96 esp=0022e1f8 ebp=0022e204 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
chrome_child!WTF::ArrayBufferContents::FreeMemory+0x5c:
5b26fd96 891f            mov     dword ptr [edi],ebx  ds:0023:2f001020=00000000
0:000> dps esp
0022e1f8  0406a7c0
0022e1fc  00000008
0022e200  04209540
0022e204  0022e21c
0022e208  5b26fe41 chrome_child!WTF::ArrayBufferContents::DataHolder::~DataHolder+0x3f [C:\b\c\b\win_clang\src\third_party\blink\renderer\platform\wtf\typed_arrays\array_buffer_contents.cc @ 147]
0022e20c  2f004000
0022e210  05000000
0022e214  00000000
0:000> kp
ChildEBP RetAddr  
0022e204 5b26fe41 chrome_child!WTF::ArrayBufferContents::FreeMemory(void * data = 0x2f004000, void * ptr = 0x2f004000, void * address = , class base::subtle::SpinLock * _Mtx = , int _Value = , std::memory_order _Order = , struct std::_Atomic_int * _Atom = , unsigned long * _Tgt = , unsigned int x = )+0x5c [C:\b\c\b\win_clang\src\third_party\blink\renderer\platform\wtf\typed_arrays\array_buffer_contents.cc @ 126]
0022e21c 5b26fa6a chrome_child!WTF::ArrayBufferContents::DataHolder::~DataHolder(int64 diff = 0n0)+0x3f [C:\b\c\b\win_clang\src\third_party\blink\renderer\platform\wtf\typed_arrays\array_buffer_contents.cc @ 147]
0022e228 5b965627 chrome_child!scoped_refptr::~scoped_refptr(class WTF::ArrayBufferContents::DataHolder * ptr = 0x0406a7c0, int _Value = , std::memory_order _Order = , struct std::_Atomic_int * _Atom = , unsigned long * _Tgt = , class WTF::ArrayBufferContents::DataHolder * x = , void * p = )+0x16 [C:\b\c\b\win_clang\src\base\memory\scoped_refptr.h @ 208]
0022e29c 5b96513a chrome_child!blink::SerializedScriptValue::TransferArrayBufferContents(class v8::Isolate * isolate = 0x032ba188, class blink::HeapVector,0> * array_buffers = 0x0022e2e4, class blink::ExceptionState * exception_state = 0x0022e59c, class WTF::ArrayBufferContents * buffer = , unsigned int capacity = , unsigned int size = , unsigned int new_min_capacity = , class WTF::ArrayBufferContents * begin = 0x04209540, class blink::Member * _First = , class blink::Member * _Last = , class WTF::String * string2 = 0x0022e230, class scoped_refptr * r = 0x0022e230, class WTF::StringImpl * p = , class WTF::StringImpl * ptr = , class WTF::StringAppend * string1 = , class blink::DOMArrayBufferBase ** value = 0x0022e230, class blink::DOMArrayBufferBase * key = 0x00000000, class blink::DOMArrayBufferBase ** b = , class blink::Member * a = , unsigned int i = , class WTF::Vector * other = 0x0022e230, class WTF::ArrayBufferContents * buffer_to_deallocate = )+0x487 [C:\b\c\b\win_clang\src\third_party\blink\renderer\bindings\core\v8\serialization\serialized_script_value.cc @ 678]
0022e2d0 598fc86b chrome_child!blink::SerializedScriptValue::TransferArrayBuffers(class v8::Isolate * isolate = 0x032ba188, class blink::HeapVector,0> * array_buffers = 0x0022e2e4, class blink::ExceptionState * exception_state = 0x0022e59c, class WTF::Vector * other = , class WTF::ArrayBufferContents * begin = , class WTF::ArrayBufferContents * buffer_to_deallocate = )+0x2a [C:\b\c\b\win_clang\src\third_party\blink\renderer\bindings\core\v8\serialization\serialized_script_value.cc @ 419]
0022e300 598f8a27 chrome_child!blink::V8ScriptValueSerializer::FinalizeTransfer(class blink::ExceptionState * exception_state = 0x0022e59c, class WTF::Vector,0,blink::HeapAllocator> * val = )+0x5b [C:\b\c\b\win_clang\src\third_party\blink\renderer\bindings\core\v8\serialization\v8_script_value_serializer.cc @ 157]
0022e354 598f85f3 chrome_child!blink::V8ScriptValueSerializer::Serialize(class v8::Local value = class v8::Local, class blink::ExceptionState * exception_state = 0x0022e59c, class blink::ExceptionState ** scoped_variable = , class blink::ExceptionState * new_value = , blink::SerializationTag tag = , class v8::Isolate * isolate = , class v8::PersistentBase * that = , class std::unique_ptr data = , unsigned int size = , class std::unique_ptr * _Right = , unsigned char * _Ptr = , unsigned char * buffer = , void * p = , void * ptr = , void * address = , class base::subtle::SpinLock * _Mtx = , int _Value = , std::memory_order _Order = , struct std::_Atomic_int * _Atom = , unsigned long * _Tgt = , unsigned int x = , class scoped_refptr * r = )+0xa7 [C:\b\c\b\win_clang\src\third_party\blink\renderer\bindings\core\v8\serialization\v8_script_value_serializer.cc @ 110]
0022e3b4 598f8593 chrome_child!blink::SerializedScriptValueForModulesFactory::Create(class v8::Isolate * isolate = 0x032ba188, class v8::Local value = class v8::Local, struct blink::SerializedScriptValue::SerializeOptions * options = 0x0022e400, class blink::ExceptionState * exception_state = 0x0022e59c, class blink::ScriptState * script_state = , char phase = , char * scope = , unsigned int64 id = , unsigned int flags = , unsigned int64 bind_id = , int thread_id = , class base::TimeTicks * timestamp = )+0x59 [C:\b\c\b\win_clang\src\third_party\blink\renderer\bindings\modules\v8\serialization\serialized_script_value_for_modules_factory.cc @ 23]
0022e3dc 598f84e5 chrome_child!blink::SerializedScriptValue::Serialize(class v8::Isolate * isolate = 0x032ba188, class v8::Local value = class v8::Local, struct blink::SerializedScriptValue::SerializeOptions * options = 0x0022e400, class blink::ExceptionState * exception = 0x0022e59c, unsigned int size = )+0x43 [C:\b\c\b\win_clang\src\third_party\blink\renderer\bindings\core\v8\serialization\serialized_script_value.cc @ 83]
0022e420 5bf71d08 chrome_child!blink::PostMessageHelper::SerializeMessageByMove(class v8::Isolate * isolate = 0x032ba188, class blink::ScriptValue * message = 0x0022e590, class blink::PostMessageOptions * options = 0x24eb2fa8, class blink::Transferables * transferables = 0x0022e470, class blink::ExceptionState * exception_state = 0x0022e59c, class blink::SerializedScriptValue * ptr = , int _Value = , std::memory_order _Order = , struct std::_Atomic_int * _Atom = , unsigned long * _Tgt = , class blink::SerializedScriptValue * x = , void * p = )+0x75 [C:\b\c\b\win_clang\src\third_party\blink\renderer\bindings\core\v8\serialization\post_message_helper.cc @ 30]
0022e530 5bf71c97 chrome_child!blink::DedicatedWorker::postMessage(class blink::ScriptState * script_state = 0x24eb23e0, class blink::ScriptValue * message = 0x0022e590, class blink::PostMessageOptions * options = 0x24eb2fa8, class blink::ExceptionState * exception_state = 0x0022e59c, class blink::SerializedScriptValue * p = , class blink::SerializedScriptValue * ptr = , int increment = , int _Value = , std::memory_order _Order = , struct std::_Atomic_int * _Atom = , unsigned long * _Tgt = , class scoped_refptr * r = , class blink::SerializedScriptValue ** _Right = , class blink::SerializedScriptValue * x = , class WTF::Vector * other = , struct WTF::VectorBufferBase::OffsetRange this_hole = , struct WTF::VectorBufferBase::OffsetRange other_hole = , class blink::MessagePortChannel * begin = , class blink::mojom::blink::UserActivationSnapshot * _Left = , unsigned int length = )+0x68 [C:\b\c\b\win_clang\src\third_party\blink\renderer\core\workers\dedicated_worker.cc @ 136]
0022e554 5b8f9b3f chrome_child!blink::DedicatedWorker::postMessage(class blink::ScriptState * script_state = 0x24eb23e0, class blink::ScriptValue * message = 0x0022e590, class WTF::Vector * transfer = 0x0022e584, class blink::ExceptionState * exception_state = 0x0022e59c, unsigned int size = , bool eagerly_sweep = )+0x47 [C:\b\c\b\win_clang\src\third_party\blink\renderer\core\workers\dedicated_worker.cc @ 126]
0022e5cc 5962833e chrome_child!blink::dedicated_worker_v8_internal::PostMessage1Method(class v8::FunctionCallbackInfo * info = , class v8::Isolate * isolate = , blink::ExceptionState::ContextType context_type = , class v8::Local object = , class v8::Local wrapper = , int index = , unsigned int obj = , unsigned int heap_object_ptr = , int offset = , class v8::Local context = class v8::Local, class v8::Local * that = , int i = , class blink::ScriptState * script_state = , class v8::Local value = , class blink::ScriptState * raw = , unsigned int size = , class v8::Local handle = , class v8::Value * val = , class blink::SharedPersistent * p = , class WTF::Vector * other = 0x0022e5cc, struct WTF::VectorBufferBase::OffsetRange this_hole = , struct WTF::VectorBufferBase::OffsetRange other_hole = , class blink::ScriptValue ** _Right = 0x0022e5cc, class blink::ScriptValue * begin = )+0x1ff [C:\b\c\b\win_clang\src\out\Release\gen\third_party\blink\renderer\bindings\core\v8\v8_worker.cc @ 148]
……
0:000> g
(d04.88c): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000004 ebx=032ba188 ecx=00000042 edx=00000042 esi=00000042 edi=2f004000
eip=5ab95606 esp=0022e6ec ebp=0022e714 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010202
chrome_child!Builtins_DataViewPrototypeSetUint32+0x386:
5ab95606 881407          mov     byte ptr [edi+eax],dl      ds:0023:2f004004=??
0:000> dd edi
2f004000  ???????? ???????? ???????? ????????
2f004010  ???????? ???????? ???????? ????????
2f004020  ???????? ???????? ???????? ????????
2f004030  ???????? ???????? ???????? ????????
2f004040  ???????? ???????? ???????? ????????
2f004050  ???????? ???????? ???????? ????????
2f004060  ???????? ???????? ???????? ????????
2f004070  ???????? ???????? ???????? ????????

你可能感兴趣的:(CVE-2019-5786漏洞分析)