Wednesday, October 25, 2017

0patching the Office DDE / DDEAUTO Vulnerability... ehm... Feature

When "Dynamic Data Exchange" Becomes "Dynamic Data Execution"


by Mitja Kolsek, the 0patch Team

Introduction

Two weeks ago SensePost's Etienne Stalmans and Saif-Allah El-Sherei published an interesting analysis of a Microsoft Office feature that can be easily exploited for running arbitrary code on user's computer. In the next few days, Cisco Talos reported of detected in-the-wild attacks exploiting this very issue, while SANS reported it being exploited by Necurs and Hancitor malware campaigns. Endgame's Bill Finlayson and Jared Day subsequently wrote an excellent root-cause analysis of this issue.

The feature in question is called Dynamic Data Exchange, introduced in early Windows systems but still used in many places today. For instance, if you want to see the revenue value from a specific cell in your financial statement Excel worksheet mirrored in a Word document - so that updating the worksheet reflects itself in Word, you can use DDE for that. The field that does that would look like this:

{ DDEAUTO Excel "statement.xlsx" revenue }

The "AUTO" in DDEAUTO instructs Word to automatically use DDE for updating the external value when the document is opened. Instead of DDEAUTO, one can use DDE, but it will require the user to manually trigger updating of the value.

The documentation on DDEAUTO and DDE fields describes their behavior, including that "the application name shall be specified in field-argument-1; this application must be running." But what happens when the application (e.g., Excel from the above example) is not running? This happens:



Word helpfully offers to launch the application for you, which is undoubtedly user-friendly. It is also where a feature turns into a vulnerability: the application name can be a full path to a malicious executable (on USB drive or network) or to a benign executable that will do malicious things based on the arguments it is provided. So when the user agrees to Word launching the DDE application, attackers code gets executed on his computer.

According to SensePost, "Microsoft responded that as suggested it is a feature and no further action will be taken, and will be considered for a next-version candidate bug." Microsoft is full of smart people who try very hard to keep their users secure (we personally know many of these people) and we believe they have good reasons for such decision - or for subsequently reverting it in case that should happen. In case of DDEAUTO (which is the better attack vector), the user has to provide two non-default answers in popup dialogs, so even if these dialogs are not security warnings, some amount of social engineering would certainly be required in an attack. While we're seeing reports of this feature being used by attackers, we currently don't have any data on how successful they are.

Regardless of Microsoft's position on this, both defense and offense sides sprung into action. The former started looking for mitigations and creating signatures and detection rules for blocking attacks, while the latter kept coming up with new attack vectors (Outlook email, Calendar invites) and ideas on how to bypass detection. A fun game to play, no doubt, but we already know that offense will always be winning. Putting guards around the hole can ever only slow the attackers down, as they will always find ways to bypass them. The only definitive way to prevent the hole from being exploited is to close it. In code. And that's what we do.


Analysis

The entire problem seems to stem from Word deviating from the documentation and helpfully attempting to launch the application named in a DDE or DDEAUTO field. This is implemented by calling CreateProcess with the provided application name, which covers two cases:

  1. If application name is a full path to an executable, such as "C:\\Windows\\System32\\cmd.exe", that executable is launched with arguments provided in the DDE/DDEAUTO field;
  2. If application name is not a full path, such as "Excel", CreateProcess starts looking for it in the system search path, starting with the current working directory, followed by three system folders, and ending with all locations specified in the PATH environment variable.

This has two security-related implications. One can obviously just specify a full-path malicious executable, even from a network path, and have it executed on user's computer. (Note that a network path can be on the Internet behind user's firewall, and Windows with the default-running Windows Client Service will download the executable via HTTP - it will just take longer.)

Less obviously,  there is a binary planting potential here: Word sets its current working directory to user's Documents folder upon launching (and let's assume the attacker can't plant his malicious executable there), which is good. However, the user can inadvertently change the current working directory by opening a file via File Open dialog, which happens by default. So an attacker could trick the user to open a Word document from his network share or USB key, which would set Word's current working directory to attacker-controlled location. Subsequently, a DDEAUTO field could launch a malicious executable without providing a full path to it.

We described this second attack vector to explain why simply blocking DDE/DDEAUTO fields that use a full path would not be enough to fully neutralize exploitation. In fact, this shows that even a benign case of {DDEAUTO Excel "statement.xlsx" revenue} in a trusted (even signed!) document could be used to launch malicious Excel.EXE from attacker's location.

DDE-related application launching in Word is apparently pretty dangerous, and we were tempted to put some security around it. But the more we thought about what to do, the more it became clear that it's impossible to distinguish between a malicious and benign use of DDE/DDEAUTO. We could disallow any forward- and back-slashes in the application name, but as shown above, even single-word application names can be misused. We could also change the current working directory to a safe location before CreateProcess gets called (and we actually already had a patch candidate doing that) but what is a universally safe location on millions of differently-configured computers worldwide?

