Source: http://blog.skylined.nl/20161205001.html

Synopsis

A specially crafted web-page can trigger a memory corruption vulnerability in Microsoft Edge. I did not investigate this vulnerability thoroughly, so I cannot speculate on the potential impact or exploitability.

Known affected software and attack vectors

Microsoft Edge

An attacker would need to get a target user to open a specially crafted web-page. Disabling Java­Script does not prevent an attacker from triggering the vulnerable code path.

Discovery

This issue was found through fuzzing in the 64-bit version of Microsoft Edge, in which the original repro triggered what appeared to be a NULL pointer dereference in CBase­Scriptable::Private­Query­Interface. So, after a very brief look at the repro, I filed a bug in the public bug tracker and published it on twitter. The original repro was:

<body onload=typeof(open().crypto)>

Soon after, I found another repro that trigger a slightly different NULL pointer dereference in CBase­Scriptable::Private­Query­Interface in a 64-bit version of Edge. The second repro was:

<body onload=typeof(open().ms­Credentials)>

I never tested the these two repros in a 32-bit version of Edge before publishing them, which I immediately regretted after finding that the second repro triggered an access violation using the obviously non-NULL address 0x1BF37D8 in a 32-bit version of Edge!

Around this time, I started finding many variations of this bug: getting the type of various properties or objects associated with another window was triggering all kinds of access violations. Many of these were not using NULL pointers on 32-bit Edge. I collected all the variations my fuzzers had found and come up with these additional repros:

<body onload=typeof(open().document.create­Element("canvas").get­Context("2d"))>

This triggered an access violation in edgehtml.dll!CBase­Scriptable::Private­Query­Interface while attempting to read from address 0x4C261 in the 32-bit version of Edge.

<body onload=typeof(open().navigator.media­Devices)>

This triggered an access violation in charkra.dll!Thread­Context::Pre­Sweep­Callback while attempting to read from address 0x­FF80A90F in the 32-bit version of Edge.

<body onload=typeof(open().to­String)>

This triggered an assertion failure because it was calling a deprecated API in the 32-bit version of Edge.

I looked again at the original crypto repro and noticed that although it triggered an access violation using a NULL pointer on both 32-bit and 64-bit versions of Edge, the two addresses (3 and 8 respectively) had different alignment. This is rather odd: true NULL pointer dereferences can cause an access violation at a different offset from NULL on these two architectures because property values and pointers stored before the one being read/written can have different sizes on 32-bit and 64-bit systems, but one usually expects them to have similar alignment: the last two bits of the address should be the same.

Report

If only I had tested the original repro in a 32-bit version of Edge when I first analyzed the issue, I might have realized it was more than a simple NULL pointer and not published it before doing additional research.

I contacted ZDI and asked if they would be interested in buying the vulnerability at this point, given that I publicly released the repro that triggered a NULL pointer and filed it with Microsoft. I was hoping they would decide that this did not disclose the underlying vulnerability and that it as such would still be a 0-day. Unfortunately for me, they were not interested in acquiring details in this situation.

At that point I decided to contact the Microsoft Security Response Center and report the additional information I had found. I also contacted a few people working on the Edge team at Microsoft directly to let them know they might want to escalate this bug from a simple NULL pointer to a security vulnerability. Unfortunately, this let them to decided to mark the bug I had filed in the Edge bug tracker as hidden. I warned them that this did little good, as the details were still public in my twitter and even if I deleted that, in general what goes on the internet stays on the internet.

Analysis

Since I had publicly released the repro, I was not going to be seeing any kind of reward for this bug, so analyzing the issue was not a priority for me. Unfortunately that meant I did not analyze it at all, other than to speculate that this bug was likely to have been a type-confusion or bad cast, where assembled code was used as data, leading to most of these repros triggering an access violation at a static address that depended on the code they were using as data. It may therefore be possible to find a variation that uses code that represents an address in the address space of Edge where an attacker might store data under his/her control. This is especially true for 32-bit Edge, as the address space is a lot smaller. Depending on what the code does with the address, it might be possible to execute arbitrary code under perfect circumstances.

On Hiding bugs in public bug trackers

Hiding a publicly reported bug after the fact is a very bad idea IMHO, as it paints an easy to detect target on the bug. Every smart attacker should have a system that makes regular copies of all publicly reported bugs in target applications and reports to their owner all bugs that become hidden, with a copy of all the information it scraped from the bug before it was hidden. Since hiding a public bug only ever happens for one of two reasons: the bug was found to be a security issue, or the report accidentally contains personal information that the owner wants hidden. It should be quite easy to distinguish between the two to filter out the vulnerabilities, giving an attacker a nearly free stream of nearly 0-day bugs. If you work on a team that has a public bug-tracker, you may want to discuss this with your team and decided how to handle such situations.

Conclusion

As useful as Bug­Id is in automating a lot of the analysis I do on every bug I find, and in helping me prioritize the issues that are most likely to be vulnerabilities, it is not perfect and cannot always detect a vulnerability for what it is. Bug­Id is not a perfect replacement for full manual analysis of bugs.

In this case I relied to heavily on its ability to distinguish vulnerabilities from other bugs. Because of the nature of this issue, the repros caused access violations at static addresses, many of which near enough to NULL to be interpreted as NULL pointer dereferences, especially for the first repro I found. Bug­Id can not actually determine the root cause of a crash, but attempts to deduce the root cause based on the details of the crash it causes. In this case, the crash looked too similar to a regular NULL pointer dereference for Bug­Id to detect it as anything else.

However, in my current situation, where I am finding way more bugs than I can analyze manually, Bug­Id does a very good job at helping me prioritize and analyze issues. I have used Bug­Id on hundreds of bugs and, as far as I know, this is the first time I mistook a security vulnerability for a regular bug based on the Bug­Id report. As such, the false-negative rate I have experienced is a fraction of a percent, which IMHO is remarkably low and entirely acceptable. At the same time, the false-positive rate I have seen so far is exactly zero.

In order to prevent this from happening in the future, I now test each repro in both the 32-bit and 64-bit version of Edge, do more manual analysis on bugs that get reported as a NULL pointer with a non-DWORD-aligned address (e.g. 3 in this case), and wait slightly longer for my fuzzers to find variations of a bug before I start my analysis and report the issue as a non-security bug.

Time-line
29 April 2016: This vulnerability was first found through fuzzing.
10 May 2016: This issue was published on Twitter and reported to Microsoft.
13 May 2016: This vulnerability was submitted to ZDI.
18 May 2016: This vulnerability was declined by ZDI.
18 May 2016: This vulnerability was reported to MSRC and I informed Edge developers directly on the seriousness of the bug.
18 May 2016: The issue was hidden in public bug tracker.
14 June 2016: Microsoft addresses this vulnerability in MS16-068.
December 2016: Details of this vulnerability are released.