Friday, October 12, 2018

Patching, Re-Patching and Meta-Patching the Jet Database Engine RCE (CVE-2018-8423)

Flawed Patches Will Always Happen, But We Can Change How They Get Fixed 


by Mitja Kolsek, the 0patch Team


TL;DR: Microsoft patched CVE-2018-8423 eighteen days after we had micropatched it. Their official patch turned out to be incomplete so we re-micropatched it.





This is a story about a Windows vulnerability that was reported to Microsoft, published as "0day" before the official patch was available, micropatched by us one day later, subsequently patched by Microsoft, found to be incorrectly patched, and now micropatched again over the flawed official patch. Confused? It's actually a nice case study to demonstrate... not how we are smarter than Microsoft (we really aren't) but how micropatching is a much more suitable technology for fixing vulnerabilities on billions of computers than the current de-facto standard of what we call "fat updates".

The story begins with the ZDI reporting a memory corruption vulnerability in Jet Database Engine (subsequently assigned CVE-2018-8423) to Microsoft, which then led to a public 0day drop 135 days later without Microsoft having issued an official fix for it. We at 0patch took ZDI's proof-of-concept exploit and created a free micropatch for our users just 24 hours later. For more details up to that point see our previous blog post.

Now, a few days ago, Microsoft issued their fix for this issue as part their October 2018 Monthly Update. As expected, the update brought a modified msrd3x40.dll binary: this is the binary with the vulnerability, which we had micropatched with 4 CPU instructions (one of which was just for reporting purposes). The version of msrd3x40.dll changed from 4.0.9801.0 to 4.0.9801.5 and of course its cryptographic hash also changed - which resulted in our micropatch for this issue no longer getting applied to msrd3x40.dll. This is a welcome automatic side effect of in-memory patching, unburdening users from doing anything beyond applying the official vendor update once it becomes available. It also nicely demonstrates how traditional "module-replacing" updating can safely co-exist with in-memory micropatching.

Naturally we were curious whether Microsoft's fix was identical to our micropatch. Two months ago, we compared Microsoft's fix with our own micropatch for the famous Sandbox Escaper's 0day and found them to be functionally identical.

So we BinDiff-ed the patched msrd3x40.dll to its vulnerable version and reviewed the differences. At this point we will only state that we found the official fix to be slightly different to our micropatch, and unfortunately in a way that only limited the vulnerability instead of eliminating it. We promptly notified Microsoft about it and will not reveal further details or proof-or-concept until they issue a correct fix.

We have, however, issued a micropatch that corrects Microsoft's patch. Namely, in an ironical twist of fate Microsoft's October update actually re-opened the CVE-2018-8423 vulnerability for 0patch users who were previously protected by our micropatch. This new micropatch, which has already been distributed to all online users by now, resumes their protection. At the time of this writing, it is confirmed to be applicable to fully updated 32-bit and 64-bit Windows 10, Windows 8.1, Windows 7, Windows Server 2008 and Windows Server 2012. We suspect all other affected Windows versions also share the same version of msrd3x40.dll, in which case the micropatch will apply there as well. (We'll update this information as we go.)

We all know that many Windows users and admins don't immediately apply Windows updates as they become available. This begs the question: What will happen if you have 0patch Agent installed and haven't applied the October Windows Updates yet? Obviously, you're currently protected by our micropatch for msrd3x40.dll version 4.0.9801.0, which is getting applied to this module whenever the module gets loaded in any running process. When you decide to apply the October Windows Updates, they will make you restart your computer, after which there will be a new version of msrd3x40.dll there, version 4.0.9801.5. But our new micropatch for it will also already be there installed and waiting in your 0patch Agent, so whenever the new  msrd3x40.dll gets loaded, it will immediately be micropatched in memory. Bottom line: you don't have to do anything!

You can see our micropatch in action in the video clip below. The video shows that after the official vendor fix for CVE-2018-8423 has been applied, the original proof-of-concept published by the ZDI really doesn't work anymore (WScript.exe correctly detects invalid input data and displays an error message), but a slightly modified POC still manages to cause an out-of-bounds write and crashes the process. ("Page heap" must be enabled for the process to actually crash, while memory corruption occurs without that as well.) Finally, the video shows that our micropatch fixes the vulnerability for the modified POC as well.







Now what does this story tell us? A couple of things.


1) In-memory micropatching nicely co-exists with traditional "fat" updating. Not only does a micropatch that is currently getting applied get automatically obsoleted when the target binary is replaced by a new version, but a micropatch for a future binary version can also be patiently waiting for you to apply a vendor update, and automatically start getting applied to it when you do. All without you so much as lifting a finger.

2) Flawed patches will always happen. This time it was Microsoft, next time it will be someone else, and then we will make a flawed micropatch ourselves. The possibility of flawed micropatches is designed into our model though; by now you already know that a micropatch gets distributed to all online agents within 60 minutes of issuance, and gets instantly applied to vulnerable modules on your computer. But in case something went terribly wrong with it and it broke something (like this vendor patch did), we could also have it revoked on all online agents within 60 minutes, causing it to stop applying. Meanwhile, you would be able to manually disable the micropatch via 0patch Console.

3) Micropatching is quick and user-friendly. All of the above - deployment, application, removal, and revocation or a micropatch - happens instantly, without restarting the computer or even having to re-open running applications. Compare this with today's standard approach to patching: first you download a ridiculous amount of bytes, which often forever take disk space on your computer, and then you have to at least restart patched applications (with all the documents you're working on) if not restart the computer, to actually get a handful of code changes applied. When something goes wrong, it takes at least as much time to revert to the pre-patched state, and that's only in the ideal case.

4) In-memory micropatching is a superior method to "fat" updates for fixing most security issues. We're not saying we could entirely do away with the concept or replacing modules on your computer and just keep micropatching it into eternity. No, software products periodically need significant modifications, but that's mostly for functional or user interface-related changes - and these really don't need to come on a monthly basis. Security fixes, however, actually need to be delivered faster if we ever want to outrun the attackers, and applied in a less user-hostile way than what we're all used to experiencing today, ideally so that users won't even notice their vulnerabilities disappearing under their feet. We can envision a product update model that includes annual or semi-annual fat updates, and lots of intermediate micropatches for security issues and occasional functional ones.



