Friday, November 17, 2017

Did Microsoft Just Manually Patch Their Equation Editor Executable? Why Yes, Yes They Did. (CVE-2017-11882)

And They Did an Absolutely Stellar Job

by Mitja Kolsek, the 0patch Team


A Pretty Old Executable

The recent Patch Tuesday brought, among other things, a new version of "old" Equation Editor, which introduced a fix for a buffer overflow issue reported by Embedi.

The "old" Equation Editor is an ancient component of Microsoft Office (Office now uses an integrated Equation Editor), which is confirmed by looking at the properties of the unpatched EQNEDT32.EXE:




We can see that File version is 2000.11.9.0 (implying being built in 2000), while Date modified is in 2003, which matches the time of its signature (signing modifies the file as the signature is attached to it.) Furthermore, the TimeDateStamp in its PE header (3A0ACEBF), which the compiler writes into the executable module when building it, indicates that the file was built on November 9, 2000 - exactly matching the date in the above version number.


We're therefore safe to claim that the vulnerable EQNEDT32.EXE has been with us since 2000. That's 17 years, which is a pretty respectable life span for software!

So now a vulnerability was reported in this executable and Microsoft spawned their fixing procedure: they reproduced the issue using Embedi's proof-of-concept, confirmed it, took the source code, fixed the issue in the source code, re-built EQNEDT32.EXE, and distributed the fixed version to Office users, who now see version 2017.8.14.0 under its properties.

At least that's how it would work for most other vulnerabilities. But something was different here. For some reason, Microsoft didn't fix this issue in the source code - but rather by manually patching the binary executable.


Manually Patching an EXE?

Really, quite literally, some pretty skilled Microsoft employee or contractor reverse engineered our friend EQNEDT32.EXE, located the flawed code, and corrected it by manually overwriting existing instructions with better ones (making sure to only use the space previously occupied by original instructions).

How do we know that? Well, have you ever met a C/C++ compiler that would put all functions in a 500+ KB executable on exactly the same address in the module after rebuilding a modified source code, especially when these modifications changed the amount of code in several functions?

To clarify, let's look at BinDiff results between the fixed (2017.8.14.0, "primary") and vulnerable version (2000.11.9.0, "secondary") of EQNEDT32.EXE:



If you're diffing binaries a lot, you'll notice something highly peculiar: All EA primary values are identical to EA secondary values of matched functions. Even the matched but obviously different functions listed at the bottom are at the same address in both EQNEDT32.EXE versions.

As we already noted on Twitter, Microsoft modified five functions in EQNEDT32.EXE, namely the bottom-most five functions listed on the above image. Let's look at the most-modified one first, the one at address 4164FA. The patched version is on the left, the vulnerable one on the right.



This function takes a pointer to the destination buffer and copies characters, one by one in a loop, from user-supplied string to this buffer. It is also the very function that Embedi found to be vulnerable in their research; namely, there was no check whether the destination buffer was large enough for the user-supplied string, and a too-long font name provided through the Equation object could cause a buffer overflow.

Microsoft's fix introduced an additional parameter to this function, specifying the destination buffer length. The original logic of the character-copying loop was then modified so that the loop ends not only when the source string end is reached, but also when the destination buffer length is reached - preventing buffer overflow. In addition, the copied string in the destination buffer is zero-terminated after copying, in case the destination buffer length was reached (which would leave the string unterminated).

Let's look at the code in its text form (again, patched function on left, vulnerable on right):

As you can see, whoever patched this function not only added a check for buffer length in it, but also managed to make the function 14 bytes shorter (and padded the resulting gap before the adjacent function with 0xCC bytes for style points :). Impressive.


Patching The Callers

Moving on. If the patched function got an additional parameter, all those calling it would have to change as well, right? There are exactly two callers of this function, at addresses 43B418 and 4181FA, and in the patched version they both have a push instruction added before the call to specify the length of their buffers, 0x100 and 0x1F4 respectively.

Now, a push instruction with a 32-bit literal operand takes 5 bytes. In order to add this instruction to these two functions while staying within the tight space of the original code (whose logic must also remain intact), the patcher did the following:

For function at address 43B418, the patched function temporarily stores some value - which it will need later on - in ebx instead of a local stack-based variable, which releases enough bytes for injecting the push call. (By the way, addievidence of manual patching is that while the local variable is no longer used, space for it is still made on the stack; otherwise sub esp, 0x10C would turn into sub esp, 0x108.)





