[Update 11/21/2017]

4164FA

[End update 11/21/2017]

A Pretty Old Executable

EQNEDT32.EXE

3A0ACEBF

EQNEDT32.EXE

EQNEDT32.EXE

Manually Patching an EXE?

EQNEDT32.EXE

EQNEDT32.EXE

EQNEDT32.EXE

EQNEDT32.EXE

4164FA

0xCC

Patching The Callers

43B418

4181FA

push

0x100

0x1F4

push

43B418

ebx

push

sub esp, 0x10C

sub esp, 0x108

4181FA

push

push

jmp loc_418318

push

jmp loc_418318

Additional Security Checks

EQNEDT32.EXE

41160F

4219F0

0x20

ecx

0x21

ecx

0x20

memcpy

memcpy

memcpy

movsd

movsb

movsb

nop

memcpy

Final Touches

EQNEDT32.EXE

5991FA38

[Update 11/20/2017]

EQNEDT32.EXE

IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE

EQNEDT32.EXE

ret

IMAGE_DLLCHARACTERISTICS_NX_COMPAT

EQNEDT32.EXE

[End update 11/20/2017]

Conclusion

EQNEDT32.EXE

EQNEDT32.EXE

memcpy

wwlib.dll

EQNEDT32.EXE

by Mitja Kolsek, the 0patch TeamToday Embedi published their proof-of-concept exploit , which allowed us to see where exactly Microsoft's manual patch blocks it. Contrary to this article's original claim that CVE-2017-11882 was patched in functionwhile six other buffer overflow checks we found were for some other attack vectors, it is actually one of those six checks that blocks Embedi's exploit. This article has been slightly corrected to reflect that. In addition, we were now able to create a micropatch for Equation Editor that also blocks all exploits targeting the vulnerability found by Embedi. All Internet-connected computers with a registered 0patch Agent running have already received this micropatch and have it automatically applied whenever Equation Editor is launched.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 ancomponent of Microsoft Office (Office now uses an integrated Equation Editor), which is confirmed by looking at the properties of the unpatchedWe can see thatis 2000.11.9.0 (implying being built in 2000), whileis 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 ), 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 vulnerablehas 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, and distributed the fixed version to Office users, who now see version 2017.8.14.0 under its properties.At least that's how itwork 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.Really, quite literally, some pretty skilled Microsoft employee or contractor reverse engineered our friend, 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 onafter 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") ofIf you're diffing binaries a lot, you'll notice something highly peculiar: Allvalues are identical tovalues of matched functions. Even the matched but obviously different functions listed at the bottom are at the same address in bothversions.As we already noted on Twitter , Microsoft modified five functions in, namely the bottom-most five functions listed on the above image. Let's look at the most-modified one first, the one at address. 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 withbytes for style points :). Impressive.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 addressesand, and in the patched version they both have ainstruction added before the call to specify the length of their buffers,andrespectively.Now, ainstruction 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), thepatcher did the following:For function at address, the patched function temporarily stores some value - which it will need later on - ininstead of a local stack-based variable, which releases enough bytes for injecting thecall. (By the way, additional evidence of manual patching is that while the local variable is no longer used, space for it is still made on the stack; otherwisewould turn into.)For the other caller, function at address, the patched function mysteriously has theinstruction injected without any other modifications to the code that would introduce the needed extra space.As you can see on the above image, theinstruction 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 extrainstruction 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 theinstruction 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 () was left there.What we've covered so far was related to Embedi's published research and CVE-2017-11882, but iswhat blocks Embedi's exploit. The new version ofhas two additional modified functions at addressesand. 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-byte buffers. These checks all look the same:(which is the counter for copying) is compared to; if it's greater than or equal to that,gets set to. All these checks are injected right before inlinedoperations. 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 inlinedcode. Note that in 32-bit code,is typically implemented by first copying blocks of 4 bytes using the(move double word) instruction, while any remaining bytes are then copied using(move byte). This is efficient in terms of performance, but whoever was patching this noticed that some space can be freed by only using, 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?)There are six such length checks in two modified functions, and just one of them is directly responsible for blocking Embedi's exploit. We believe that Microsoft noticed some additional attack vectors that could also cause a buffer overflow and decided to proactively patch the other fives and the patched function we covered earlier.After patching the vulnerable code and effectively manually building a new version of Equation Editor, the patcher also corrected the version number ofto 2017.8.14.0, and thein the PE header to August 14, 2017 (hex value) - 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.)Another thing Microsoft also patched inwas the "ASLR bit", i.e., they set theflag in the PE optional header structure This is good. Enabling ASLR onwill 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 theto do all the work.Interestingly, Microsoft decided not to also set the("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, the exploit will likely include execution of data on stack or heap.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. While Office has had a new Equation Editor integrated since at least version 2007, Microsoft can't simply remove(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-optimizingor 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'sthis 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!P.S.: If you happen to know the person(s) who did the binary patching of, please send them a link to this blog post. We'd like them to know how much we admire their work. Thanks!