Exploit-DB #44096
Description: Microsoft Edge - 'UnmapViewOfFile' ACG Bypass
Type: dos
Platform: windows
Date: 2018-02-16
Author: Google Security Research

Exploit:
Background:

To implement ACG (https://blogs.windows.com/msedgedev/2017/02/23/mitigating-arbitrary-native-code-execution/#VM4y5oTSGCRde3sk.97), Edge uses a separate process for JIT compiling. This JIT Process is also responsible for mapping native code into the requesting Content Process.

In order to be able to write JITted (executable) data into the Content Process, JIT Process does the following:

1. It creates a shared memory object using CreateFileMapping()
2. It maps it into Content Process as PAGE_EXECUTE_READ and in the JIT proces as PAGE_READWRITE using MapViewOfFile2(). At this point the memory is reserved, but not yet committed.
3. When individual pages need to be written to they are first allocated from the region in step 2 using VirtualAllocEx(). This also marks the memory as committed.

The issue:

If a content process is compromised and the content process can predict on which address JIT process is going to call VirtualAllocEx() next (note: it is fairly predictable), content process can:

1. Unmap the shared memory mapped above above using UnmapViewOfFile()
2. Allocate a writable memory region on the same address JIT server is going to write and write an soon-to-be-executable payload there.
3. When JIT process calls VirtualAllocEx(), even though the memory is already allocated, the call is going to succeed and the memory protection is going to be set to PAGE_EXECUTE_READ. 

Note #1: The content written in step 2 is going to survive the memory protection change.
Note #2: JIT server is going to write the JITted payload into its own "side" of the shared memory, so the content in the Content Process is not going to get immediately overwritten.

See the debug log below for a demonstration.

Debug log:

Let's attach one instance of WinDBG to JIT process and another to a Content Process.

Let's also verify that ACG is indeed applied for the Content Process. We can do this using Get-ProcessMitigation PowerShell command. See the output in the screenshot (note the "BlockDynamicCode: ON" field).

Now, in JIT Process, let's set a breakpoint on VirtualAllocEx() and wait.

0:020> bp kernelbase!virtualallocex
0:020> g

Soon the breakpoint is hit.

Breakpoint 0 hit
KERNELBASE!VirtualAllocEx:
00007fff`5590e170 4883ec38        sub     rsp,38h

We can examine the call stack to see where we are - we see we are in the Encode phase of ServerRemoteCodeGen() which is a function that Content Process calls on the JIT server when it wants to JIT a function or a loop body.

0:011> k
 # Child-SP          RetAddr           Call Site
00 000000c2`48cfcfe8 00007fff`4dff3104 KERNELBASE!VirtualAllocEx
01 000000c2`48cfcff0 00007fff`38752dcd EShims!NS_ACGLockdownTelemetry::APIHook_VirtualAllocEx+0x14
02 000000c2`48cfd030 00007fff`38752a16 chakra!Memory::PreReservedSectionAllocWrapper::Alloc+0xd5
03 000000c2`48cfd0b0 00007fff`3875233e chakra!Memory::PageSegmentBase<Memory::PreReservedSectionAllocWrapper>::AllocDecommitPages<BVStatic<272>,1>+0xea
04 000000c2`48cfd150 00007fff`38752464 chakra!Memory::PageAllocatorBase<Memory::PreReservedSectionAllocWrapper,Memory::SegmentBase<Memory::PreReservedSectionAllocWrapper>,Memory::PageSegmentBase<Memory::PreReservedSectionAllocWrapper> >::TryAllocDecommittedPages<1>+0x8e
05 000000c2`48cfd210 00007fff`38751e7a chakra!Memory::PageAllocatorBase<Memory::PreReservedSectionAllocWrapper,Memory::SegmentBase<Memory::PreReservedSectionAllocWrapper>,Memory::PageSegmentBase<Memory::PreReservedSectionAllocWrapper> >::SnailAllocPages<1>+0x4c
06 000000c2`48cfd2d0 00007fff`38751488 chakra!Memory::CustomHeap::CodePageAllocators<Memory::SectionAllocWrapper,Memory::PreReservedSectionAllocWrapper>::AllocPages+0x72
07 000000c2`48cfd340 00007fff`38751210 chakra!Memory::CustomHeap::Heap<Memory::SectionAllocWrapper,Memory::PreReservedSectionAllocWrapper>::AllocNewPage+0x68
08 000000c2`48cfd3c0 00007fff`38750e14 chakra!Memory::CustomHeap::Heap<Memory::SectionAllocWrapper,Memory::PreReservedSectionAllocWrapper>::Alloc+0x9c
09 000000c2`48cfd470 00007fff`38750cae chakra!EmitBufferManager<Memory::SectionAllocWrapper,Memory::PreReservedSectionAllocWrapper,CriticalSection>::NewAllocation+0x58
0a 000000c2`48cfd500 00007fff`388599dc chakra!JITOutput::RecordOOPNativeCodeSize+0x8e
0b 000000c2`48cfd590 00007fff`388a5506 chakra!Encoder::Encode+0x9dc
0c 000000c2`48cfd710 00007fff`389904e5 chakra!Func::TryCodegen+0x356
0d 000000c2`48cfdfb0 00007fff`3877c00e chakra!Func::Codegen+0xed
0e 000000c2`48cfe3e0 00007fff`3877be54 chakra!<lambda_869fb2da08ff617a0f58153cb1331989>::operator()+0x166
0f 000000c2`48cfe500 00007fff`3877bde2 chakra!ServerCallWrapper<<lambda_869fb2da08ff617a0f58153cb1331989> >+0x54
10 000000c2`48cfe550 00007fff`3877bd85 chakra!ServerCallWrapper<<lambda_869fb2da08ff617a0f58153cb1331989> >+0x4e
11 000000c2`48cfe5c0 00007fff`57006d13 chakra!ServerRemoteCodeGen+0x75
12 000000c2`48cfe630 00007fff`57069390 RPCRT4!Invoke+0x73
13 000000c2`48cfe690 00007fff`56f93718 RPCRT4!Ndr64StubWorker+0xbb0
14 000000c2`48cfed40 00007fff`56fb73b4 RPCRT4!NdrServerCallNdr64+0x38
15 000000c2`48cfed90 00007fff`56fb654e RPCRT4!DispatchToStubInCNoAvrf+0x24
16 000000c2`48cfede0 00007fff`56fb6f84 RPCRT4!RPC_INTERFACE::DispatchToStubWorker+0x1be
17 000000c2`48cfeeb0 00007fff`56fc0693 RPCRT4!RPC_INTERFACE::DispatchToStubWithObject+0x154
18 000000c2`48cfef50 00007fff`56fc1396 RPCRT4!LRPC_SCALL::DispatchRequest+0x183
19 000000c2`48cff030 00007fff`56fbd11e RPCRT4!LRPC_SCALL::HandleRequest+0x996
1a 000000c2`48cff140 00007fff`56fbe843 RPCRT4!LRPC_ADDRESS::HandleRequest+0x34e
1b 000000c2`48cff1f0 00007fff`56fecc58 RPCRT4!LRPC_ADDRESS::ProcessIO+0x8a3
1c 000000c2`48cff330 00007fff`594665ae RPCRT4!LrpcIoComplete+0xd8
1d 000000c2`48cff3d0 00007fff`594aeed9 ntdll!TppAlpcpExecuteCallback+0x22e
1e 000000c2`48cff450 00007fff`5946471c ntdll!TppDirectExecuteCallback+0xb9
1f 000000c2`48cff4c0 00007fff`57ea1fe4 ntdll!TppWorkerThread+0x47c
20 000000c2`48cff850 00007f