For the other caller, function at address 4181FA, the patched function mysteriously has the push instruction injected without any other modifications to the code that would introduce the needed extra space.


As you can see on the above image, the push instruction is injected at the beginning of the yellow block, and all original instructions in that block are pushed down 5 bytes. But why does this not overwrite 5 bytes of the original code somewhere else? It's as if there were 5 or more unused bytes already in existence just after this block of code that the patcher could safely overwrite.

To solve this mystery, let's look at the code in its text form.


Surprise, the vulnerable version actually had an extra jmp loc_418318 instruction at the end of the modified code block. How convenient! This allows the code in this block to be moved down 5 bytes, making space for the push instruction at the top.

Coincidence? Perhaps, but it looks an awful lot like this code block got manually modified before in the past, whereby it got shortened for 5 bytes and its last instruction (jmp loc_418318) was left there.


Additional Security Checks

What we've covered so far was related to Embedi's published research and CVE-2017-11882. Their described buffer overflow would now be prevented by checking for the destination buffer length. But the new version of EQNEDT32.EXE has two additional modified functions at addresses 41160F and 4219F0. Let's have a look at them.

In the patched executable, these two functions got a bunch of injected boundary checks for copying to what appear to be 0x20-byte buffers. These checks all look the same: ecx (which is the counter for copying) is compared to 0x21; if it's greater than or equal to that, ecx gets set to 0x20. All these checks are injected right before inlined memcpy operations. Let's look at one of them to see how the patcher made room for the additional instructions.



As shown on the above image, a check is injected before the inlined memcpy code. Note that in 32-bit code, memcpy is typically implemented by first copying blocks of 4 bytes using the movsd (move double word) instruction, while any remaining bytes are then copied using movsb (move byte). This is efficient in terms of performance, but whoever was patching this noticed that some space can be freed by only using movsb, and perhaps sacrificing a nanosecond or two. After doing so, the code remained logically identical but now there was space for injecting the check before it, as well as for zero-terminating the copied string. Again, an impressive and clever hack (and there was still an extra byte to spare - notice the nop?)

There are six such length checks in two modified functions, and since they don't seem to be related to fixing CVE-2017-11882, we believe that Microsoft noticed some additional attack vectors that could also cause a buffer overflow and decided to proactively patch them.


Final Touches

After patching the vulnerable code and effectively manually building a new version of Equation Editor, the patcher also corrected the version number of EQNEDT32.EXE to 2017.8.14.0, and the TimeDateStamp in the PE header to August 14, 2017 (hex value 5991FA38) - which is just 10 days after Microsoft acknowledged receipt of Embedi's report. (Note however that due to the manual nature of setting these values it's possible that code has been modified after that date.)

[Update 11/20/2017] Another thing Microsoft also patched in EQNEDT32.EXE was the "ASLR bit", i.e., they set the IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE flag in the PE optional header structure:







This is good. Enabling ASLR on EQNEDT32.EXE will make it harder to exploit any remaining memory corruption vulnerabilities. For instance, Embedi's exploit would not work with ASLR because it relied on the fact that the call to WinExec would always be present at the same memory address; this allowed them to simply put that address on stack and wait for the ret to do all the work.

Interestingly, Microsoft decided not to also set the IMAGE_DLLCHARACTERISTICS_NX_COMPAT ("DEP") flag, which would prevent code execution from data pages (e.g., from stack). They surely had good reasons, but should any additional vulnerabilities be found and exploited in EQNEDT32.EXE, the exploit will likely include execution of data on stack or heap. [End update 11/20/2017]


Conclusion  

Maintaining a software product in its binary form instead of rebuilding it from modified source code is hard. We can only speculate as to why Microsoft used the binary patching approach, but being binary patchers ourselves we think they did a stellar job.

This old Equation Editor is now under the spotlight, and many researchers are likely to start fuzzing it for additional vulnerabilities. If any are found, we'll probably see additional rounds of manual binary patches in EQNEDT32.EXE. While Office has had a new Equation Editor integrated since at least version 2007, Microsoft can't simply remove EQNEDT32.EXE (the old Equation Editor) from Office as there are probably tons of old documents out there containing equations in this old format, which would then become un-editable.