Our micropatches, including this current one, are still completely free for everyone, and can be obtained by installing and registering 0patch Agent from https://0patch.com. We highly welcome your feedback on support@0patch.com.


Cheers!

@mkolsek
@0patch








Wednesday, October 3, 2018

Words "Patching" and "Instantly" Now in the Same Phrase

A Retrospective of The Last Couple of Months in our Patch Factory

by Stanka Salamun, the 0patch Team


The last couple of months were very exciting for our team. We were good Internet citizens – all together we produced, tested and distributed a few hundred bytes of code for more than 15 micropatches (feel free to sigh and whisper: “so tiny?” 😊), but every byte of that was a precise microsurgical cut with a significant impact to the security of your computers. You probably did not notice any of them, because we strive to make the healing process completely painless for you. It’s time for the world to realize that the terms "patching" and "instantly" finally belong together.

We fixed 4 (yes, four) 0days; right now one of them is still without an official vendor patch, and some were or are exploited in the wild.  One bug that we fixed was initially rejected for patching by the original vendor and another one broke users’ networking.  Some of these “micropatch stars” deserve a bit of additional attention.


Outrunning the attackers at a 0day in Microsoft Jet Database Engine




This one is still a genuine 0day without a CVE.  The Zero Day Initiative published details of an unpatched remotely exploitable vulnerability in all Windows versions due to Microsoft missing their 120-day fixing window.

How long does it take for a vulnerability to be patched after a 0day has been dropped? Our answer is: ideally, one day. Challenge accepted: 7 hours after ZDI has published details on this unpatched remotely exploitable vulnerability in Jet Database Engine, we had a micropatch candidate on Windows 7, and within 24 hours our users had micropatches installed and applied on all supported Windows versions.


One of our goals with 0patch is to make vulnerability patching so fast that attackers won't even manage to develop a reliable exploit for a public vulnerability before it is already patched on most users' computers. What a goal, huh?

There is still a lot of buzz about this bug in the media – you can read about it in The Register, Softpedia  and SecurityWeek.  We also reveal all the dirty technical details in our blog post.

As of this writing, our micropatches for the remotely exploitable "0day" in Jet Database Engine are still THE ONLY patches available for this issue.


We Also Micropatched a Publicly Dropped 0day in Task Scheduler (CVE-2018-8440) Ahead of Microsoft




Being who you are can be a bad thing if you're a system service. This is especially true if you are Windows Task Scheduler service that allows a local unprivileged user to change permissions of any file on the system, and you suffer from a local privilege escalation vulnerability. Bad luck for Task Scheduler: as it was a local bug that required the attacker to be locally logged, Microsoft didn’t feel the need to release an out-of-band update.

But we did.  We created a micropatch and described it in detail. After that we were curious how Microsoft would approach the issue. It turned out their official patch was functionally identical to our micropatch, but we were able to be more agile and less intrusive using a different patching technology.

For this one we earned our appearance in Forbes as they said: “ACROS Security seems to have beaten Microsoft to the punch.”


Famous CVE-2018-8174 – a micropatch instead of the official update that probably broke your network


This was a critical remote code execution issue in Microsoft VBScript Engine, exploit for which has previously been detected in the wild.  The bug became kind of famous because the official Microsoft update broke networking on some computers, prompting users to avoid its application.  As Windows updates are "all or nothing" these days, the users can't just remove a defective KB and enjoy the protection provided by other KBs issued on the same Patch Tuesday, so many of them were left vulnerable. For these users we created a single-instruction micropatch and you can read the whole story here.


CVE-2018-8414 – initially rejected for patching by Microsoft

This vulnerability was reported to Microsoft but deemed non-critical, until attackers started exploiting it. So an official vendor patch was available, but we decided to create a micropatch anyway.

We wanted to prove that micropatches could implement the same logic as official updates, but without the fuss for the users and with minimum changes on the affected system. Many users and admins don't / can't / won't / shan't apply official updates or delay their application for all sorts of reasons. In all these cases micropatching is a reliable, targeted, instantly reversible alternative.

In our video you can view this micropatch in action.



As always, all of these micropatches were automatically deployed on all computers running 0patch Agent within 60 minutes from our publishing.  So if you had 0patch Agent installed, you were among the lucky ones that were immediately protected.


Finally,  a friendly reminder for those who aren't 0patch users yet: our micropatches for the remotely exploitable "0day" in Jet Database Engine are still THE ONLY patches available for this issue. And they're FREE! Everyone is welcome to download free 0patch Agent from https://0patch.com  and register a free account to get these micropatches.

If (or when) the official fixes become available, just apply them fearlessly. Our micropatches will automatically stop getting applied because the cryptographic hashes of updated binaries will no longer match the ones associated with the micropatches. You don't have to do anything else; 0patch micropatches are simply stepping aside when they are no longer your best option.

Monday, September 24, 2018

Outrunning Attackers On The Jet Database Engine 0day (CVE-2018-8423)

Micropatching Makes It Possible To Create And Apply Patches Before Attackers Write a Reliable Exploit

by Mitja Kolsek, the 0patch Team


Timeline


  • Day 0 (05/08/18) - ZDI reports the vulnerability to Microsoft
  • Day 135 (09/20/18) - ZDI publishes vulnerability details and PoC, no official fix available
  • Day 136 (09/21/18) - Micropatches for all supported Windows versions created, distributed and applied on all users' computers
  • Day 154 (10/09/18) - Microsoft issues an official fix 





 

Introduction


