This blog post has been written based on Malwarebytes Anti-Exploit 1.08.1.1189. The described limitation of Anti-Exploit has been fixed in v1.09
One of the ways in which computers get infected by adversaries is by using remote code execution exploits. Where there are attack vectors, there are defenses. A well-known piece of software that can tackle most common exploits is Malwarebytes Anti-Exploit. Just like EMET Anti-Exploit consists of a series of exploit mitigations that should stop most off-the-shelf exploits. However, this does not mean that with enough time spent into researching a security solution there is no way to bypass it. No single security product can have a definitive advantage in the cat-and-mouse game between security researchers and exploit developers at one side and the defenders at the other side.
If an attacker would want to bypass Anti-Exploit two paths can be followed:
- Bypass each individual mitigation.
- Find a single technique to disable most (or all) of MBAE’s mitigations.
In this blog post a technique will be discussed that takes the second route.
Just like EMET Anti-Exploit stores its configuration in a section of read-only memory (from now on called MITIGATION_CONFIG). Based on this configuration it is determined whether a mitigation check should be performed or not.
(Almost) all mitigations that Anti-Exploit implements to appear check in the same way whether a mitigation is enabled or not. Let’s for example take the function that implements the stack pivot mitigation in Anti-Exploit 1.08.1.1189. As shown in the figure below, Anti-Exploit assesses whether the mitigation is enabled before checking whether a given address lies within the stack boundaries as defined in the TIB.
Anti-Exploit checks whether a mitigation is enabled by performing an AND operation on a global boolean variable stored at mbae.dll+0x53b00 and the mitigation setting of the stack pivot mitigation in the read-only config (MITIGATION_CONFIG+0x3c). When the stack pivot mitigation is enabled 0x35f is stored at MITIGATION_CONFIG+0x3c, when the mitigation disabled 0x35e is stored at MITIGATION_CONFIG+0x3c. Under normal conditions one of the two following AND operations will be performed:
|Stack pivot mitigation enabled||0x1 & 0x35f = 1|
|Stack pivot mitigation disabled||0x1 & 0x35e = 0|
If the mitigation is not enabled the function responsible for the stack pivot mitigation (mbae.dll+0x6980) will return zero as expected. There is one catch though with this AND operation, the global boolean variable (mbae.dll+0x53b00) is stored in writable memory.
When mbae.dll+0x53b00 is overwritten with zero, the AND operation will always yield zero. This type of check seems to be used in about all mitigations, therefore it is possible to disable Anti-Exploit by only overwriting one value in memory. The table below shows the effect of overwriting the boolean variable with zero on the stack pivot mitigation:
|disarm + stack pivot enabled||0x0 & 0x35f = 0|
|disarm + stack pivot disabled||0x0 & 0x35e = 0|
Writing a POC for this disarming flaw would only require the following two steps:
- Obtain the base address of mbae.dll
- Overwrite mbae.dll+0x53b00 with NULL (For MBAE 1.08.1.1189)
This disarming flaw discussed in this blog post has been fixed in MBAE version 1.09 by marking the memory that stores the boolean variable as read-only.