Internet Explorer Didn't Get The Memo.
by Mitja Kolsek, the 0patch Team
Five days ago, security researcher John Page published details and a proof-of-concept for a vulnerability in Internet Explorer that he had previously reported to Microsoft but received a response that "...a fix for this issue will be considered in a future version of this product or service."
In this article we will explain why we think Microsoft has underestimated the severity of this vulnerability, how one Microsoft product inadvertently sabotaged another Microsoft product's security, and what you can do to protect yourself while waiting for Microsoft to fix this bug.
The vulnerability is a classic: an XML External Entity ("XXE") attack can be mounted in Internet Explorer using an XML block inside a MHT file. As a result, a user opening such MHT file will have one or more of their local files sent to attacker's web server. Similar XXE vulnerabilities have been found in hundreds of products before, and exploited for exfiltrating local files.
The attack is nicely demonstrated in John's video, where you can see the user downloading an MHT file with Edge and then opening it with Internet Explorer - resulting in their system.ini file being sent to attacker's server.
But... What About The Mark-Of-The-Web?
After watching John's video we tried to reproduce the issue, and a Windows 7 machine was at hand. We downloaded the MHT file with Internet Explorer, then double-clicked it, and... nothing. Process Monitor showed that system.ini was in fact read, but it didn't get sent to the remote server. Then we created the same MHT file locally instead of downloading it, and the exploit worked.
This looked like a classic "mark-of-the-web" situation: when a file is obtained from the Internet, well-behaved Windows applications like web browsers and email clients add a mark to such file in form of an alternate data stream named Zone.Identifier, containing a line ZoneId=3. This allows other applications to know that the file has come from an untrusted source - and should thus be opened in a sandbox or an otherwise limited environment.
Indeed, Internet Explorer does put a mark-of-the-web on the downloaded MHT file, and when rendering that file, notices said mark and decides not to make the request to the remote server. Deleting the mark from the file effectively turns the file into a "trusted" file and the exploit works.
Okay, this is all good and well, but why does the exploit work with a downloaded file on John's video?
To answer that, we moved our analysis over to Windows 10 in order to replicate John's demo more closely. We downloaded the MHT file, this time with Edge, and opened it locally with Internet Explorer: Surprise! The exploit worked, just like in the demo!
But why? Does Edge not put the mark-of-the-web on downloaded files, or does it do it differently and somehow confuses Internet Explorer? That would be a serious flaw.
It was time for some differential analysis. We had two MHT files downloaded from the same location; one downloaded with Internet Explorer (msie-xss-0day-1.mht) and the other with Edge (msie-xss-0day-2.mht). Same content, when opened with an editor, but slightly different Zone.Identifier data streams:
It turned out Edge does, unsurprisingly, put a mark-of-the-web on the file - but apparently stores some additional data there compared to Internet Explorer. Could this additional data somehow confuse Internet Explorer? It was easy to check; we copied the content of the Zone.Identifier stream from file #1 to file #2 and saved it.
Result: no difference; file #2 was still able to launch the exploit.
So we had two identical files with identical data streams, and one of them executed the exploit while the other one didn't. After a bit of frustration, mixed with wild fantasies of Internet Explorer somehow remembering its downloaded files and tracking them on the computer, our Twitter buddy Eric Lawrence proposed checking the permissions on these files.
That was a silly proposal, of course, as obviously they would have identical permissions, inherited from the Downloads folder they were stored in. Obviously.
|Permissions on the file downloaded|
with Internet Explorer
|Permissions on the file downloaded|
Strange. Edge seemed to have added two entries to the downloaded file's AC, both for some SIDs that Windows can't or won't translate to a friendly name:
To see whether these ACL entries affected the exploit's execution in Internet Explorer, we decided to delete them one by one and retry the exploit. It turned out that removing the second one, SID S-1-15-2-*, resulted in exploit not working anymore. How weird: what looks like Read access permission (see the (R) above) for some unknown user account prevents the exploit from working.
Not finding anything useful about this SID on the Internet (although the AppContainer SID looks related)*, we turned to Process Monitor hoping to see some interesting differences between the execution of both files. And differences we have found, the most obvious being that Internet Explorer got a lot of ACCESS DENIED's on Edge-downloaded MHT file (the one where exploit was working), while it got none on Internet Explorer-downloaded MHT file.
|Low Integrity iexplore.exe process gets ACCESS DENIED errors on opening the Zone.Identifier stream|
Remember that Internet Explorer works with multiple iexplore.exe processes, some running with Medium Integrity and some with Low Integrity (i.e. in a sandbox). Low Integrity processes are not allowed to write or change files with higher integrity even if user account they're running as otherwise has permissions to do that. They are allowed to read files with higher integrity though.
All the ACCESS DENIED's were happening to Low Integrity iexplore.exe processes on read access, and that was clearly caused by the mysterious S-1-15-2-* SID we had found above because removing that ACL entry from file's permissions also removed all ACCESS DENIED's.
It became clear that we have stumbled upon an undocumented Windows 10 feature, a flag that can be set on a file to prevent Low Integrity processes from even reading its content or its attributes. We theorize that Edge is using this feature to further tighten the security of saved files against malicious code executing in its Low Integrity sandbox. Nothing wrong with that.
But why does this flag help the exploit to execute in Internet Explorer? We looked at some of the ACCESS DENIED events and noticed that two of them occurred on attempting to read MHT file's Zone.Identifier data stream. Remember the mark-of-the-web discussed above? It's stored in this data stream - and Internet Explorer was unable to read it. What if failing to read it made Internet Explorer assume that there is no mark-of-the-web on the file (which is true for all locally created files), resulting in treating it as a "trusted" file?
It turned out that's exactly what happened. In order to understand why, we need to dive into the code. The stack trace on one of the ACCESS DENIED events includes a call to a function with an extremely interesting name: GetZoneFromAlternateDataStreamEx.
|The ACCESS DENIED event includes a call to GetZoneFromAlternateDataStreamEx|
A quick look at the function in IDA, combined with observing its calls in WinDbg, tells us it takes two arguments: (1) path to a file, and (2) pointer to the Zone Id value. It tries to read the file's Zone.Identifier stream and parse the ZoneId value from it, storing it to the provided address if found. Its return value is the error code, typically 0 if the stream was found and read, or 80070002 ("File not found") if the stream doesn't exist.
GetZoneFromAlternateDataStreamEx resides in urlmon.dll and is only called from one place. That call, however, is not followed by any checking of the error code returned by the function. The calling code simply assumes that if GetZoneFromAlternateDataStreamEx can't read the Zone Id from the file for whatever reason, the file must be "trusted". This logic was probably correct until the new feature we had discovered above got introduced.
Namely, when the MHT file permissions include the mysterious S-1-15-2-* SID, GetZoneFromAlternateDataStreamEx gets an ACCESS DENIED on attempting to read the file's Zone.Identifier stream, stores no Zone Id, and returns the error code 80070005 ("Access denied"). The calling code, not caring about the error, understands this as the file not having a mark-of-the-web, subsequently allowing it to make a request to attacker's server.
See the irony here? An undocumented security feature used by Edge neutralized an existing, undoubtedly much more important feature (mark-of-the-web) in Internet Explorer.
This is clearly a significant security issue, especially since the attack can be further improved from what was originally demonstrated. We have found that:
- the malicious MHT file doesn't have to be downloaded and manually opened by the user - just opening it directly from Edge can be made to work as well;
- the exploit can be enhanced so that it works more silently, and extracts many local files using a single MHT file.
On the upside, only Edge users are at risk. No other leading web browsers and email clients we've tested are using the undocumented security flag on the downloaded files, which effectively blocks the exploit.
While we believe Microsoft will update their original severity assessment of this issue and provide a fix for it, we wanted to give our users a micropatch to allow them to protect themselves. Namely, published 0days often start getting exploited, especially when no vendor fix is available.
Let's look at the code that calls GetZoneFromAlternateDataStreamEx and ignores the error returned by it.
|The code calling GetZoneFromAlternateDataStreamEx|
Fixing this seems pretty straight-forward: we need to add some error checking immediately after the call to GetZoneFromAlternateDataStreamEx to detect if the error was 80070005 ("Access denied"), and if so, set Zone Id to 3 ("untrusted"). This will effectively make Internet Explorer aware of Edge's security feature. Such is the source code of our micropatch for Windows 10 version 1803:
Here's video if you'd like to see how the micropatch affects the exploit.
As always, if you have 0patch Agent installed and registered, this micropatch is already on your computer - and applied to urlmon.dll in Internet Explorer and other processes loading it. "Why not just Internet Explorer?" you might ask. Well, while we now know that Internet Explorer is vulnerable, there are other products using urlmon.dll, and in case some of them happen to be using its (flawed) logic we'll automatically fix them as well.
If you don't have the 0patch Agent yet, you can register a 0patch account and install it to get this micropatch applied.
Following our guidelines on which patches to provide for free, this micropatch affects many home and education users, and is therefore included in both FREE and PRO 0patch license until Microsoft provides an official fix. After that the micropatch will only be included in the PRO license.
We are currently providing this micropatch for fully updated:
- Windows 10 version 1803
- Windows 10 version 1809
- Windows 10 version 1709 [added on 4/18/2019]
* [Update 4/18/2019] James Forshaw of Google Project Zero has subsequently noted the mysterious undocumented SIDs are "capability and group SIDs for the Microsoft.MicrosoftEdge_8wekyb3d8bbwe package." We trust James so let's put it here as some day this will help someone researching a similar issue. You're welcome ;)
[Update 4/23/2019] User itman on a Wilders Security Forums thread about this issue has provided a lot of useful additional information on said SIDs in multiple posts that are well worth reading.We generally agree with itman on everything stated there (including our then "clueles[ness] to the fact that Edge in essence always operates in equivalent IE11 EPM mode,"), except on the risk introduced by modifying "code that is loaded by multiple Win system processes". While urlmon.dll is in fact being loaded by many processes, not all are using it for determining the Zone identifier (the DLL has many other exported functions). For those that are, and are faced with the same situation of being unable to read the Zone.Identifier stream due to running with Low Integrity or in a different AppContainer, we believe our added code that checks for errors has a net positive effect as it prevents such apps from overly trusting downloaded files.