Last Thursday, Zero Day Initiative published details of an unpatched remotely exploitable vulnerability in all Windows versions (discovered by Lucas Leong) due to Microsoft missing their 120-day fixing window. We immediately tested ZDI's proof-of-concept and found the following:

  1. Jet is only supported in 32-bit, which means that a 64-bit application tricked into accessing a malformed data source file will not be exploitable. Indeed, double-clicking ZDI's poc.js on 64-bit Windows results in an error message; in order to launch poc.js on a 64bit machine one needs to use the 32-bit wscript.exe by launching c:\windows\SysWOW64\wscript.exe poc.js.
  2. Obviously, getting a user to launch a .js file is not a convincing attack scenario (such file could already do anything within user's privileges). The good news for attackers is that this attack can be mounted via Internet Explorer, especially since even on 64-bit Windows, Internet Explorer rendering processes are 32-bit. On the upside, we were unable to get the exploit working from a web site because - at least on IE11 - the security setting "Access data sources across domains" is disabled in Internet and Intranet zone, which resulted in a JavaScript error. Launching a malicious poc.html from a local drive (or USB disk) does work, however, whereby the accompanying data source file can be in a shared folder and doesn't need to be delivered with poc.html. Nevertheless, the user then has to press the "Allow blocked content" button, which amounts to a considerable level of social engineering required to execute the attack via Internet Explorer.
  3. A more realistic attack could probably be conceived using a malicious Office document referencing an external malformed Jet data source. We haven't investigated that, however, as our job is not to write exploits but micropatches. (Resourceful attackers will soon reveal their weaponization ideas anyway.)

Vulnerability Analysis


As usually, we started our analysis from the closest observable point of failure and worked backward to the vulnerable code. Ideally, the "closest observable point of failure" is a process crash, and in this case, ZDI's PoC indeed causes a crash in wscript.exe due to an attempt to write past the allocated memory block. So their PoC was perfect for us. (Not surprisingly, it's easier for us to work with a crash case than a full blown calc-popping exploit.) Here's how the crash looks like in WinDbg, with Page Heap enabled and invalid memory access in function TblPage::CreateIndexes:


eax=00002300 ebx=00000002 ecx=00834918 edx=00000000 esi=008360a8 edi=0024d64c
eip=6b881234 esp=0024d4c0 ebp=07ac427c iopl=0         nv up ei pl nz ac po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010212
msrd3x40!DllRegisterServer+0x1b644:
6b881234 89b48174050000  mov     dword ptr [ecx+eax*4+574h],esi ds:002b:0083da8c=????????


For anyone with some mileage in reverse engineering, the instruction causing the exception indicates that the code is accessing an indexed element in an array containing 4-byte elements, whereby the array starts at ecx+574h and the index is in eax (which contains 2300h). And it's clear that the index is beyond the array's limits.

From a patcher's perspective, this raises the following questions:

1) Where does this index come from? (And does it affect anything else, which would cause a micropatch to simply shift the problem further down the code and prompting another micropatch for that?)

2) Does the array have a fixed size? (Which would mean a simple hard-coded index validation micropatch could suffice.)

In tracking the index value 2300h, we found that eax was read from address esi+24h a few instructions back. We then used !heap to find out who allocated it and set an access breakpoint to see who wrote this value there. That turned out to be function Index::Restore, which is called a couple of instruction earlier in TblPage::CreateIndexes. What happens there is value 2300h is copied to the location we're watching with our access breakpoint from address pointed to by edx. And edx at that moment points to the memory-mapped copy of the malformed data source file.

So we found out that the malformed value 2300h comes directly from the PoC file group1, specifically at offset 1257h. Furthermore, looking around the code for additional uses of this user-provided index, we found one case immediately after the crash location, so whatever we'd do we'd need to make sure that this second use wouldn't also cause problems.

An obvious solution to fixing both uses of the malformed index was to check the index immediately after it gets copied from the memory-mapped file (i.e., right after the call to Index::Restore), and setting it to some safe value in case it was out of bounds. But in order to check the bounds we'd need to know the bounds.

Using !heap on ecx at the point of crash we inspected who allocated the memory block from which the PoC has escaped, and how large that block was. We found it to be a fixed-size block of 778h bytes, most likely a C++ object containing a fixed-size array of 4-byte elements. But how many elements? Let's calculate: the array begins at offset 574h in the memory block, and ends at most at offset 778h-1. The difference between the two means there is at most 204h bytes in the array, and dividing that by 4 (bytes) we get 81h elements, meaning that valid index values are between 00h and (at most) 80h.

Note that we say "at most 80h" - at this point we can't know whether the entire space between offset 574h and the end of the memory block is reserved for the array (although it surely seems so), we just calculated the bounds for preventing the offending instruction from escaping the memory block. And we'll be okay with this for now: we'll prevent an exploit from overwriting heap management data or other objects allocated on the heap, which will take most (if not all) of the leverage from the attacker.


The Micropatch 


So how should the micropatch look like? It would have to be injected right after the call to Index::Restore and check that the user-supplied value that was written to [esi+24h] doesn't exceed 80h. If it did exceed 80h, the patch would have to correct the value at [esi+24h], say by overwriting it with 0 (which is a valid index).  And here is the source code of this micropatch:


; Patch for VULN-4112 in msrd3x40.dll version 4.0.9801.0 32bit
MODULE_PATH "..\AffectedModules\msrd3x40.dll_4.0.9801.0_32bit\msrd3x40.dll"
PATCH_ID 1000010
PATCH_FORMAT_VER 2
VULN_ID 4112
PLATFORM win32


patchlet_start
 PATCHLET_ID 1
 PATCHLET_TYPE 2
 PATCHLET_OFFSET 0x4118C
 code_start

   cmp dword [esi+24h], 0x80  ; is the index lower than or equal to 0x80?
   jbe pass                   ; if not, we let it pass
                              ; (jbe means unsigned compare, we don't want to allow
                              ; values like 0x80002300 to be considered negative)
   mov dword [esi+24h], 0     ; otherwise we set it to 0
   call PIT_ExploitBlocked    ; and show the Exploit Blocked popup
  pass:

 code_end
patchlet_end



We initially wrote this micropatch for Windows 7, and had a candidate ready just 7 hours after ZDI had published their PoC. After attempting to port it to other supported Windows versions, we noticed that almost all of them have the exact same version of msrd3x40.dll - which meant the same micropatch would apply to all these systems. The only Windows version with a different msrd3x40.dll was Windows 10: peculiarly, both DLLs had the same version and exactly the same size, but plenty of small differences between the two (including the link timestamp). The code was exactly the same and in the same place though (probably just a re-build), so we could actually use the exact same source code for the micropatch, just a different file hash.

These two micropatches for a published 0day were then issued less than 24 hours after the 0day was dropped, and distributed to our users' computers within 60 minutes, where they were automatically applied to any running process with vulnerable msrd3x40.dll loaded. Which nicely demonstrates the speed, simplicity and user-friendliness of micropatching when it comes to fixing vulnerabilities. In fact one of our goals with 0patch is to make vulnerability patching so fast that attackers won't even manage to develop a reliable exploit for a public vulnerability, much less launch a campaign with it, before the vulnerability is already patched on most users' computers. What a goal, huh?