Now how would we micropatch CVE-2017-11882 with 0patch? It would actually be much easier: we wouldn't have to shrink existing code to make room for the injected one, because 0patch makes sure that we get all the space we need. So we wouldn't have to come up with clever hacks like de-optimizing memcpy or finding an alternative place to temporarily store a value for later use. This freedom and flexibility makes developing an in-memory micropatch much easier and quicker than in-file patching, and we believe software vendors like Microsoft could benefit greatly from using in-memory micropatching for fixing critical vulnerabilities.

Oh by the way, Microsoft also updated Office's wwlib.dll this Patch Tuesday, prompting us to port our DDE / DDEAUTO patches to these new versions. 0patch Agent running on your computer will automatically download and apply these new patches without interrupting you. If you don't have 0patch Agent installed yet, we have good news for you: IT'S FREE! Just download, install and register, and you're all set. 


Cheers!

@mkolsek
@0patch

P.S.: If you happen to know the person(s) who did the binary patching of EQNEDT32.EXE, please send them a link to this blog post. We'd like them to know how much we admire their work. Thanks! 










Thursday, November 9, 2017

0patching a Pretty Nasty Microsoft Word Type Confusion Vulnerability (CVE-2017-11826)

by Mitja Kolsek, the 0patch Team

In September 2017, Qihoo 360 Core Security detected an in-the-wild attack that leveraged an Office 0day vulnerability now known as CVE-2017-11826. The attack employed an RTF document with embedded DOCX Word documents, whereby one of them exploited the vulnerability to cause type confusion in the OOXML parser, Microsoft's parser for Office Open XML File Formats. Additional noteworthy analyses of this vulnerability subsequently came from McAfee and Kaspersky.

Yang Kang, Ding Maoyin and Song Shenlei of Qihoo 360 Core Security reported this issue to Microsoft, which fixed it in their October security update.

This vulnerability is particularly easy to exploit, as it only requires the user to open a malicious RTF document in Word (which is the default RTF editor when installed), and isn't mitigated by Word's Protected View. It is actually surprising that it's not getting exploited more widely, as attackers know very well that many organizations need weeks or months to apply software updates.

Once we got the exploit file* we could start the analysis. We first minimized the exploit file by removing unnecessary payload and effectively turning it into a crash PoC. Turning a working exploit (one that reliably executes malicious code) into a crash case is an important step for us because in contrast to malware analysts, we're not interested in what malware does when it gets control, but only how it gets control. Making an exploit crash instead of execute its code generally leads us closer to the vulnerability that gets exploited - and closer to writing a patch for it. It's also easier to test a patch, once you have one, with a simple and quick test case.

* Exercise caution with this exploit file - it may run malware on your computer.

Analysis

Opening the crash PoC in Word 2010 crashes immediately with a call to an illegal address:


This looks like a call to a vtable function, and since we already learned from Kaspersky above that we're dealing with a type confusion issue, it's likely that we're looking at a case of code processing an object of some expected type with the address at ecx+4 supposedly pointing to a valid function, while the object is actually of a different type.

Type confusion flaws are not the easiest kind to patch if you don't have the source code. While the fix is usually in the form "if type != EXPECTED_TYPE goto GET_OUT_OF_HERE", it's not trivial to figure out where the said type resides in the object without looking around the code where else the same type may be used. Once you have that figured out, it's relatively simple to determine the EXPECTED_TYPE by observing the type value with legitimate test cases and comparing that to the value when you process the PoC. Finding a suitable GET_OUT_OF_HERE location also needs some figuring out: you want the patched code to escape the flaw gracefully without leaving a corrupted state or cutting off some legitimate functionality. So sometimes this can get time consuming - but in this case, we had the official fix so we could peek into what Microsoft did to correct this flaw.

Diffing the patched wwlib.dll with the latest vulnerable one revealed very few changes. In fact, the only noteworthy change was in the exact function containing the above call that crashes. Let's look at the diff:




The patched function is on the left, the vulnerable on the right (click the image to magnify). It's easy to see that the patched function introduced a branch before the grey-marked code in the vulnerable function. And it looks exactly like "if type != EXPECTED_TYPE goto GET_OUT_OF_HERE": it starts with a check for a specific value, and if there is no match, execution is diverted to some new code (marked "Safe exit code" above), which avoids the call that crashed.

