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:
https://0x00sec.s3.amazonaws.com/original/2X/6/6a53340a79f1f087af9d10f11f8a81408927a8f0.jpeg
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:
The 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 \\.\pipe\MBLG
string:
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 ole32
library:
Reverse Engineering the COM Protocol
One of the many amazing tools released by James Foreshaw is OleViewDotNet. Using this, we can discover COM objects available on a system.
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 RTPController
interface:
and here is the LicenseController
interface:
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 2
.
The next IDL to be compiled is the PoliciesController
. The IDL file states that its dependencies are stdole2.tlb
and 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 SysAllocStringLen
and 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
. The 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 ConnectNamedPipe
:
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 CreateRemoteThread
method.
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.
Demonstration
Conclusion
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!
– dtm