How Can You Get These Micropatches, And What To Do Next?


These micropatches are completely free for everyone, and can be obtained by installing and registering 0patch Agent from https://0patch.com. They will prevent exploitation of this 0day as long as you have the latest Windows updates applied (we did not port them to older Windows updates, but if you have a need for that you can contact us at support@0patch.com).

Once Microsoft issues their official fix (likely on October Patch Tuesday), you will simply apply that update and these micropatches will automatically get obsoleted without you having to do anything else.


Cheers!

@mkolsek
@0patch

Tuesday, September 11, 2018

Comparing Our Micropatch With Microsoft's Official Patch For CVE-2018-8440


by Mitja Kolsek, the 0patch Team


As expected, Windows Update has just brought the official patch for CVE-2018-8440 today, a patch that would replace our "unofficial" micropatch we've issued 13 days ago.

To quickly refresh your memory, 15 days ago security researcher SandboxEscaper published details and proof-of-concept (POC) for a "0day" local privilege escalation vulnerability in Windows Task Scheduler service, allowing a local unprivileged user to change permissions of any file on the system. The next day, we at 0patch have analyzed the vulnerability and prepared a "micropatch candidate" (a micropatch that blocks the POC but needs to be tested for functional side effects). We published the micropatch for Windows 10 version 1803 the day after, and subsequently ported this micropatch to a number of other Windows versions.

Obviously, correcting someone else's code is not to be taken lightly (especially if that code is running on millions of computers), and not having the source code doesn't make it any easier. However, our team of security researchers and the growing community of experts who want to get their vulnerabilities fixed as quickly and efficiently as possible, are doing just that.

In this particular case, our analysis showed that a call for changing permissions of a file, made from Task Scheduler's code, should have been impersonated - but it wasn't. We corrected that by making a call to RpcImpersonateClient right before the call, and another call to RpcRevertToSelf right after it. Pretty basic stuff. And it worked.

Our micropatches are sometimes very similar to official patches (at least functionally speaking), and other times very different; not surprisingly, there is always more than one way to skin a cat - or to fix a vulnerability. So when the official patch from Microsoft came out today, we were naturally curious as to how they fixed it.

The first thing we noticed was that the Windows Update replaced schedsvc.dll with a new version. Promising - this is the exact DLL we had micropatched. Next we ran BinDiff to compare the new and the old version. Even more promising - the only modified function was RpcServer::SetSecurity, which is the very function we had micropatched. And finally, BinDiff showed the exact difference between the old and the new version of this function. Let's take a look at that (note: the diff is for 64bit Windows 7).




 
As you can see, Microsoft's official patch is functionally identical to our micropatch. We could say that this official patch delivers Microsoft's unofficial confirmation that our approach to patching this vulnerability - in a complex closed-source product - was also optimal from their perspective. That's very positive feedback for us and a great case for "unofficial" 3rd-party micropatches.

Don't get carried away, however! There have been, and will be cases where a 3rd-party micropatch will fix a vulnerability differently and less well than the official vendor fix - or even break a thing or two. While we're doing our best and leverage our advantages (agility, instant patch deployment, instant patch removal), original product developers will always have advantage in their knowledge about the product and its environment, and their code fixes will be preferred. Nevertheless, our mistakes are much easier to remedy than those delivered in a 300MB package that requires a reboot both to install and uninstall. When a micropatch needs to be revoked, it takes just a few minutes for us to do that, and within an hour all online agents stop applying it without interrupting users in the slightest. And an improved version of the micropatch can later be delivered just as easily.

In any case, if you have installed 0patch Agent to micropatch this vulnerability on your computer(s), it is now time to let the official fix take over. To do that, you simply apply today's Windows Updates and as soon as they replace the vulnerable schedsvc.dll, our micropatch will automatically stop applying because the cryptographic hash of the DLL will no longer match that associated with the micropatch. You don't have to do anything else; this is 0patch being a good citizen and stepping aside when it's no longer your best option.


Cheers!

@mkolsek
@0patch




Friday, August 31, 2018

How We Micropatched a Publicly Dropped 0day in Task Scheduler (CVE-2018-8440)


Being Who You Are Can be a Bad Thing if You're a System Service


by Mitja Kolsek, the 0patch Team



[Update 9/11/2018] Official fix for this vulnerability is now available and users are advised to apply Windows Updates, which will automatically result in our micropatch not getting applied any more. You're welcome to read about our comparison between our micropatch and Microsoft's official fix. 

Earlier this week security researcher SandboxEscaper published details and proof-of-concept (POC) for a "0day" local privilege escalation vulnerability in Windows Task Scheduler service, which allows a local unprivileged user to change permissions of any file on the system - and thus subsequently replace or modify that file.

As the researcher's POC demonstrates, one can use this vulnerability to replace a system executable file and wait for a privileged process to execute it. In particular, it was shown that a printing-related DLL could be replaced and then executed by triggering the Print Spooler Service to load it. (The latter being a legitimate system operation, only used for demonstrating how replacing a system executable leads to elevated privileges. One could alternatively replace one of a large number of other system executables, or perhaps even a configuration file that gets loaded by a privileged process.)

SandboxEscaper's documentation properly identifies the problem being in Task Scheduler's SchRpcSetSecurity method, which is externally accessible via Advanced Local Procedure Call (ALPC) facility. This method, which can be called by any local process, sets a desired security descriptor (sddl) on a task or folder, i.e., on a provided file path (path).

HRESULT SchRpcSetSecurity(
   [in, string] const wchar_t* path,
   [in, string] const wchar_t* sddl,
   [in] DWORD flags
 );

SandboxEscaper noticed that this method fails to impersonate the requesting client when setting the security descriptor, which results in Task Scheduler changing the access control list of the chosen file or folder as Local System user even if the user calling this method is a low-privileged user. Impersonation is a feature where, to put it simply, a process running as user A gets a request for some action from user B and performs this action disguised as user B, borrowing user B's permissions for that. Task Scheduler is such a process running as user Local System, and when some other user calls its SchRpcSetSecurity method, it should impersonate the caller to perform the file operation using their identity - but apparently it doesn't, and uses its own powerful permissions to do so.