The only surprise is that instead of seeing a comparison of some type variable with some type constant (usually a small number, resulting from an enum clause in the source code), we see a comparison with a function address:

cmp ds:[eax+0x48], 0x31E94A4A


Note that 0x31E94A4A is actually an address of some function in wwlib.dll, which gets stored to the address pointed to by eax+0x48 a couple of blocks earlier in the same patched function. So what Microsoft's patch seems so do is check the validity of the object not by inspecting its type (perhaps there is no type ID in the structure at all), but by inspecting the address of a function pointed to by the object. Why not - as long as it works. No one in the world knows this code better than Microsoft's developers and if they thought this was the best possible fix, they're probably right.


Patching

Without further analysis, we can clone the same logic to the vulnerable code. The only thing we add is the "Exploit Attempt Blocked" dialog in case the object type is found to be incorrect. And here is the result:



What Do You Do Now?

We wrote a CVE-2017-11826 micropatch for 32-bit Word 2010's wwlib.dll version 14.0.7182.5000. While it's generally simple, and often fully automatable, to port a micropatch to other module versions, we prefer to do that on demand for now. So if you're running any affected Office version and for any reason can't apply Microsoft's official update (yet) but would like to be protected from this highly exploitable vulnerability, do let us know at support@0patch.com. We'll port the micropatch to your version and make it available for everyone else too.

If you are a security researcher and have a non-public proof-of-concept for some vulnerability, consider either writing a micropatch for it (using 0patch Agent for Developers) or sharing the PoC with us so we can create a micropatch together. You'll see it's almost as fun to thoroughly break someone's exploit as it is to write one ;)

Cheers!

@mkolsek
@0patch



Friday, November 3, 2017

Office DDE Exploits and Attack Surface Reduction

by Mitja Kolsek, the 0patch team


Windows 10 Fall Creators Update brought a powerful security capability: Windows Defender Exploit Guard. Among its many features and mitigations, the one that sounded most interesting to us was the Attack Surface Reduction (ASR) - especially for its announced ability to block DDE-based attacks:


DDE-based attacks have been very popular lately. They are misusing a DDE-related feature whereby Office gets tricked into launching a malicious executable via a DDE or DDEAUTO field, the latter only requiring the user to confirm one non-security notice.

You may also know that we have created a free micropatch for Word that prevents DDE fields from launching any executables - thereby enforcing the behavior specified in official Microsoft documentation, effectively blocking DDE attacks.

We therefore wanted to see how ASR does its magic. So we installed the Fall Creators Update and Office 2010, and followed Microsoft's instructions to enable the "Block Office applications from creating child processes" rule. We tested how DDE-based attacks are blocked, looked around a bit to see what could go wrong, and compared the said mitigation to our micropatch.

You can see the whole process in the video below, but the main takeaways are these:

  1. Most importantly, we confirmed that ASR does block DDE attacks in Word.
  2. Unfortunately, ASR only applies to Word, Excel, PowerPoint and OneNote, so Outlook-based DDE attacks are not blocked. (Perhaps Outlook will be added at a later time?)
  3. Also unfortunately, the ASR rule in question breaks some legitimate functionality. We found, for instance, that  it prevents Adobe Reader from being launched via Word's legitimate "Create PDF/XPS Document" functionality. There is another case (not shown in video) with Word being unable to launch "System Info" from the About dialog. Legitimate Word add-ins may want to launch processes too, and will be blocked.
  4. In comparison, a real fix (in this case, our micropatch) only blocks the dangerous process-launching while leaving all other functionalities intact; it also automatically works in Outlook. Naturally, an actual code fix is always better than a generic mitigation.
  5. Nevertheless, exploit mitigations have one big advantage compared to patches: they can stop or slow down attackers exploiting unknown vulnerabilities. A patch can only fix a known bug.

In conclusion, ASR (with all its rules) is a very powerful tool and while one must exercise caution with its blocking of potentially legitimate functionality, it can significantly limit the attacker. So by all means use it if you can, but also apply patches for known vulnerabilities!

You can't use ASR, unfortunately, on non-Windows-10 systems, or on Office 2007, which has just reached its end of support but is likely still used in many places. 






Stay safe!

@mkolsek
@0patch