Hijacking Malwarebytes via COM IPC
Attacking K7 Security was my first attempt at discovering, analysing, and exploiting software at the protocol level. It made me curious as to how other vendors designed and developed their inter-process communication (IPC) methods between the untrusted client (the user-controlled software; usually the GUI) and the high integrity service process. Here is the diagram that shows the interaction between these two components again, for completeness:
This article will be about diving into reverse engineering the communication protocol used in Malwarebytes and the issues that I identified as a result of bypassing the intended (and assumed?) approach towards controlling functionality.
Disclaimer: I do not claim to know everything on the topics discussed here as fact, this is purely what I have inferred from my research. Especially if the COM information is inaccurate in any way, shape or form, please let me know and I will fix it as soon as possible. PoC code snippets has been withheld until the vendor applies a patch or until a reasonable amount of time has passed.
Recommended Pre-requisite Knowledge
- Windows API
- C/C++ and Intel Assembly
- Inter-process communication
- Named Pipes
- Component Object Model (COM)
Discovering IPC Methods
There are multiple ways with which software can perform IPC. How do we figure out which ones Malwabytes uses? Let’s start with analysing the imported functions of the GUI and service executables in IDA. Starting with the service executable,
MBAMService.exe, we can identify named pipe functions:
While we’re here, let’s see if we can extract the pipe name.
CreateNamedPipe is documented to receive the name of the pipe:
Following the references of
CreateNamedPipeW will lead us to:
But what does
MBLG mean…? If we take a look at the
ConnectNamedPipe references, there are strings which will give us a hint:
Perhaps MalwareBytes License Generator? Nevertheless, we’ve identified one of the IPC techniques.
In the documentation for the available IPC methods, it states that “the foundation of OLE is the Component Object Model (COM)”. If we look into the
ole32 library imports, we can see this:
CoInitializeEx documentation states:
This verifies that the service process also uses COM IPC.
What about the GUI executable,
mbam.exe? Let’s look for the same IPC methods beginning with named pipes:
CallNamedPipe is documented to receive the target pipe name:
Again, we will follow the reference to that function and again, we’ll see the
This time, it’s accompanied with another string
NeedAKey which is passed into the input buffer for the service process.
mbam.exe also imports the COM functions from the
Reverse Engineering the COM Protocol
Firing up OleViewDotNet, we can list all the COM classes exposed through their CLSIDs. Filtering by servers, we can see that the
MBAMService.exe executable is packaged with type libraries that define protocol information in human-readable and code form.
Here is the
and here is the
How can we extract this information so we can use it? The OleViewDotNet tool is based on Microsoft’s OleView which can pull the type library information into a compilable Interface Definition Language (IDL) file.
Notice that at the top of the generated IDL file there is a
typelib filename: 12 which probably indicates that there are at least 12 type libraries. After extracting all of the IDL files, we should have fourteen files:
Compiling IDL Files
To compile these into code, we can use Microsoft’s
midl.exe utility like so:
midl /out <output directory path> /header <header output file path> <path to IDL file>
But we have to do this is a specific order. Looking back at the generated
AEControllerCOMLib IDL file in
OleView, there are dependencies specified with
importlib("<file>"). These are the other compiled IDL files used by Malwarebytes. We must first find and compile the IDL file with none of these dependencies and then gradually work our way up until we have all of the dependencies required to compile all of the other IDL files. Let’s take a brief look at an example.
The first file we need to compile is the
LogController. We can see that it only has the
stdole2.tlb type library as its dependency which is provided by Microsoft.
If we compile this using
midl, we’ll get three files (depending on how you named them on the command line):
LogController.h LogController.tlb LogController_i.c
The header file will contain the definition of all of the enums, structs, and class methods for the COM object:
The C file will contain the interface identifiers (IID) and CLSIDs to query and use the COM object:
The type library (
tlb) file is required for dependencies to compile the other IDL files. This file name should correspond with what’s specified in the IDL file’s
typelib filename - in this case, it’s just
The next IDL to be compiled is the
PoliciesController. The IDL file states that its dependencies are
2, both of which we have.
Now we just repeat the steps as before with the
LogController and the rest of the other IDL files.
Querying Malwarebytes’ COM Object Classes
To figure out how we can interact with the COM classes, we can look at how
mbam.exe does it using Rohitab’s API Monitor. With this information, hopefully we can re-implement it ourselves. We’ll set the filter to monitor for COM and Pipe API calls only in the
mbam.exe module and it should look like this when monitoring the startup of the application:
Let’s ignore the first call to
CoCreateInstance, because it fails, and move onto
CallNamedPipeW. We saw this call earlier with the
NeedAKey string in the input buffer. Here is what the output buffer contains:
Looks like a randomly-generated sequence of wide ASCII characters (we’ll call this the “license string”), and we can see it used in calls to
SysFreeString which returns a
BSTR type of the input string and frees it, respectively. The
BSTR type is used in passing data in COM objects. The license string is also used in
IClassFactory2::CreateInstanceLic which is documented as so:
That’s good to know! But how do we get here and what is this
IClassFactory2 used for? Let’s jump back to the call to
CoGetClassObject and its arguments. The
MBAMServiceController CLSID is specified to retrieve its corresponding pointer to its object which is returned as an
IClassFactory::QueryInterface method is then used to obtain the
IClassFactory2 interface. But what’s the point in all of this? As it turns out - after a lot of experimentation - that the
IClassFactory2::CreateInstanceLic interface can be used to acquire a pointer to the
IMBAMServiceController interface. Using this interface, we can gain access to the COM object classes exposed by Malwarebytes, i.e. the
RTPController class, the
LicenseController class, etc. So just to summarise this section, here is what the flow looks like:
1. Get a license string with CallNamedPipe("NeedAKey"), 2. Get an IClassFactory interface with CoGetClassObject(CLSID_MBAMServiceControllerClass) 3. Get an IClassFactory2 interface with IClassFactory::QueryInterface(IID_IClassFactory2), 4. Get a BSTR for the license string with SysAllocStringLen("LicenseKey"), 5. Get an IMBAMServiceController interface with IClassFactory2::CreateInstanceLic("BSTR_LicenseString"), 6. Get the desired MBAM COM class's interface with the IMBAMServiceController interface.
Bypassing Security Checks
Of course it wouldn’t be so easy. Malwarebytes’ service implements some verification on the process that requests a license string on
We can see it in log files too:
To use my time effectively, I opted to try a few methods to try to bypass the verification checks before digging into the assembly. I’ve seen these problems exist in other products so I had a few ideas. The immediate method that came into my head was to do process hollowing on
mbam.exe. However, I don’t believe I can get that to work on the 64-bit architecture since I had never gotten it to work before. Perhaps it could work in the case of 32-bit? The next method was to append the signature using SigThief. Unfortunately, that did not work:
The last technique involves injecting shellcode into a
mbam.exe process. Though this requires a fair bit of work to generate the shellcode, it was what ended up working. The shellcode can be generated through the compiler, thanks to Matt Graeber’s excellent post on Writing Optimized Windows Shellcode in C. Once the binary is compiled, simply extract the code from the
.text section and copy it into another project that performs basic shellcode injection into a target process. Personally, I just used the classic
There are also two other security measures that I have not mentioned yet. The first is the User access option which, when set, requires a password to modify any selected settings. The second is a UAC prompt for when a non-administrative user tries to change critical settings such as self-protection or real-time protection. As I’ve discovered, directly interacting with the settings through the COM classes entirely bypasses these two security components.
This post shows yet another example of attacking security products via their communication channels. Although there were layers of protection for preventing unauthorised access, the trust granted in the verification procedure was subverted using trivial impersonation that has impacted other products in the past and present. Perhaps the self-protection was insufficient and needs to extend to all of the Malwarebytes processes. This could stop the shellcode injection and therefore, this attack vector.
As always, the PoC code (currently withheld) can be found on my Antimalware-Research GitHub.
Thanks for reading and I hope you learned something!