What the POC does to demonstrate this issue is:

  1. create an UpdateTask.job file in folder %SystemRoot%\Tasks where any user is allowed to create files (this is needed in the process of creating a new scheduled task, and non-admin users are allowed to do that); however, this file is not an ordinary file but rather a hard link pointing to a system file PrintConfig.dll. (which non-system user can't modify or replace);
  2. call Task Scheduler's SchRpcSetSecurity method to change permissions on UpdateTask.job such that everyone will be able to modify it; this actually changes permissions of the linked-to PrintConfig.dll file, which thus becomes user-modifiable;
  3. replace PrintConfig.dll with a "malicious" DLL that simply launched Notepad;
  4. trigger the Print Spooler service to load and execute the modified PrintConfig.dll using its own Local System identity.


Vulnerability Analysis 


The problem is clearly in step #2, which allows a non-admin user to change permissions on a system executable, and one can quickly assess the root cause of the problem to be a combination of two facts:

  1. Task Scheduler doesn't impersonate the caller in SchRpcSetSecurity method when performing the SetSecurityFile file system operation, and 
  2. Task Scheduler being willing to perform SchRpcSetSecurity on a hard link.

After running the POC, we took a look at operations performed on UpdateTask.job with Process Monitor, and found the one that changes permissions:




So we took a look at its call stack to see who invoked this action:




Okay, there's schedsvc.dll (Task Scheduler's executable) making a call to taskcomp.dll (Task Scheduler's helper library), which ends up with a call to kernel's NtSetSecurityObject. So we disassembled schedsvc.dll and taskcomp.dll to see what's going on in there at the identified locations. What we found was interesting.

The call from schedsvc.dll to taskcomp.dll occurs in function RpcServer::SetSecurity (in the orange block):





We were expecting to see code without any impersonation here, but actually found impersonation being used - just that the call that sets file permissions is done before the impersonation (in the lowest code block) begins.

The plan was clear: let's begin impersonation before the offending call to make sure that said call will be impersonated. So we created a micropatch with a single patchlet containing a call to RpcImpersonateCient and placed it at the beginning of the block preceding the orange block. How about reverting the impersonation? It turns out that wasn't needed because all code execution paths were leading directly to another impersonation call without making any other kernel calls that might be affected by our impersonation.

We tried this micropatch, but the exploit still worked !?! What was going on?

It turned out that there is another permissions-setting call in function RpcServer::SetSecurity, possibly a fallback mechanism in case the first one failed. So we made the first one fail, and the second one came to the rescue - again without impersonation (the middle orange block).






In this case, we can see a call to RpcRevertToSelf right before the offending call, which means that previous impersonation was reverted too soon to include the said call.

What we did here was remove the premature RpcRevertToSelf call and insert a replacement RpcRevertToSelf call to the code block following the offending call. While this block has many other branches leading to it, we checked that these are not impersonated which means our inserted call won't erroneously prematurely revert some other impersonation.

So finally, our micropatch worked and Process Monitor showed this instead:




You can see the "Impersonating" line, proving that we have successfully forced Task Scheduler to impersonate the calling user when trying to set permissions on UpdateTask.job. Now, since this file was a hard link to another file which our user had insufficient permissions to modify ACL for, the result was ACCESS DENIED, as it should be.

This is the source code of our micropatch, with all of its 4 instructions in three patchlets:


; Patch for VULN-4051 in schedsvc.dll version 10.0.17134.1 64bit
MODULE_PATH "..\AffectedModules\schedsvc.dll_10.0.17134.1_64bit\schedsvc.dll"
PATCH_ID 328
PATCH_FORMAT_VER 2
VULN_ID 4051
PLATFORM win64


patchlet_start
 PATCHLET_ID 1
 PATCHLET_TYPE 2
 PATCHLET_OFFSET 0x6F5CB
 PIT rpcrt4.dll!RpcImpersonateClient
 code_start
  xor ecx, ecx ; Impersonating the client that made the request
  call PIT_RpcImpersonateClient
 code_end
patchlet_end


patchlet_start
 PATCHLET_ID 2
 PATCHLET_TYPE 2
 PATCHLET_OFFSET 0x6F81E
 JUMPOVERBYTES 6 ; We eliminate the 6-byte call to RevertToSelf
 code_start
  nop
 code_end
patchlet_end


patchlet_start
 PATCHLET_ID 3
 PATCHLET_TYPE 2
 PATCHLET_OFFSET 0x6F844
 PIT rpcrt4.dll!RpcRevertToSelf
 code_start
  call PIT_RpcRevertToSelf
 code_end
patchlet_end


Micropatch In Action

This video shows our micropatch in action.





Frequently Asked Questions


Q: Which Windows versions does this micropatch apply to?

Currently we have instances of this micropatch applying to:
  1. fully updated 64bit Windows 7 [added on 9/6]
  2. fully updated 64bit Windows Server 2008 [added on 9/6]
  3. fully updated 64bit Windows 10 version 1607 [added on 9/4]
  4. fully updated 64bit Windows 10 version 1709 [added on 9/5]
  5. fully updated 64bit Windows 10 version 1803
  6. fully updated 64bit Windows Server 2016 
  7. fully updated 64bit Windows Server 1803 [added on 9/6]
[Update 9/6/2018] Big thanks to Will Dormann for confirming the vulnerability as well as effectiveness of our micropatch on Windows Server 1803 in a real-time Twitter DM session!

We can quickly port the micropatch to other affected versions but we'll only do that on request (support@0patch.com). As far as we know at this point, the vulnerability was confirmed to also be present and exploitable on 32bit Windows 10 and 32bit Windows 7, so it's safe to assume that at least all Windows versions from Windows 7 and Windows Server 2008 are likely affected.


Q: Will modifying the exploit allow attackers to bypass this micropatch?

No, and that's one of the significant advantages of changing the code compared to signature- or behavior- based exploit prevention systems. For instance, while most antivirus products will detect the original POC by now, Will Dormann modified the POC and showed that it went undetected. Such modifications always allow for bypassing detection-based systems, while fixing the code actually removes the vulnerability. There is simply nothing there to bypass. There is no more efficient and reliable way to address a vulnerability than to actually remove it. (Although the entire industry built around vulnerabilities will try to convince you otherwise.)   


Q: How do we apply this micropatch?

If you have 0patch Agent already installed, this micropatch is already downloaded and applied so you don't have to do anything. Otherwise, download and launch the 0patch Agent installer, create a free 0patch account and register the agent to that account. You will immediately receive all micropatches including this one, and it will automatically get applied to Task Scheduler.


Q: Is this patch functionally identical to how Microsoft will fix it?

Obviously we can't know that. As we always claim, the original vendor - with their internal knowledge of the product - is best-positioned to correct their own code. Nobody else knows all the possible side effects of a code change as well as they do (granted, with large products even they often don't see everything) and in an ideal world software vendors would be issuing micropatches like this to quickly and painlessly fix vulnerabilities. That said, Microsoft may do the same as we did, but they may also prevent Task Scheduler from changing permissions on hard links. Or they may find that they need to support hard links and not impersonating the user is essential for some other operation that Task Scheduler performs - and will make a substantial code change. We often create micropatches after the vendor has issued the official update, which allows us to see what they did and ideally replicate their logic in a micropatch. With a 0day, this is obviously not possible.