Then we decided to do some functional testing, and discovered that we were unable to get Word (either 2010 or 2013) to properly launch the requested application at all in any meaningful way. For instance, the following did launch Excel (with binary planting concerns aside):

{ DDEAUTO Excel " " }


But that's clearly useless, as no file and range are specified. However, the following, while useful, did not launch Excel. Process Monitor showed a single attempt to launch excel.exe from user's Documents folder (probably because it was the current working directory):

{ DDEAUTO Excel "statement.xlsx" revenue }

In contrast, the above worked great for getting the revenue value from statement.xlsx when the latter was already opened in Excel on the same computer. And that was it. We decided to simply amputate the app-launching capability for the purpose of DDE.


Patching

We located the code block with the CreateProcess call in wwlib.dll, removed the call and simulated a failed CreateProcess call by putting 0 in eax. After creating a patch like this, it turned out that Word still launched the specified application. What was happening? It turned out there is a fallback mechanism in place that tries to launch the app again from mso.dll if CreateProcess in wwlib.dll fails. Word apparently really tries to help the user launch the app.

So we also removed the fallback code. The image below shows the relevant code graph, including the CreateProcess and Fallback blocks, which we effectively amputated.








Our first micropatch was for 64-bit Word 2010 with the following source code:


; Patch for WWLIB.DLL_14.0.7189.5001_64bit.dll
MODULE_PATH "C:\Program Files\Microsoft Office\Office14\wwlib.dll"
PATCH_ID 294
PATCH_FORMAT_VER 2
VULN_ID 3081
PLATFORM win64

patchlet_start
 PATCHLET_ID 1
 PATCHLET_TYPE 2

 PATCHLET_OFFSET 0x92458c ; at the beginning of the CreateProcess block
 JUMPOVERBYTES 68 ; jump over everything including CreateProcess call

 code_start

   mov eax, 0 ; we say that CreateProcess failed to prevent original code
              ; from tryig to close the handle of the non-existent process

 code_end
patchlet_end

patchlet_start
 PATCHLET_ID 2
 PATCHLET_TYPE 2

 PATCHLET_OFFSET 0x92463f ; at the beginning of the fallback block
 JUMPOVERBYTES 40 ; remove the entire block
 N_ORIGINALBYTES 5

 code_start

   nop ; no added code here, we just need to remove the original code

 code_end
patchlet_end



After applying this micropatch, Word no longer attempts to launch the application specified in DDE and DDEAUTO fields. The dialogs remain the same, and Word still asks if your want to launch the application - but even if you agree, the application doesn't get launched and Word proceeds as if the attempt to launch it has failed (notifying you that it cannot obtain DDE data).


Demonstration





Conclusion

We created micropatches for Office 2007, 2010 ,2013, 2016 and 365 (32-bit and 64-bit builds). To have micropatches applied, all you need to do is download, install and register our free 0patch Agent. (Glitch warning: due to being sandboxed in Word 2016 and 365, 0patch Console either shows an empty line for Word in the Applications list with only the on/off button, or doesn't show Word at all. While said button works as it should, it looks confusing. We're working on fixing this by end of our beta.)


Note that we only make micropatches for fully updated systems, so make sure to have your Office updated (latest service pack plus all subsequent updates from Microsoft) if you want to use them. We'll be tweeting out notifications about additional micropatches so follow us on Twitter if you're interested.

