Hello people, it's nice to step out for a moment and see the daylight. Those of you following us have noticed our near radio silence in the past months. To stop you from worrying I'd like to just quickly update you all on what's going on inside our walls (spoiler: a lot) and what our plans are (spoiler: big).
As you know, Windows 7 and Windows Server 2008 R2 are reaching their end-of-support on January 14 next year, which is causing a lot of headaches for people and companies who are entirely happy with the way these OSs work, or have compatibility requirements preventing them from upgrading. If they want to keep receiving security fixes, their options are: (a) to upgrade to Windows 10 and a newer server version, or (b) to buy Extended Security Updates from Microsoft (conditions apply, and 3rd party patch management solutions cannot be used for delivery).
NetMarketShare shows that last month, 5 months before end-of-support, 30% of desktop OSs were running Windows 7. With the current upgrading trend, we can safely forecast that the number of Windows 7 machines on February 11, 2020 (the first Patch Tuesday to exclude them) will be approximately somewhere between huge and vast. There are few public stats on how Windows Server 2008 R2 is doing but judging from what our users are saying, it's not going to get extinct anytime soon.
You see where we're going with this. Back in January 2018, we at 0patch "security-adopted" Microsoft Office Equation Editor, a program integrated into Microsoft Word with which scientists, teachers and students had written millions of equations. They were all suddenly left without a way to edit their equations when Microsoft decided to delete Equation Editor from their computers.
What we did was create micropatches for all known Equation Editor vulnerabilities and made instructions for users to bring back Equation Editor on their computers so they could continue using it while keeping Office regularly updated with Microsoft's security patches.
But that was just a trial run for becoming an adoptive parent of a vendor-abandoned product. This time we're going bigger: we're going to security-adopt Windows 7 and Windows Server 2008 R2 for those of you who want to keep them patched after their official security updates have dried out.
What does this mean, exactly?
It means that after the last official security update has been issued for Windows 7 and Windows Server 2008 R2 in January 2020, we'll start doing the following:
Each Patch Tuesday we'll review Microsoft's security advisories to determine which of the vulnerabilities they have fixed for supported Windows versions might apply to Windows 7 or Windows Server 2008 R2 and present a high-enough risk to warrant micropatching.
For the identified high-risk vulnerabilities we'll inspect Windows Updates for supported Windows versions (e.g., Windows 10) to confirm whether the vulnerable code that was fixed in Windows 10 is actually present on Windows 7 or Windows Server 2008 R2. (For all intents and purposes, such vulnerabilities will be considered 0days for these OSs.)
If the high-risk vulnerable code is found to be present on Windows 7 or Windows Server 2008 R2, we'll start a process of obtaining a proof-of-concept (POC) for triggering the vulnerability. Sometimes a POC is published by security researchers soon after the official vendor fix is out (and sometimes even before); other times we can get one from our partner network or threat intelligence sources; occasionally researchers share a POC with us privately; and sometimes we have to create a POC ourselves by analyzing the official patch and working our way out towards the input data that steers the execution to the vulnerability.
Once we have a POC and know how the vulnerability was fixed by the people who know the vulnerable code best (i.e., Microsoft developers), we'll port their fix, functionally speaking, as a series of micropatches to the vulnerable code in Windows 7 and Windows Server 2008 R2, and test them against the POC. After additional side-effect testing we'll publish the micropatches and have them delivered to users' online machines within 60 minutes. (Which by the way means that many Windows 7 and Windows Server 2008 R2 will be patched sooner than those with still-supported Windows versions where organizations will continue to prudently test Windows updates for days or weeks before having them applied to all computers.)
Okay - but what are we so busy with now? A lot of things:
Firstly, in order for large organizations to be able to use 0patch efficiently, we're developing a central management service (think WSUS for 0patch, but nice and fast) which will allow admins to organize computers in groups and apply different policies to these groups. Admins will thus be able to set up "testing" groups where new micropatches will be applied immediately, and subsequently have them applied to the rest of their computers with a few clicks (and of course, without users ever noticing anything). Naturally they'll also be able to un-apply any micropatches just as easily and quickly should they choose to. There will be alerts, graphs, reports, and drill-downs, and the very next step will be an on-premises version of 0patch server which so many organizations are asking for.
Secondly, we're growing our team: things are buzzing in our 0patch bootcamp and a nice side effect of passing one's knowledge onto others is that one has to neatly organize and document it. Consequently, adding further new members to the team afterwards will be even smoother and quicker.
Lastly, we're enhancing our reversing, patch analysis, vulnerability analysis, micropatch development and micropatch porting processes with new tools and techniques. Suffice to say that we've never had as many disassemblers, debuggers, decompilers, plugins and concurrently opened reversing projects running as we have now. But the thing I'm personally most excited about is our introduction of symbolic execution in micropatch creation, verification and porting processes. We've been aiming for eventual formal verification of our micropatches since the beginning and we're finally working on that. But not only that: symbolic execution and emulation will help us avoid errors sooner during micropatch development and allow us to perform unit testing against micropatched code even before we have a POC. Goosebumps!
This concludes our news from the 0patch lab. If you're interested in getting early access to 0patch central management (in November), or have any questions about our service, please consult Frequently Asked Questions or send an email to sales@0patch.com.
Backward Compatibility is Hard, and so is Stacked Impersonation
by Simon Raner and Mitja Kolsek, the 0patch Team
[Update 6/12/2019: Yesterday's Windows Updates include a fix for this vulnerability, 12 days after our micropatch has been released. The issue was assigned CVE-2019-1069.]
Last August we issued a micropatch for a local privilege escalation 0day in Task Scheduler, published by SandboxEscaper. The vulnerability allowed a local attacker on a Windows machine to change permissions of any chosen file, including system executables, such that the attacker would subsequently be able to modify that file. This obviously allowed for privilege escalation, although many system files can't be changed even with suitable permissions either due to being owned by TrustedInstaller or due to being in use. Nevertheless, at least one such file can always be found.
Fast forward to last week. SandboxEscaper has dropped three Windows 0days, one of which is again a local privilege escalation in Task Scheduler. We tested it and it worked on a fully patched Windows 10 machine. According to Will Dormann of CERT/CC, the exploit "functions reliably on 32- and 64-bit Windows 10 platforms, as well as
Windows Server 2016 and Windows Server 2019. While Windows 8 still
contains this vulnerability, exploitation using the publicly-described
technique is limited to files where the current user has write access,
in our testing. As such, the impact on Windows 8 systems using the
technique used by the public exploit appears to be negligible. We have
not been able to demonstrate the vulnerability on Windows 7 systems."
Analysis
Analysis always starts with reproducing the POC. It comes as a Windows executable that takes two arguments, username and password of a local low-privileged user. Let's see what it does when we run it as a low-privileged user test:
C:\Temp\Vuln-5172_bearlpe\Exploit>polarbear.exe test test SUCCESS: The parameters of scheduled task "bear" have been changed. SUCCESS: The parameters of scheduled task "bear" have been changed.
C:\Temp\Vuln-5172_bearlpe\Exploit>icacls "c:\Windows\system32\drivers\pci.sys" c:\Windows\system32\drivers\pci.sys NT AUTHORITY\SYSTEM:(Rc,S,X,RA) 0P-WIN-10-ENT-3\test:(R) BUILTIN\Administrators:(I)(R,W,D,WDAC,WO) NT AUTHORITY\SYSTEM:(I)(R,W,D,WDAC,WO) 0P-WIN-10-ENT-3\test:(I)(F)
Obviously, the POC was able to change permissions on pci.sys. Furthermore, in contrast to the last year's Task Scheduler 0day we had micropatched, this one also changed the ownership of the target file; not being owned by TrustedInstaller any more, pci.sys could be modified freely by the attacker.
Its operation is fairly simple; when launched with credentials of a low-privileged user test with password test, the POC performs these steps (as seen from its source code):
Copy file bear.job to c:\windows\tasks\bear.job
Execute schtasks.exe /change /TN \"bear\" /RU test /RP test (This instructs Task Scheduler to take bear.job created above and create a new scheduled tasks - resulting in a new file c:\windows\system32\tasks\Bear. Note that a legacy schtasks.exe from Windows XP is used, which uses legacy RPC interface for that.)
Delete c:\windows\system32\tasks\Bear.
Create a hard linkc:\windows\system32\tasks\Bear, pointing to system file c:\windows\system32\drivers\pci.sys.
Again, execute schtasks.exe /change /TN \"bear\" /RU test /RP test (This time, since the task already exists, Task Scheduler sets full permissions and ownership for user test on the task file. Since the task file is actually a hard link to pci.sys, it apparently changes permissions and ownership on that file.)
Observing operations against c:\windows\system32\tasks\Bear with Process Monitor during POC execution told us more:
Apparently, there were two SetSecurityFile operations performed on the file, with the following call stacks:
Both of these SetSecurityFile operations stem from function _SchRpcSetSecurity in schedsvc.dll, and based on our prior experience with Task Manager's impersonation issues we assumed this function was responsible for calling SetSecurityInfo without proper impersonation. Next step: debugger.
We set a breakpoint at _SchRpcSetSecurity and traced its execution towards the call to SetSecurityInfo - its first call being made from function SetJobFileSecurityByName. Therein, before the call to SetSecurityInfo was made, we checked the thread's access token, expecting it to be not-impersonated.
But surprise! The token was impersonated. Only the user it was impersonating was not the attacker's user test, but Local System (S-1-5-18). What was going on?
Was function _SchRpcSetSecurity broken and incorrectly impersonated the caller? We found an impersonation call in it and it looked okay. Clearly we needed to understand this function better, and it's natural to start with the documentation when available. The specification of function _SchRpcSetSecurity describes its behavior in detail, including this step that is relevant for our analysis (the path parameter being the Bear file in our case.):
This makes sense: if someone asks Task Scheduler to change permissions on a task file, said someone should have write permissions on that file. A typical use case for this is when the user who created a task subsequently decides to have that task executed as some other user, which requires that user to have at least read access to the task file. And this is also the use case triggered by the schtasks.exe's /change option, where /RU and /RP parameters specify the "run-as" user's credentials.
We then reverse engineered _SchRpcSetSecurity to find where this security check is implemented and find out why it doesn't work as specified.
Except we found that it does work as specified: the code attempts to open the Bear file with permissions to change its DACL and its owner - and if that succeeds, actually does that. Which would work great if only it was impersonating the low-privileged attacker instead of Local System (who obviously can do all that on the linked-to pci.sys file).
So why didn't the function impersonate the attacker? After some head-scratching, we remembered that this attack only works with the legacy schtasks.exe, and not with the new one. Could it be that the old schtasks.exe was calling some other RPC function than _SchRpcSetSecurity, which then in turn called _SchRpcSetSecurity via RPC? While still paused inside the _SchRpcSetSecurity call, we looked at other threads in the same process - and found an interesting one with this call stack:
Hmm, a thread in taskcomp.dll, which was itself triggered via an RPC call (as suggested by RPCRT4!Invoke) called a function named SchRpcSetSecurity, which invoked another RPC call (as suggested by RPCRT4!NdrClientCall2), and was now waiting for it to return. A few debugging sessions later, we could confirm that this is indeed what is happening: the legacy schtasks.exe makes a RPC call to a legacy RPC endpoint SASetAccountInformation implemented in taskcomp.dll, which implements the old task scheduler instructions with RPC calls to the new ones implemented in schedsvc.dll, such as SchRpcRegisterTask and SchRpcSetSecurity.
Our focus thus turned to taskcomp.dll. Namely, RPC calls can be stacked: process A can RPC-call process B, and then the code processing said call in process B can further RPC-call process C. In our case, schtasks.exe (running as attacker) calls RPC endpoint taskcomp!SASetAccountInformation in Task Scheduler's process svchost.exe (running as Local System), which in turn calls RPC endpoint schedsvc!_SchRpcSetSecurity in the same svchost.exe (still running as Local System). When the latter impersonates its caller, it actually impersonates the access token of the thread in taskcomp.dll that called it, and if that thread had previously impersonated its own caller (i.e., attacker), the final impersonated token would also be attacker's. However, taskcomp.dll does not impersonate its caller; it impersonates self (Local System) to enable the SeRestorePrivilege privilege that is needed for it to set DACL and ownership on any file:
This impersonation breaks the tie with attacker's identity, and causes the subsequently executed schedsvc!_SchRpcSetSecurity to believe it was Local System, not the attacker, who requested the change of DACL and owner on pci.sys. It was time to patch.
Patching
Correcting the behavior of someone else's code in a complex environment is always tricky, and legacy support + task scheduling = complex, we believe it was actually an error to impersonate self in taskcomp.dll instead of impersonating the client. The latter would in fact allow the security check in schedsvc!_SchRpcSetSecurity to perform correctly and work as intended on a regular file as well as on a hard-linked system file (correctly failing when invoked by a low-privileged user).
We therefore decided to replace self-impersonation with client-impersonation, and to do that, we removed the call to ImpersonateSalfWithPrivilege and injected a call to RpcImpersonateClient in its place.
We wrote a micropatch for this and tested it.
The POC still worked.
It turned out that there was another RPC call to SchRpcSetSecurity in taskcomp.dll, which got called when the first one was unsuccessful:
It looked like some monitoring thread was used for getting the job done when the original call failed, but this thread was not called via RPC, and client impersonation could not be used there. We therefore decided on a more drastic approach and simply amputated the call to SetSecurity.
After that, we got the desired behavior: The legacy schtasks.exe was behaving correctly when creating a new task from a job file, and when setting a "run-as" user for an existing task that the user was allowed to change permissions on. On the other hand, the hard link trick no longer worked because the Task Scheduler process correctly identified the caller and determined that it doesn't have sufficient permissions to change DACL or ownership on a system file. Since we didn't even touch schedsvc.dll, the new (non-legacy) Task Scheduler functionality was not affected at all.
With our micropatch in place, re-launching the POC and observing the Bear task file in Process Monitor only showed two CreateFile operations from SchRpcSetSecurity's security check described above, and both ended with an ACCESS DENIED error due to correct impersonation.
This is the source code of our micropatch for 32bit Windows 10 version 1809:
;Micropatch for taskcomp.dll version 10.0.17763.1 MODULE_PATH "..\AffectedModules\taskcomp.dll_10.0.17763.1_x86\taskcomp.dll" PATCH_ID 374 PATCH_FORMAT_VER 2 VULN_ID 5172 PLATFORM win32
patchlet_start PATCHLET_ID 1 PATCHLET_TYPE 2 PATCHLET_OFFSET 0x000184dd PIT rpcrt4.dll!RpcImpersonateClient JUMPOVERBYTES 16 ; we skip the call to ImpersonateSelfWithPrivilege N_ORIGINALBYTES 1
code_start mov dword [ebp-0b20h], 0 ; token (set to 0 to force the ImpersonateSelfWithPrivilege ; destructor to call RpcRevertToSelf) push 0 ; Impersonating the client that made the request call PIT_RpcImpersonateClient code_end
patchlet_end
patchlet_start PATCHLET_ID 2 PATCHLET_TYPE 2 PATCHLET_OFFSET 0x00015e72 JUMPOVERBYTES 5 ; we skip the call to SetSecurity@RpcSession N_ORIGINALBYTES 1
code_start add esp, 0ch ; 3 x pop mov eax, 00000000h ; simulate that SetSecurity@RpcSession() function ; returned 0 (as on successfull call) code_end
patchlet_end
And here it is in action:
As always, if you have 0patch Agent installed and registered, this
micropatch is already on your computer - and applied to taskcomp.dll in your Task Scheduler service. 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 1809 32bit
Windows 10 version 1809 64bit
Windows Server 2019
0patch PRO users are welcome to request porting this micropatch to other Windows 10 or Server versions at support@0patch.com. (Note that Windows 8, Windows 7, and their Server counterparts 2012 and 2008 don't seem to be affected.)
One final question: Does the attacker really need a local user's password?
We seriously doubt that. While running the legacy schtasks.exe with an incorrect password via argument /RP results in an error, the documentation for IScheduledWorkItem::SetAccountInformation method (which actually gets called by legacy schtasks.exe) states: "If you set the TASK_FLAG_RUN_ONLY_IF_LOGGED_ON flag, you may also set pwszPassword to NULL for local or domain user accounts." We haven't tested this but it sounds reasonable that for "run only if logged on" tasks a password would not be needed. Since attacker's goal is not to have the task executed but to have Task Scheduler change permissions on a target file, we believe executing the attack should also be possible without knowing any password.
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
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.
What Now?
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.
Wait, what?
Permissions on the file downloaded
with Internet Explorer
Permissions on the file downloaded
with Edge
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.
The Micropatch
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:
MODULE_PATH "..\AffectedModules\urlmon.dll_11.0.17134.648_32bit\urlmon.dll" ; Windows 10 version 1803 PATCH_ID 362 PATCH_FORMAT_VER 2 VULN_ID 5000 PLATFORM win32
patchlet_start
PATCHLET_ID 1 PATCHLET_TYPE 2 PATCHLET_OFFSET 0x00034507 ; Injecting after the call to GetZoneFromAlternateDataStreamEx
code_start
cmp eax, 0x80070005 ; did we get ACCESS DENIED on reading the MOTW? jne pass ; nope, all is OK
; we got ACCESS DENIED mov dword [ebp-4], 3 ; we set the ZoneID to 3 if we had ACCESS DENIED
pass:
code_end
patchlet_end
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]
0patch PRO users are welcome to request porting this micropatch to other Windows 10 versions at support@0patch.com.
* [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.
Maribor, Slovenia, April 2nd 2019 – ACROS Security, a globally recognized provider of security micropatches, is excited to announce the launch of 0patch PRO, a service that simplifies security patching on Windows platform for Enterprises and individuals.
The official launch date is April 2nd 2019.
0patch PRO is a first commercial platform of its kind to enable micropatches (microscopic pieces of binary code that replace only vulnerable parts) to rapidly and unobtrusively fix the most important security vulnerabilities. By using an innovative approach, 0patch reduces the “security update gap” in enterprises from months to days or even hours.
Micropatching, implemented by 0patch PRO, enables quick fixes of "0days" and unpatched vulnerabilities, end-of-life and unsupported products (for instance, vulnerable old Java versions) and fixes vulnerable 3rd party components and customized software. 0patch PRO provides patches for legacy operating systems and applications, and eliminates vulnerabilities that are being exploited in the wild.
0patch makes software patching almost imperceptible, without unnecessary system or application reboots and with minimal footprint.
The company is offering 0patch FREE service for personal and non-profit educational use that will implement the same technology as 0patch PRO, but with a limited set of micropatches.
ABOUT ACROS Security
ACROS Security, with two decades of presence on international markets, is a leading provider of in-depth security research, realistic penetration testing, code review and security patches for customers with highest security requirements.
ACROS Security serves customers across a wide range of industries, including global software vendors, financial institutions, online service providers, cloud providers, virtualization solutions providers and others who consider security of their products, information and services critical.
Why It Makes Sense To Micropatch a Vulnerability When Official Fix Is Available
by Mitja Kolsek, the 0patch Team
Yesterday, Checkpoint published a detailed vulnerability report about a serious vulnerability in WinRAR (reportedly used by 500+ million users) which allowed a malicious archive, when extracted, to create an arbitrary file in a chosen location on user's computer. This includes,for instance, user's Startup folder where said file would get executed when the user logged in to Windows again. Nadav Grossman, the researcher who found the vulnerability and wrote the report, nicely described
the
flow of his analysis, including the problems he stumbled into, and
tools he used to analyze the vulnerability. Anyone interested in
analyzing vulnerabilities, or in details of this particular one, is
advised to read his report.
The vulnerability (CVE-2018-20250) was present in WinRAR's extracting of ACE archives, whereby said extracting was actually done using a dynamic library UNACEV2.DLL written by e-merge GmbH, maintainer of WinACE software. This DLL, interestingly, hasn't changed since 2005 - which actually attracted Nadav's attention.
At the time the report was published, WinRAR producer RARLAB was already offering a fixed version of WinRAR (5.70 beta 1) on their download page, and their fix was very simple: being unable to fix the vulnerability, they dropped support for ACE format. (You might remember Microsoft doing something similar with the 17-year old Equation Editor last year.)
To Micropatch Or Not To Micropatch?
For us at 0patch, a vulnerability like this is inherently interesting: it allows for a remote code execution, it potentially affects millions of users, and the product doesn't auto-update so it is likely to be present for a long time. And while we've micropatched a bunch of 0days lately, 0patch is primarily meant to bridge the security update gap by fixing vulnerabilities that may already have an official fix.
In deciding whether to micropatch this one, we came up with the following supporting arguments:
This vulnerability is a perfect candidate for malicious phishing campaigns. "Please review the attached purchase order PO.rar."
WinRAR is a really widely used product. Maybe it doesn't actually have 500 million users but still.
No auto-update means that most users will never update it. In fact, unless one has actually read about this vulnerability (surely most users haven't) and feels threatened by it, why would one take the time to update?
Some users may actually need the ACE format support. Granted, there is still WinACE, but in contrast to WinRAR, its maintainer seems long gone.
From defensive perspective, blocking exploits of this vulnerability is not as simple as rejecting files with .ace extension, as a malicious file can also be renamed to .rar or .zip (or likely any other supported extension) as long as it's extracted with WinRAR. The user can't even tell that a ZIP file is actually an ACE file.
Some of 0patch users are probably also users of WinRAR. By creating a micropatch, we will protect them without them even being aware of this WinRAR vulnerability, or the fact that we micropatched it. (Letting users not worry about patches and not disturbing them with patching is a core concept of 0patch.)
Admins with multiple 0patch Agents on users' computers would like the fact that whichever version of WinRAR, even if old and vulnerable, their users use or "temporarily" install, they will be protected against this issue.
Other products than WinRAR may be using this particular version of UNACEV2.DLL, and our micropatch would protected from a similar attack too, without us even knowing what these products are.
As you suspected, we decided to go ahead.
Patching
Fortunately, Nadav's analysis provided most of the details we needed (big thanks, Nadav!) and a potential solution was fairly obvious. If you look at Figure 15 in his report and the accompanying text, you'll see that the exploit causes the execution through a branch that allows the malicious file path to remain a relative path by prepending it with... well, an empty string. So the idea was to always force the execution through the other branch, which prepends the malicious path with the target directory.
This way, instead of allowing malicious paths like:
C:\some_folder\some_file.ext
or
C:../some_folder\some_file.exe
to pass unmodified, they would become (when extracted to C:\temp):
C:\tempC:\some_folder\some_file.ext
or
C:\tempC:../some_folder\some_file.exe
These are both invalid paths and an application can't create a file using them.
One question remained open, however: Do any legitimate use cases need the code branch we were planning to "amputate"? Such use case would include intentionally extracting a file to a location specified with full path on user's computer; which would be considered a vulnerability of equal severity as the issue we're fixing here. So we concluded that removing this branch is an acceptable approach, and wrote this single-instruction micropatch:
PATCHLET_ID 1 PATCHLET_TYPE 2 PATCHLET_OFFSET 0x000CC13 JUMPOVERBYTES 10 ; We remove mov and call N_ORIGINALBYTES 1
code_start
mov eax, 0 ; making sure to proceed via the "absolute path" branch, ; not the "empty string" branch
code_end
patchlet_end
When the micropatch is applied, extracting a malicious ACE archive results in a number of warnings as shows below, and files aren't extracted.
Here's a video showing our micropatch in action:
Conclusion
By micropatching this vulnerability in a 14-year old closed-source DLL we demonstrated how suitable in-memory micropatching is for quickly fixing a critical vulnerability, even when fixing it in source code is literally impossible.
One nice side-effect of micropatching a third-party DLL is that while our micropatch was targeted at WinRAR, it actually protects all products using this particular version of UNACEV2.DLL from a similar attack. The DLL will be patched no matter who loads it. If you come across one, do let us know!
As always, if you have our Agent installed and registered, this
micropatch is already on your computer - and applied to WinRAR
whenever it loads UNACEV2.DLL (enable popups via 0patch Console to see when that happens).
Otherwise, you can register a free 0patch account and install 0patch Agent to get this micropatch applied to your WinRAR.
Nevertheless - if you don't need support for ACE format in WinRAR, we recommend you update WinRAR to a fixed version (currently 5.70 beta 1). There may be other vulnerabilities there in UNACEV2.DLL which are more likely to be found now that it's been put in the spotlight.
[Update 2/12/2019: Just one day after we had issued this micropatch, Adobe released an update of Adobe Reader DC that fixed the vulnerability. We strongly recommend all users of Adobe Reader to apply Adobe's update, at which point our micropatch will automatically no longer get applied.]
Today we'll look at a fairly simple vulnerability in Adobe Reader DC that allows a PDF document automatically send an SMB request to attacker's server as soon as the document is opened. The vulnerability was published by Alex Inführ along with a proof-of-concept in a detailed report on Alex's blog and hasn't been patched at the time of this writing. [Update 2/12/2019: the vulnerability was subsequently assigned CVE ID CVE-2019-7089.]
This vulnerability, similar to CVE-2018-4993, the so-calledBad-PDFreported by CheckPoint in April last year, allows a remote attacker to steal user's NTLM hash included in the SMB request. It also allows a document to "phone home", i.e., to let the sender know that the user has viewed the document. Obviously, neither of these is desirable.
At the time of this writing, the latest version of Adobe Reader DC, 2019.010.20069, is affected but older versions are likely in the same boat too.
Analysis
To understand the problem, let's start with the above-mentioned Bad-PDF (CVE-2018-4993). The malicious PDF included a certain element that triggered automatic loading of another PDF from a remote share. Until Adobe Reader got patched, this happened without the user noticing anything, or being able to prevent it.
Adobe patched this issue by adding a security warning that was shown to the user before making the request to a remote share:
Security warning added to fix the Bad-PDF issue.
This warning allowed the user to decide whether to allow the potentially malicious document to "phone home" or not.
Now on to the issue at hand: it is functionally identical (for attacker), just in a different place. While Bad-PDF used an /F entry to load a remote file, this issue exploits loading a remote XML style sheet via SMB. Interestingly, if the document tries to do so via HTTP, there is a security warning there:
Attempting to load a remote style sheet via HTTP triggers a warning.
However, when using a UNC path (the type of path that denotes a resource in a shared folder), the loading occurs without a warning
The Micropatch
The Reader already implies the correct behavior by showing a security warning on loading a remote style sheet via HTTP, so we decided to add our own security warning for loading style sheets via UNC as well.
We started by locating the point where the HTTP-loading and UNC-loading execution paths diverge, and patch-in a warning for UNC paths. Finding said divergence point was relatively easy using Process Monitor: we caught CreateFile events on both HTTP and UNC loads, then compared their call stacks to see where the two execution paths said good-bye to each other. This turned out to be in function sub_208B3C53, the relevant code block of which is shown here:
Both HTTP- and UNC-based style sheet loads reach the above code block, whereby an HTTP-based load never returns from the first call marked in red (it triggers an exception that Reader handles somewhere else), and a UNC load is implemented inside the second call marked in red.
As you can see from the comments in the code, we found where the file path resides before the second call (this required a little digging on the stack at that point). We verified it by changing said path on-the-fly in debugger to make sure the path-loading code actually uses that, and then we were ready to inject our patch.
There exists a handy Windows function called PathIsUNCA, which is already being used by Reader elsewhere to check whether a path is a UNC path (the "A" at the end denotes an ASCII path in contrast to a wide-char path). So we decided to first call this function to see if we have a UNC path, and then if we do, issue a prompt to the user. To do the latter, we used another well-known Windows function MessageBoxA. Based on user's response, we would then allow the UNC path to be loaded or not. The only thing to decide at this point was how to implement the "not", i.e., how to prevent the UNC path from loading if the user decided not to trust the document.
In such cases, we always want to keep things simple and minimize possible surprises. We therefore decided to simply empty the path string by putting a zero at its beginning, resulting in Reader trying to load an empty path. It does complain that it can't load it though but the exploit is blocked and, frankly, we're fairly confident that loading style sheets form UNC paths doesn't work at all because as much as we tried, Reader always encountered an error. So maybe UNC style sheets are not a supported use case at all but they did turn out to be a vulnerability.
PATCHLET_ID 1 PATCHLET_TYPE 2 PATCHLET_OFFSET 0x000b3f6b ; Injecting before the call towards loading ; a non-HTTP URL PIT Shlwapi.dll!PathIsUNCA,User32.dll!MessageBoxA
code_start
push ecx ; save ecx so we don't corrupt it
mov eax, dword [eax+10h] ; at this point, the address of path ; is at [eax+10h]+4 add eax, 4 push eax push eax ; we push the address of path twice as we may need it ; in remediation too call PIT_PathIsUNCA ; is it a UNC path? test eax, eax jz skip ; not a UNC path - skip the warning dialog
push 0x00000134 ; uType = MB_YESNO | MB_ICONEXCLAMATION | MB_DEFBUTTON2 call get_lpCaption db "Security Warning",0 get_lpCaption: call get_lpText db "This document is trying to access a file on the network. If you trust this document, choose Yes. If you do not trust this document, choose No.",0 get_lpText: push 0 ; hWnd = NULL call PIT_MessageBoxA cmp eax, 6 ; Did user click "Yes"? je skip ; if so, skip remediation
; Remediation: we'll simply delete the entire URL string by putting ; a 0 at the beginning pop eax ; pop the previously pushed path address mov byte [eax], 0 ; put a 0 at the start of path push eax ; we need to re-push something because we have a pop in the epilogue
skip: pop eax ; pop the additional address of path
pop ecx ; restore original ecx and continue to execute the call ; towards loading the provided URL
code_end
patchlet_end
With this micropatch in place, opening the malicious document results in the following warning before a request is made to the remote server:
The security dialog that was micropatched into Adobe Reader.
If the user selects "Yes", the remote style sheet is loaded (whereby the SMB request delivers your NTLM hash to the remote server), and if they select "No", Reader complains about not being able to load the style sheet from and empty path. Done.
We asked Alex, the security researcher who found this issue, to test our micropatch and he was kind enough to confirm its efficiency against this attack. (Thanks, Alex, for your help! We encourage all security researchers to collaborate on creating and testing micropatches.)
For the visual types among you, here is a video of our micropatch in action:
Conclusion
As always, if you have our Agent installed and registered, this micropatch is already on your computer - and applied to Adobe Reader whenever it loads AcroForm.api (that only happens when certain documents are opened).
Otherwise, you can register a free 0patch account and install 0patch Agent to get this micropatch applied to your Reader.
Note that Adobe might issue an official fix for this issue tomorrow as they
release their monthly Reader update, but if not, we'll quickly port the micropatch to the new Reader version to keep the vulnerability
patched on your computers. [Update 2/12/2019: Adobe did issue an official fix the next day.]