You should therefore consider our micropatch a temporary solution while waiting for the official fix.  



Q: What will happen on Patch Tuesday?

When Microsoft makes their official fix available, you simply apply it as you would if you had never heard of 0patch. Applying it will automatically obsolete this micropatch on your computer as the update will replace a vulnerable executable with a fixed one, thereby changing its cryptographic hash. Since our micropatches are associated with specific hashes, this will make the micropatch inapplicable without intervention on either your end or ours. 


Q: Can we keep using this micropatch instead of applying Microsoft's update?

We strongly recommend against that. Microsoft's update will not only fix this issue in a more informed way, but will also bring fixes for other vulnerabilities that we don't have micropatches for. Yes, we hate losing hours of our lives to updating our systems too, but wouldn't dream of outright replacing official updates with our micropatches ;)


Q: How can you provide a micropatch so quickly compared to original vendors?

While having a micropatch candidate ready 24 hours after a 0day was dropped is quick relative to today's standards of software patching, a couple of things must be considered:
 
  1. Software vendors know their products much better than we do, and are likely to create a more comprehensive code fix than we can without their knowledge and source code. That takes more time than writing a micropatch.
  2. Software vendors bundle numerous fixes together in a "fat update" that replaces entire executables, which requires a lot more testing across the board. We test our micropatches with focused tests targeting only the patched code.
  3. "Fat updates" are a huge problem for users and vendors when something goes wrong, which is why software vendors are even more wary of issuing a defective update. Of course a micropatch also can be flawed, but it can be revoked remotely and instantly replaced with a corrected version without users ever noticing anything. That said, we will always have "fat updates" for substantial functional changes, we're just arguing that we may not need them this frequently because most vulnerabilities could be patched with micropatches. 
  4. Software vendors must issue patches for all supported versions, and extensively test all of them. We currently only have this micropatch for two three four six seven affected Windows versions. Nevertheless, porting to other versions, basic testing and deployment would take us about two hours of effort for each additional version, so that could still be done in one day.    

All that said, comparing our speed with software vendors' must also account for the difference in our deliverables. A micropatch can be quickly created, deployed to all computers in a hour's time and applied without even the slightest disturbance to users. But it must be considered a temporary security measure until the official patch can be applied.


Q: What should we do if we encounter problems with Task Scheduler after applying this micropatch?

