Introduction
MBAE (Malwarebytes Anti-Exploit) –formerly known as ExploitShield by ZeroVulnerabilityLabs- is a security program from Malwarebytes that adds security protections to user mode programs. MBAE works by injecting mbae.dll into protected processes, which will installs hooks into critical Windows APIs, these installed hooks are merely jumps to MBAE protections, which are responsible for analyzing the caller code to decide whether it is malicious or not. The findings presented in this paper are applicable to MBAE version 1.08.1.1189, and have been reported to Malwarebytes to be fixed and resolved in MBAE version 1.09.
The key findings of this paper:
- Describing MBAE design and how it’s different components communicate.
- Enumerating security issues in MBAE that subverts both ASLR and DEP.
- Presenting two techniques to disable MBAE:
- Using MBAE To Disable MBAE.
- MBAE Switch-off.
Please refer to Appendix if you would like to know more about MBAE protections.
MBAE System Design
MBAE has different parts, in kernel and user modes. To understand how MBAE works, it is important to focus on how these different components interact with each other’s.
MBAE runs in the background as a Windows service (MbaeSvc) this centralized service is the backbone for the MBAE system, and is responsible for handling the injection engine driver (ESProtectionDriver). It also manages the communication between different processes across the system using IPC mechanisms such as: Advanced Local Procedure Calls (ALPCs), events, memory mapped-files and anonymous pipes. In particular, this service creates an ALPC port, named “mchIpcMBAE_IPC_CLIENT_CHANNEL” to communicate, with the MBAE client. Additionally, it creates another ALPC port named “mchIpcMBAE_IPC_PROTECTION_CHANNEL” to communicate with protected programs (using the loaded mbae.dll in protected processes).
MBAE prepends ALPC ports with “mchIpc”, which suggests that MBAE is using madCodeHook [1] as an API hooking engine and DLL injection library.
The service communicates to a protected program by sending events and accessing a memory mapped-file named “MBAE_IPC_PROTECTION_CHANNEL”. It also communicates to the MBAE client by sending events and by writing to a memory-mapped file named “MBAE_IPC_SERVICE_CHANNEL”.
Figure 1: Viewing MBAE IPC mechanisms
ESProtectionDriver is responsible for the kernel hooking. It uses PsSetCreateProcessNotifyRoutine for registering a callback function that gets called every time a new process is created or destroyed. This callback function is responsible for injecting mbae.dll into protected processes, and to achieve that, the driver use the ntdll.dll module to resolve multiple Windows APIs for the driver:
- ZwReadVirtualMemory
- ZwWriteVirtualMemory
- ZwQueryVirtualMemory
- ZwProtectVirtualMemory
- ZwQueryInformationThread
- ZwOpenProcessTokenEx
The driver also uses PsSetLoadImageNotifyRoutine to register another callback function that gets called every time a new module is loaded into the hooked process. This callback function looks for ntdll.dll, and resolves multiple Windows APIs:
- NtTestAlert
- NtProtectVirtualMemory
- LdrLoadDll
- NtAllocateVirtualMemory
- NtFreeVirtualMemory
- NtQuerySystemInformation
- NtDelayExecution
These APIs are needed for the injection code, which is responsible for loading mbae.dll into protected processes. The driver uses ZwWriteVirtualMemory to write the injection code into each process, and to patch ZwTestAlert with a jump instruction to MBAE injection code (which will load mbae.dll). Eventually ZwTestAlert will be called to flush queued Asynchronous Procedure Calls (APCs), which will result in executing the injection code. The injected code calls LdrLoadDll to load mbae.dll into the loading process.
When mbae.dll is loaded into a protected process, it installs the hooks and it creates a new thread, which is responsible for reporting any triggered protection. This is done by sending events and writing to a memory mapped-file named “MBAE_IPC_PROTECTION_CHANNEL”. This will be delivered to the MBAE service, which will send this information to MBAE client by sending events and writing to a memory-mapped file named “MBAE_IPC_SERVICE_CHANNEL”, the MBAE service also sends anonymous pipe message that detail the alerted protection to be displayed as a user alert in the MBAE client.
The MBAE client is responsible for receiving alerts from the service and them to the user. MBAE client also gives the user the ability to control MBAE and disable or enable MBAE protections. To communicate with the MBAE service, the MBAE client creates an ALPC port named “mchIpcMBAE_IPC_ SERVICE _CHANNEL”. It also uses anonymous pipe, sends events and writes to a memory mapped-file named “MBAE_IPC_ CLIENT _CHANNEL”.
Figure 2: MBAE system components
Security Issues In MBAE
Subverting ASLR and DEPIn the previous section, I described how the DLL injection code is written to every protected process. Due to limitations in the injection engine that MBAE use, MBAE always writes its DLL injection code to a static address (0x30000), which subverts ASLR in MBAE protected processes. Furthermore, MBAE marks its DLL injection code readable, writable, executable (RWX), which subverts DEP. This endangers MBAE protected processes. The DLL injection code calls LdrLoadDll to load mbae.dll.
LdrLoadDll(
IN PWCHAR PathToFile OPTIONAL,
IN ULONG Flags OPTIONAL,
IN PUNICODE_STRING ModuleFileName,
OUT PHANDLE ModuleHandle
);
Figure 3: MBAE injection code calls LdrLoadDll to load mbae.dll
Figure 4: MBAE hooked APIs always jump to fixed addresses
Figure 5: MBAE hooks memory areas
Since MBAE protections are implemented in these memory areas, this allows attackers to bypass MBAE by patching these memory areas to jump over MBAE implemented protections, or worse it can be used to defeat ASLR and DEP, as these allocated memory areas can offer attackers a wide variety of fixed addresses ROP gadgets.
Using MBAE To Disable MBAE
By design, user mode protections, which share the same level of privilege as exploit code, will always be susceptible to being bypassed or disabled. The main purpose of MBAE protections is to raise the bar of exploitation, and to increase the cost of attacks. Defeating MBAE protections with easy or low cost attacks, defeats the main purpose of MBAE. I analyzed MBAE and found that calling DllMain in mbae.dll, with the parameters (mbae.dll base address, 0, 0) will result in disabling MBAE protections.
DllMain(GetModuleHandleA("MBAE.dll") , DLL_PROCESS_DETACH , NULL);
This DllMain call results in calling the unload function located at offset 0x5C60 in mbae.dll. The benefit of calling DllMain rather than calling the unload function directly, is that DllMain can be retrieved from the PE (Portable Executable) header, which will always be present at mbae.dll’s base address. Consequently, this technique would be more reliable across different versions of MBAE, instead of using the unload function offset which may change from one version to another. FireEye published (Using EMET To Disable EMET by Alsaheel and Pande) [2], which presented the same technique against EMET from Microsoft. Using an existing exploit for an old and patched vulnerability, CVE-2011-2371, and the same ROP gadgets I used against EMET, the result was disabling MBAE protections. The ROP gadgets merely call DllMain with parameters (mbae.dll base address, 0, 0), which calls MBAE's unload function. The unload function calls the function located at offset 0x4AE0, which retrieves the global variable located at offset 0x53758. This variable points to a doubly linked list of structures with the size 0xD0 bytes for each structure (only the first three values are relevant to this analysis).
struct HOOKED_API {
PVOID nextHookStruct; // pointer to the next hook structure
PVOID prevHookStruct; // pointer to the previous hook structure
PVOID HookConfig; // Pointer to global variable points to hook memory area
};
Figure 6: MBAE unloading code loops through HOOKED_API structures
This function loops through the HOOKED_API structures, and for each structure it retrieves HookConfig pointer -which points to MBAE global variable-. For each structure, MBAE reserves a global variable to track the hook memory area. After unhooking a hooked API, the associated global variable will point to an unhooked API instead of a hook memory area. Additionally, the hook memory area will be de-allocated as it will not be needed after unhooking the API.
Figure 7: MBAE global variables point to hooks memory areas
Figure 8: MBAE global variable point to unhooked API after unhooking
At offset 0x18650, MBAE unloading code passes three important parameters (the API address to be patched, the bytes of the original API prologue and the size of the API prologue).
Figure 9: Preparing unloading parameters
Instead of patching (removing) the hook directly, MBAE uses a safer approach that executes the code at offset 0x1C2A7 to copy the original API prologue bytes to a copy of the hooked API stored on the stack. This is to not interfere with the API in case if it was being used by another thread. MBAE use CMPXCHG8B (Compare and Exchange) instruction with LOCK prefix to patch (unhook) the hooked API safely.
Figure 10: MBAE Patching hooked API
As shown in the figures 11, 12, 13 and 14, the EBX and ECX values have been copied to the hooked API to be unhooked.
Figure 11: Before patching memory with CMPXCHG8B instruction
Figure 12: After patching memory with CMPXCHG8B instruction
Figure 13: CreateProcessInternal before unhooking
Figure 14: CreateProcessInternal after unhooking
Finally, the unloading code calls the function at offset 0x11D90, which is responsible for reporting the unloading event to the MBAE service.
Figure 15: Reporting the unloading event to MBAE service
MBAE Switch-Off
NOTE: This finding is also independently reported by another researcher to Malwarebytes [3].
MBAE has a global variable at offset 0x53B00, this variable can serve as a global switch that can be used to disable all MBAE protections. This global variable is in extensive use, and is referenced more than 90 times in MBAE code. The main purpose of this global variable is to store the returned value from the function located at offset 0x84B0. This function is responsible for allocating memory areas for MBAE protections use. In case this function fails, it returns zero to be stored in the disable global variable. I found that zeroing out this global variable -which is located in readable and writable data section- causes MBAE protections routines to fail, as they check this global variable value, and they go off when this variable does not contain the expected value.
Figure 16: Example of how MBAE protections routines use the disable global switch
I have used an old and patched vulnerability, CVE-2011-2371, and built the ROP gadgets on top of an existing exploit. The ROP gadgets just zero out the disable global variable, which result in disabling MBAE protections.
Conclusion
This paper presents MalwareBytes MBAE design, with detailing MBAE components and how they communicate. The analysis shows several serious security issues in MBAE, which open a new attack vectors to protected programs. MBAE subverts ASLR and DEP, giving attackers the chance to easily patch MBAE protections; also it offers attackers a wide variety of fixed addresses ROP gadgets.
Additionally, the paper presents two techniques to disable MBAE. The first technique is to unload mbae.dll by calling DllMain(), which is more reliable as it does not rely on a fixed offset related to one MBAE release. The second technique disables MBAE by zeroing out a global variable that can serves as a global switch to disable MBAE, and this technique implementation fits in short and easy to find ROP gadgets.
Special thanks to Dan Caselden.
Appendix
Malwarebytes MBAE has four layers of protections [4]:
Layer0: Application Hardening Layer-
This layer objective is to make the protected programs more resilient against exploit attacks.
Enforces Data Execution Protection (DEP)- this protection enforces DEP for modules which have not been compiled with /NXCOMPAT option.
Anti-HeapSpraying Enforcement- Reserves certain memory ranges to prevent heap spraying.
Dynamic Anti-HeapSpraying Enforcement- Analyzes heap to find evidence of malicious shellcode.
Bottom-Up ASLR- adds additional randomization to the heap.
Disable Internet Explorer VB Scripting- Prevents deprecated Visual Basic scripting engines from loading.
Detection of Anti-Exploit fingerprinting attempts- Detects victim machine fingerprinting attempts by popular exploit kits.
Layer1: Protect Against Operating System Security Bypasses-
This layer of protection prevents shellcodes from bypassing operating system protections (e.g. DEP).
DEP Bypass Protection- Detects attempts to turn off DEP.
Memory Patch Hijacking Protection- Detects attempts to bypass DEP using WriteProcessMemory.
Stack Pivoting Protection- Prevents creating or using a fake memory stack.
ROP Gadget Detection- Detects and prevents Return Oriented Programming (ROP).
Layer2: Memory Caller Protection (Also known as Malicious Return Address detection)-
This layer is to prevent exploits from executing code from outside the loaded modules or from special memory areas such as heap or stack.
Layer3: Application Behavior Protection-
This layer defends against exploits that escape sandbox in applications like Acrobat Reader and Java, and detects Java meterpreter shellcodes.
Malicious LoadLibrary Protection- Prevents exploits from delivering a payload library from a Universal Naming Convention (UNC) network path.
Protection for Internet Explorer VB Scripting- Detects and prevents design vulnerability [5].
Protection for MessageBox Payload- Detects and prevents messagebox payloads, disabled by default.
Java Malicious Inbound Shell Protection- Detects and prevents bind shellcodes.
Java Malicious Outbound Shell Protection- Detects and prevents reverse shellcodes.
Java Metasploit/Meterpreter Generic Protection- Detects and prevents Metasploit Java/Meterpreter shellcodes.
Java Metasploit/Meterpreter Command Execution Protection- Detects and blocks commands in an established Java/Meterpreter session.
References
[1] http://help.madshi.net/madCodeHook.htm
[2] https://www.blackhat.com/docs/us-16/materials/us-16-Alsaheel-Using-EMET-To-Disable-EMET.pdf
[3] https://blog.ropchain.com/2016/11/21/circumventing-malwarebytes-anti-exploit-1-08-with-a-single-write-to-memory/
[4] https://www.malwarebytes.com/pdf/guides/MBAEBGuide.pdf
[5] https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2014-6332
Great job mate.keep it up.
ReplyDelete