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. (Password is aiongnostes1004.)
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:
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.
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 firstname.lastname@example.org. 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 ;)