Obviously we can't guarantee that our micropatch won't cause some unwanted side effects, e.g., with non-admin users editing existing scheduled tasks under certain circumstances. (Then again, software vendors can't guarantee that either.) The rule of thumb for using 0patch (or any other 3rd party behavior-changing product like antivirus or malware blockers) should be to first disable the agent and see if the problem persists, before contacting the original software vendor for the affected product. If the problem persists, the culprit is unlikely to be the micropatch. If the problem goes away, it's probably us and we'd like to hear from you at support@0patch.com.

Fortunately, in contrast to standard "fat update" patching that software products employ today, 0patch allows you to instantly revert a patch with a click of a button.



Cheers!

@mkolsek
@0patch

Wednesday, May 30, 2018

0patching Foxit Reader Buffer... Oops... Integer Overflow (CVE-2017-17557)

by Luka Treiber, 0patch Team


https://0patch.com


In April, Steven Seeley of Source Incite published a report  of a vulnerability in Foxit Reader and PhantomPDF versions up to 9.0.1 that could allow for remote code execution on a target system. Public release of this report was coordinated with an official vendor fix included in the April's Foxit Reader and PhantomPDF 9.1. release.

According to our analysis the PoC attached to the report triggers a heap-based buffer overflow in a Bitmap image data copy operation inside ConvertToPDF_x86.dll module using an overlong biWidth attribute.

When dropping SRC-2018-0009.bmp into Foxit Reader we immediately got a crash and inspected it by hooking WinDbg with Page Heap enabled.


(3250.3480): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\Program Files (x86)\Foxit Software\Foxit Reader\Plugins\Creator\x86\ConvertToPDF_x86.dll -
eax=00000004 ebx=00000000 ecx=00000008 edx=15db5ef8 esi=15db9f38 edi=15dc2000
eip=5f5f9d17 esp=1866f904 ebp=1866f920 iopl=0         nv up ei pl nz na po cy
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010203
ConvertToPDF_x86!CreateFXPDFConvertor+0x1f42e7:
5f5f9d17 8807            mov     byte ptr [edi],al          ds:002b:15dc2000=??



It first looked like a typical buffer overflow, where a missing boundary check allows data to be written over the edge of destination buffer addressed by edi. (Note: the crash offset is marked in red.)






But the loop of the copy operation is constrained by checking biWidth (at esi+54h) which is read from Bitmap image header. So why is there an access violation despite this check?

When inspecting that buffer's properties something stuck out: an unusually small buffer size was reported by !heap to have been allocated, specifically just 4 bytes (UserSize in the WinDbg output below).


0:012> !heap -p -a edi
    address 15dc2000 found in
    _DPH_HEAP_ROOT @ 157e1000
    in busy allocation ( DPH_HEAP_BLOCK:  UserAddr  UserSize -  VirtAddr  VirtSize)
                               15ce264c:  15dc1ff8         4 -  15dc1000      2000
    65688e89 verifier!AVrfDebugPageHeapAllocate+0x00000229
    7748103e ntdll!RtlDebugAllocateHeap+0x00000030
    7743abe2 ntdll!RtlpAllocateHeap+0x000000c4
    773e34a1 ntdll!RtlAllocateHeap+0x0000023a
    5f564b50 ConvertToPDF_x86!CreateFXPDFConvertor+0x0015f120
    5f6de073 ConvertToPDF_x86!CreateFXPDFConvertor+0x002d8643
    5f6ddead ConvertToPDF_x86!CreateFXPDFConvertor+0x002d847d
    5f6de124 ConvertToPDF_x86!CreateFXPDFConvertor+0x002d86f4
   *5f5f9975 ConvertToPDF_x86!CreateFXPDFConvertor+0x001f3f45
    5f5d8598 ConvertToPDF_x86!CreateFXPDFConvertor+0x001d2b68



Inspecting SRC-2018-0009.bmp in a hex editor revealed that biWidth is set to a huge value of 40000001h - a remotely controllable attribute in case the image used in conversion to PDF was obtained from the attacker. So if biWidth was used in calculation of destination buffer size before buffer allocation, that calculation was probably prone to integer overflow in order to result in so much lower a value (4).

Besides heap block properties, !heap also printed out the buffer allocation call stack. We inspected that and indeed we found an overflow-prone buffer size calculation right before the orange-marked return address in the above call stack. The code graph below shows only a part of the vulnerable execution path, but the omitted code is very similar so it suffices for our explanation. There are three code blocks; edi represents biWidth read from image data in the first block, in the third block eax is the destination buffer size to be allocated, ecx represents biBitCount (number of bits per pixel).





There are 3 instructions that can overflow in the last code block:

  1. imul eax, ecx - in case of SRC-2018-0009.bmp, eax=40000001h and ecx=4 so this is the operation that overflows (result is 00000001`00000004h but eax can only hold the lower DWORD - 00000004h)
  2. add eax,ebx - addition of 1Fh to a potentially huge number held by eax could overflow in case previous multiplication didn't overflow
  3. add eax,edx - addition of up to 1Fh (edx is and-ed to 1Fh beforehand) to a potentially huge number held by eax could overflow

As already said, this is not the only vulnerable code block before the destination buffer allocation. Depending on biBitCount value that can hold at most 20h two other similar buffer size calculations can occur. In order to fix all of them, many checks would have to be inserted so we decided for a more compact solution. Given that all constraints to the buffer size calculation are known - buffer size can not theoretically exceed 0xffffffff, biBitCount can be at most 20h and two potentially added values are at most 1Fh -, the maximum valid biWidth could be calculated beforehand as follows:

(0xffffffff-0x1f-0x1f)/0x20 = 0x07fffffe

However, one of the vulnerable blocks does not properly handle signed values so this also needs to be taken into account by halving the maximum buffer size to 0xffffffff/2 = 0x7fffffff. Once we do that, the add eax,edx instruction can't overflow because edx is the sign extension (mind the cdq instruction) and will always be 0. So the final constraint calculation goes like this:

(0x7fffffff-0x1f)/0x20 = 0x03ffffff

Knowing this, a single check can be placed right before biWidth is first used - at the first block of the code graph above - that makes sure only biWidth values lower than 0x03ffffff can pass. If this condition is not met, we can set biWidth to 0 so the subsequent jle would raise a handled exception and abort further processing of a malformed image. Here is the patch code that does this:


; CVE-2017-17557 patch for Foxit Reader 9.0.1 
MODULE_PATH "C:\0patch\Patches\CVE-2017-17557\ConvertToPDF_x86.dll"
PATCH_ID 1000031
PATCH_FORMAT_VER 2
VULN_ID 3556
PLATFORM win32

patchlet_start
PATCHLET_ID 1
PATCHLET_TYPE 2
PATCHLET_OFFSET 0x2098d9
JUMPOVERBYTES 3
N_ORIGINALBYTES 2

code_start
    mov edi, [esi+54h]      ; original instruction replaced by jump to this
                              patchlet
    cmp edi, 03ffffffh      ; is edi<(7fffffffh-1fh)/20h?
    jb skip                 ; if so, everything's okay 
    call PIT_ExploitBlocked ; otherwise, we had an integer overflow - 
                              show the popup
    xor edi,edi             ; and annulate biWidth so the subsequent logic 
                              will throw a handled exception
    skip:
    ;test    edi, edi       ; original instruction displaced to the trampoline
code_end
patchlet_end


When this patch is applied to Foxit Reader's memory, the first two instructions from the top code block above (mov edi, [esi+54h] and test edi, edi, making up exactly 5 bytes) are replaced by a 5-byte jump to the patch code, forcing us to repeat the first instruction (mov edi, [esi+54h]) at the beginning of our patch code if we want to inject our code between the two instructions. The second instruction (test edi, edi) is automatically placed after the patch code by 0patch Loader because JUMPOVERBYTES 3 directs it to only omit the first instruction (3 bytes) while keeping the remaining 2-byte instruction.

This video shows our micropatch in action.




This micropatch has already been published and distributed to all installed 0patch Agents. If you're using Foxit Reader or Foxit PhantomPDF version 9.0.1.1049, you can download our free 0patch Agent, create a free 0patch account and register the agent to that account to immediately receive this micropatch and have it applied to your Foxit software.

If you're using some other version of Foxit Reader and would like to have micropatches for it, please contact us at support@0patch.com.


Tuesday, May 15, 2018

Windows Updates Broke Your Networking? Free Micropatches To The Rescue (CVE-2018-8174)

A Single-Instruction Micropatch For a Critical Remote Execution Issue

by Mitja Kolsek, 0patch Team

Last week, Microsoft issued an update resolving (among others) a critical remote code execution issue in VBScript Engine named CVE-2018-8174, exploit for which has previously been detected in the wild.

Unfortunately, Microsoft's update breaks networking on some computers (details below), prompting users to avoid their application. Windows updates are "all or nothing" these days, so users can't just remove a defective KB and enjoy the protection provided by other KBs issued on the same Patch Tuesday. Fortunately, micropatching provided by 0patch is the exact opposite of that, addressing each vulnerability individually. Let's start with the exploit and the underlying vulnerability.   

It appears the exploit has been caught by at least two security firms, Kaspersky and 360, and they both issued detailed analyses of the infection chain and, more importantly for us, the vulnerability itself [Kaspersky's analysis, 360's analysis]

The vulnerability is triggered by this simple proof-of-concept provided by Kaspersky:


Dim ArrA(1)
Dim ArrB(1)

Class ClassVuln
    Private Sub Class_Terminate()
        Set ArrB(0) = ArrA(0)
        ArrA(0) = 31337
    End Sub
End Class

Sub TriggerVuln
    Set ArrA(0) = New ClassVuln
    Erase ArrA
    Erase ArrB
End Sub

TriggerVuln


By simply saving the above code to poc.vbs and double-clicking it on a Windows computer without May 2018 updates installed, we trigger the vulnerability and get wscript.exe to crash in oleaut32.dll's VariantClear function .


Access Violation in OLEAUT32.DLL's VariantClear due to accessing an unallocated memory address

 

Let's see what goes on here. We create two fixed-size arrays ArrA and ArrB with one element each.
A ClassVuln object is created and assigned to the first element of the ArrA array, then the ArrA array is erased. There is just one element in the array, namely the ClassVuln object, and Erase sets ArrA(0) to Nothing, which removes all references to the object and triggers its destruction.

Our object has a weird method called Class_Terminate. This is a class event that's not officially supported in VBScript anymore but apparently still works and gets called upon object's destruction. (If you're wondering why unsupported functionality still works it's probably because removing it would break an unknown number of production scripts that are using it, and cause much more headache than keeping it and removing it from documentation.)

In our Class_Terminate, the first element of ArrB is set to ArrA(0) - but wait, ArrA(0) is the very object being destroyed, and you can imagine where this is heading. This assignment increases the reference ("lock") count for the object, but the following instruction (ArrA(0) = 31337) decreases the same reference count, which will lead to the object being actually destroyed.

However, after being destroyed, there will still be a reference to this object in ArrB(0), which is called a "dangling pointer," i.e., a pointer to a memory block that has already been freed. In this case the object was allocated on the heap, so now ArrB(0) has a reference to some location on the heap, and those versed in exploiting "use after free" issues have a nice starting point to make use of this reference.

We're not interested in exploiting this issue, however, but in patching it. To do that, we first need to understand the vulnerability.

Kaspersky researchers pointed their fingers at the VBScriptClass::Release function, which only checks the object reference count before calling VBScriptClass::TerminateClass (this executes our Class_Terminate code), and doesn't account for the case where Class_Terminate would increase the object reference count. So even though the reference count was increased (by Set ArrB(0) = ArrA(0)), the object still gets destroyed.

We're not sure that this is the (entire) root cause, although we would probably try to fix the issue using this avenue if we didn't have Microsoft's official patch that contains information on how they fixed it.

After applying the May 2018 Windows updates, executing the PoC has a different result:


Patched Windows show a runtime error

The error states: "Object required: 'ArrA(...)'" in line 6, indicating that the assignment of ArrA(0) is not possible as ArrA is not an object (anymore).

First we decided to compare code execution between the patched and the unpatched version using WinDbg's wt (trace and watch data) command. Fortunately symbols are available for oleaut32.dll so the traces looked fairly identical up to the point where the patched version "derailed" from the unpatched version's path towards crashing. It turned out the fork happened in function vbscript!AssignVar, executed as a result of assigning ArrA(0) to ArrB(0). In this function (which hasn't been modified by the patch), a test is being made on the vt (variant type) member of a VARIANT structure, which in our case denotes the ArrA(0) element. And it turns out on the unpatched computer, vt is 9 (VT_DISPATCH) while on the patched computer, it is 0 (VT_EMPTY).

This means someone must have changed the value of vt from 9 to 0 along the way. In order to find out who did that, we placed an access breakpoint to the address of vt sometime earlier in the execution where it was still 9 on the patched computer.

Bingo! We found a "mov word [rdi], bp" instruction in our old friend, the VariantClear function, which sets our vt to 0. (rdi points to the VARIANT structure, and ebp is 0 throughout the function.)

It was time for BinDiff. We compared the patched and the last unpatched version of oleaut32.dll (which is where the crash occurs), and looked at the modified functions. Unsurprisingly, VariantClear was slightly modified in several places (orange blocks on the image below). Three of these blocks set the vt (variant type) member of a VARIANT structure to 0, which means VT_EMPTY, or an empty object.



Left: unpatched code; Right: patched code





Our PoC executes the middle orange block, and that block contains - in its patched version - the instruction that sets vt to 0.

So we created a micropatch that injects a logically identical instruction to the vulnerable code. Since the vulnerable code uses rbx as the pointer to the VARIANT, this is what we needed to inject:

mov word [rbx], 0

And this is the source code for our micropatch:


; Patch for CVE-2018-8174 in oleaut32.dll version 6.1.7601.23775 64bit
MODULE_PATH "c:\windows\system32\oleaut32.dll"
PATCH_ID 321
PATCH_FORMAT_VER 2
VULN_ID 3568
PLATFORM win64

patchlet_start
 PATCHLET_ID 1
 PATCHLET_TYPE 2
 PATCHLET_OFFSET 0x000011bf

 code_start

  mov word [rbx], 0 ; Set VARIANT's vt to 0 (VT_EMPTY)

 code_end

patchlet_end


Sure enough, applying this micropatch stopped the wscript.exe from crashing and made its behavior identical to the officially patched version:

The old version with our micropatch has an identical reaction to our PoC as the new version.


Our micropatches for this vulnerability have been labeled ZP-320 and ZP-321 for 32-bit and 64-bit version of oleaut32.dll respectively, and are applicable on Windows 7 and Windows 2008 Server updated up to April 2018 Windows updates. Why only these versions? Well, updates KB4103718 and KB4103712 are reportedly causing networking problems on some computers, which prompts users to delay their application - remaining vulnerable to the issue we have micropatched here. We have reports of Windows 7 and Windows 2008 Servers being affected, but if you or someone you know is experiencing this issue on other Windows versions, just ping us and we'll quickly port these micropatches.

As always, if you have 0patch Agent installed, the above-mentioned micropatches should already be present and applied on your system. If not, you can download a free copy of 0patch Agent to protect your system from CVE-2018-8174 at least until Microsoft resolves the functional problems with their updates.
 

Cheers!

@mkolsek
@0patch