Our micropatch can co-exist with existing mitigations for this same issue, so you can still disable automatic updating of DDE fields (either manually, or using Will Dormann's collection of Registry settings for various Office versions) and use malware detection/protection mechanisms - although the latter will add no value once the hole has been closed.

If active exploitation of this feature continues to spread, Microsoft will likely respond with an update or mitigations. When they do, make sure to apply them - as the original vendor they know their code best, and they are also aware of many use cases none of us could think of.

Should you experience any problems with our micropatches, please let us know. It's unlikely that our injected code would be flawed (being a single CPU instruction), but you may have a DDE use case that doesn't agree with our solution. Send us an email to support@0patch.com if you do. One great thing about micropatches is that in addition to getting applied in-memory while applications are running, they can also get revoked in-memory. So we can revoke a micropatch and issue a better version without your users even knowing that anything happened. Zero user disturbance.

Finally, let's quickly rehash the benefits of micropatching:

  1. A micropatch, just like original vendor's update, actually closes the hole instead of putting guards (signature-based detection or prevention) on attacker's paths towards the hole.
  2. In contrast to typical vendor updates, which replaces a huge chunk of a product, a micropatch brings a minuscule change to the code on the computer. This means minimal possible risk of error, as well as ability for everyone to actually review the new code (anyone familiar with assembly language can quickly understand the source code above).
  3. A micropatch gets instantly applied in memory, even while the vulnerable application is running, so the user doesn't have to restart the application or even reboot the computer.
  4. If something is wrong with a micropatch (while the risk is minimal, it's still there), it can be just as instantly removed from running applications, and replaced with a corrected one. Again, users don't even notice anything. (You want them to focus on their work, not on security updates, don't you?)
  5. With the low risk of error and the ability to quickly apply and un-apply micropatches, you can afford to simplify your updating process. Except in the most critical of cases, you don't need to test a micropatch for weeks or months before applying it, as the cost of un-applying is approximately zero if you can do that from a central location (we're currently working on that, by the way). In comparison, what was your cost of un-applying a "fat update" from thousands of computers the last time something went wrong with an update? 
We'll probably always have "fat updates", as there will always be a need for significant functional changes and new features. But fat updates are really not fit for patching vulnerabilities - these need rapid deployment, not weeks or months of testing in user environments that keeps the window wide open for attackers even though official patches are available. Micropatching is the only approach we're aware of that can change that. And that's why we're doing it.

Keep your feedback coming! Thank you!

[Update 3/11/2017: Windows 10 introduced a new mitigation called Attack Surface Reduction with the Fall Creators Update. We took a look at how it helps block DDE-borne attacks and how it compares to our micropatches.]

Wednesday, October 4, 2017

Micropatching a Hypervisor With Running Virtual Machines (CVE-2017-4924)

The Now and the Future of Hypervisor Patching

by Luka Treiber and Mitja Kolsek of 0patch Team


Introduction

Those of you following our micropatching initiative already know that micropatching makes it possible to fix vulnerabilities without restarting the computer or even relaunching the patched application that a user might currently be using. In other words, no disruption for users and servers.

Now let's take it a step further. Do you know which component of an IT environment one least wants to restart? You guessed it: a hypervisor. Especially if dozens or hundreds of critical virtual machines are running, which all need to be stopped or suspended, and can't get back online until the hypervisor is patched. And then if the patch turns out to be broken... you get the picture - and it's not a pretty one.

When the next Heartbleed, Shellshock, or a "guest-to-host escape" vulnerability comes out, you can be pretty sure that hypervisors all around the World will get massively patched - and restarted. And lots of people, from hypervisor vendors to CIOs, admins and end users, will go through various levels of unhappiness.

A few weeks ago, Comsecuris published a detailed report on three vulnerabilities in VMware Workstation that allowed a malicious guest to cause a memory corruption in the hypervisor (vmware-vmx.exe) running on host (and we all know what that leads to). Nico and Ralf cleverly patched a guest component of VMware's graphics-related DLL in a guest machine to make it send a malformed data structure to the hypervisor - which then crashed because it lacked the sanitization checks. (Interestingly, the debug version of the hypervisor did have assert statements with these checks, and these turned out to be quite helpful for both vulnerability analysis and patching.)

All three Comsecuris' vulnerabilities have been patched by VMware, two in Workstation 12.5.5, and one in Workstation 12.5.7. We decided to write a micropatch for the latter to show how a hypervisor can be patched without stopping virtual machines running on top of it.


Reproducing the PoC

We reproduced the Comsecuris' "dcl_resource" PoC on a 64-bit Windows 10 machine running on VMware Workstation 12.5.5. (Fun fact: you can run a VMware Workstation inside another VMware Workstation.)

For those of you who might want to play with this PoC: install Python for Windows and Visual C++ 2015 Redistributables, place PoC files in a folder, launch exec_poc.py and wait for the virtual machine to crash. If it doesn't crash (as it didn't for us at first), make sure your virtual machine's Hardware Compatibility is set to "Workstation 12.x" or something else reasonably new, otherwise DirectX 10 will not be supported, and the PoC relies on that.


Vulnerability Analysis

Once we got it working, the PoC crashed the vmware-vmx.exe running our virtual machine. We attached a debugger but even though Comsecuris' report was quite detailed we found it hard to match our crash context to their analysis, as there is a lot of convoluted code there to look at.

So using a hint from Comsecuris' report, we replaced vmware-vmx.exe with vmware-vmx-debug.exe to use the debug version of the hypervisor, and repeated the procedure. The result was this:





An assert message popped up revealing the assert's source code line. It seemed odd that an exploit that apparently hasn't been envisioned in the release version would be stopped by an assert, but hey, it looked really promising.  So we searched the disassembly for shaderTransSM4.c string and the hex equivalent of 1856 (the line number) - 740h. And found a match (lower left orange block):




When scrolling up the code graph we got a view of a whole chain of asserts originating in a case clause named case 88 (upper right orange block):




Next we disassembled the release vmware-vmx.exe (the one that crashed) and found a matching case 88 clause there - but no checks resembling the assert chain which we found in the debug version.

We then made a diff with the fixed release 12.5.7 version of vmware-vmx.exe and found the exact same cascade of checks following the case 88 clause. So VMware developers apparently took the assert statements form the debug version and turned them into actual release version checks. The image below shows the patched case 88 code branch on the left, and its vulnerable match on the right.




The last block in the cascade (dark red) routes either to the green default case clause (also present in the vulnerable version on the right) or to a red block on the left which directs execution towards an error handler if rbp+1A8h points to a value of 80h or greater. That dark red block was the one that stopped the PoC. Rereading the original report also revealed a parallel to our conclusion. It said the disclosed vulnerabilities "were fixed with the exact same code as in the debug version".


Writing a Micropatch

With that information in our hands we could create a micropatch. We chose to set our patch location one instruction before the last jmp instruction in the grey box - on mov [r12+1Ch], ecx. We could theoretically inject it after the jmp but 0patch Agent currently does not support patching a relative jmp instruction (it will in the future). In the patch we implemented an equivalent of the buffer overflow check from the dark red block above; we only had to replace rbp+1A8h with an equivalent that worked in our context: r12+50h. If an attempted overflow is detected, our patch code calls up an "Exploit Attempt Blocked" dialog, sets rcx to string "0patch: Exploit Blocked for CVE-2017-4924!", and jumps to the error handler in the original code that writes our custom message string to vmware.log and terminates the processing of the malformed data structure.

So the nice thing about this patch is not only that it fixes the vulnerability without disrupting running virtual machines, but it also records an error in VMware Workstation's log for subsequent inspection.

This is the resulting 0pp file, the source code for our micropatch.



MODULE_PATH "..\AffectedModules\vmware-vmx.exe-12.5.5.17738\vmware-vmx.exe"
PATCH_ID 291
PATCH_FORMAT_VER 2
VULN_ID 2971
PLATFORM win64

patchlet_start
 PATCHLET_ID 1
 PATCHLET_TYPE 2
 PATCHLET_OFFSET 0x0021223f
 PIT vmware-vmx.exe!0x002122b5

 code_start
  cmp dword[r12+50h], 80h ; r12+50h equals rbp+50h in 12.5.5. debug
                          ; and rbp+1A8h in 12.5.7. release

  jb skip
  call PIT_ExploitBlocked
  call arg1                   ; push string
  db "0patch: Exploit Blocked for CVE-2017-4924!",0Ah,0;
 arg1:
  pop rcx
  jmp PIT_0x002122b5          ;jump to the error handler @ loc_1402122A3+12
 skip:
 code_end
patchlet_end


Going Live

See a video of our micropatch in action. As you can see after we demonstrate the crash, we patch vmware-vmx.exe while the virtual machine is running, and the PoC gets blocked.



Closing Remarks

We wanted to demonstrate how patching a vulnerability in a hypervisor could look like in the future: instant, without disturbing the running virtual machines, strictly targeted at a particular vulnerability (as opposed to replacing megabytes of code) and also instantly un-patchable in case of a flawed patch. While clearly VMware Workstation is unlikely to host critical machines that are costly to stop or suspend, this same vulnerability also affected ESXi  - which is running thousands of just such machines around the World. Unfortunately we can't micropatch ESXi (yet) but if there's interest, there is no reason why that couldn't be done.

You might have noticed that our patch only addresses the exact flaw demonstrated by Comsecuris'  PoC, while the debug hypervisor version has a cluster of assert statements, each likely to be triggered on a different invalid value. So an exploit writer could probably walk through these assert statements and compile PoCs for additional cases not addressed by our micropatch. That said, we're not here to create new PoCs, and we only make micropatches for vulnerabilities we can prove (i.e., for which we have a PoC for). If VMware was using micropatching, they could have easily implemented all these checks as micropatches (or even a single micropatch, albeit a bit larger than usual).

We're hoping to get the idea of micropatching to all product development groups who know that applying patches can be really costly for their users - and want to do something about it.

Finally, thanks to Nico Golde of Comsecuris for helpful hints on getting their PoC to work, and useful ideas about patching.

If you have 0patch Agent installed (it's still free!), all the magic is already there: this micropatch is already on your computer and is getting automatically applied whenever you launch VMware Workstation 12.5.5. Contact us if you want to have this same patch for some other version of  VMware Workstation.

@0patch