Demystifying Windows Component Object Model (COM)
The article is my way of understanding the Component Object Model better, to better understand its attack surfaces and its potential for exploitation by adversaries.
It is by no means an exhaustive COM Internals guide as COM is a vast subject that cannot be covered in a mere article, however I attempt to cover information that I think is relevant for security researchers interested in COM.

0x01: The Component Object Model (COM)

Threat actor have been using COM for quite a long time now, using COM for various purposes such as code execution, persistence, privilege escalation and lateral movement.
This article's purpose is to provide you with deeper understanding of the Component Object Model to better understand those attack vectors.
To open with a quote for Microsoft's documentation
COM is a platform-independent, distributed, object-oriented system for creating binary software components that can interact. COM is the foundation technology for Microsoft's OLE (compound documents) and ActiveX (Internet-enabled components) technologies.
What this essentially means is that COM is a standard that Microsoft developed for the development and usage of code, that enables other developers to use code regardless of the original language their code is written in and without re-compiling that code.

History

"Antony Williams, who was involved in the creation of the COM architecture, distributed two internal papers in Microsoft that embraced the concept of software components: Object Architecture: Dealing With the Unknown – or – Type Safety in a Dynamically Extensible Class Library in 1988 and On Inheritance: What It Means and How To Use It in 1990.
These books provided the foundation of many of the ideas behind COM. Object Linking and Embedding (OLE), Microsoft's first object-based framework, was built on top of DDE (another, older IPC technology) and designed specifically for compound documents. It was introduced with Word for Windows and Excel in 1991.
It's important you understand there are various technologies involved here to enable various kinds of capabilities, A windows compound document is built on top of the COM technology to provide the ability to access and use various type of components (COM Objects) on a single document.
An example for that is a Word Document inside a Spreadsheet and vise-versa.
The excel file being the compound document, using a Word COM object to embed the document inside the Excel's spreadsheet.
Don't worry if you still don't fully understand it, much detailed example will be given below at the 0x02: Understanding COM Objects Section.
Word component embedded inside Excel

COM is Everywhere

Although an old technology, Microsoft's products leverages COM objects everywhere, from Microsoft's Office components that can be used to create and embed documents and spreadsheets.
Other components even enable arbitrary code execute, those are categorized as "Automation objects", and WScript.Shell is amongst those object.

Some Statistics before we dive in

OleViewDotNet is a great(!) tool by the even more great James Forshaw(@tiraniddo).
James Forshaw is definitely one amazing researcher whos done a lot (!) of research on COM and has a outstanding understanding of the technology, its faults and its attack surface, he published many lectures and content about COM Internals and Exploitation (amongst other things) and I really encourage you to see his lectures and read his blog.
This screenshot below is the result of the enumeration of all install COM objects on my lab machine.
The purpose of these statistics is to give you a rough idea of the potential attack surface that exists in COM by mare quantity of objects, each Windows build version may have some COM objects removed or added.
Windows 10 x64 , 1909 Stats:
  • 8300+ COM Objects
  • 27000+ Interfaces
  • 7500+ Are In-process Servers (DLLs)
  • 810+ Are Out-of-Process Servers (EXE)
OleViewDotNet COM Stats

COM Objects Database

Using OleDotNetView Powershell module I created a bunch of CSV files and uploaded them to my github windows-com-objects which can be used to explore COM objects.
I enjoyed importing the CSV files to a pandas DataFrame. it is also possible exploring them dynamically with OleViewDotNet Powershell Module as it creates an entire Database in-memory in the form of Powershell collections and objects which are really easy and fun to handle.
Here's an example of what to expect in some of the CSVs
COM Objects Database as a Pandas DataFrame

0x02: Understanding COM Objects

What is a COM Object?

It's an object that conforms to the OLE Component Object Model (COM).
A COM object is an instance of an object definition, which specifies the object's data and one or more implementations of interfaces on the object. Clients interact with a COM object only through its interfaces.
The deployment of these classes is in the form of a Dynamically linked library (DLL) or an Executable (EXE).
A COM Class contains the object's definition and its various interfaces.
All COM interfaces inherit from the IUnknown interface. The IUnknown interface contains the fundamental COM operations for polymorphism and instance lifetime management. The IUnknown interface has three member functions, named QueryInterface, AddRef, and Release. All COM objects are required to implement the IUnknown interface.
A COM object exposes its features through an interface, which is a collection of member functions.
Using OleViewDotNet we can browse our COM objects and view their interfaces:
Windows Script Host Shell Object
In RED Windows Script Host (Wscript) Com Class, In BLUE the class' interfaces.
Since this COM object has a TypeLib (a file that contains the interface's methods definition) we can also view The IWshShell Interface's methods definition with their expected types.
If an interface is not documented, A TypeLib will help us understand how to execute it's methods properly.
Using these methods we can create registry keys (RegWrite), run executables (Run), create link files(CreateShortcut) and more!
We can use Powershell to test the IWshShell Interface, and try to execute it
1
# Activte the Windows Script Host COM Object by using a ProgID (WScript.Shell)
2
# We get in return a pointer to the IWshShell Interface
3
$WshShell = New-Object -comObject WScript.Shell
4
5
# Using the CreateShortcut method returns a Shortcut object.
6
# This path is where the lnk file will be saved
7
$Shortcut = $WshShell.CreateShortcut("C:\tools\my.lnk")
8
# Here we specify the location the link points to (what will be executed when we click the file)
9
$Shortcut.TargetPath = "C:\windows\system32\cmd.exe"
10
$Shortcut.Save()
Copied!
And here's the results, a link file create by a the IWshShell Interface that we executed via Powershell.
The Same COM object can be executed many other languages(C/C++, .NET, Python, Go), which is what COM is essentially created for, which is Interoperability.
A link file that was created with the IWshShell.CreateShortcut method

COM Objects Identifiers

Following the example we've seen, It's important you get to know COM's Identifiers terminology
  • CLSID - Class ID, Is globally unique identifier (GUID) that Identifies a COM Class. In our previous example, we looked at Windows Script Host Class which has CLSID - {72C24DD5-D70A-438B-8A42-98424B88AFB8}
  • ProgID - Program ID, A friendly name for a COM Class which can be used in a similar manner as CLSID, In our previous example, we looked at Windows Script Host Class and used a powershell script to call it by its ProgID, which was Wscript.Shell
  • AppID - Application ID, globally unique identifier (GUID) that identifies a registry key that contains various configuration for an individual or a group of COM classes
  • IID - Interface ID Is globally unique identifier (GUID) that Identifies a COM Interface, In our previous example, we looked at IWshShell Interface which had the IID of - {F935DC21-1CF0-11d0-ADB9-00C04FD58A0B}

COM Object Creation Deep Dive

To understand how a COM object is built in a programmatic manner, and its step-by-step creation I recommend reading this article by Mohamed Fakroud(@T3nb3w).

Activation of a COM Object

When we want to use a certain Interface of a COM object, we cannot create an instance of that interface, but rather create an instance of the COM Class that implements that Interfaces. This process is called Activation.
Looking at different programming languages we have multiple functions that help us creating an instance, for example:
  • C / C++ - CreateInstance | CoCreateInstance | CoCreateInstanceEx
  • VBScript / JScript - CreateObject | ActiveXObject
  • Powershell - New-Object -ComObject
Some are low-level functions and others a high-level functions, but no matter which one of them you use, you must provide the CLSID or ProgID of the COM Class you wish to create an instance for, After that, there is a routine that locates the COM Object's server, performs some security checks and eventually if everything goes smoothly, creates the instance of the Interface.

In C++

1
hr = CoCreateInstance(
2
&CLSID_TaskbarList, // A Constant of a CLSID
3
NULL,
4
CLSCTX_INPROC_SERVER,
5
&IID_ITaskbarList3, // A Constant of an IID
6
(LPVOID *)&infoPtr->lpTaskbarList3);
Copied!
In Powershell
1
# Create Instance from CLSID
2
[System.Activator]::CreateInstance([Type]::GetTypeFromCLSID("56FDF344-FD6D-11d0-958A-006097C9A090"))
Copied!
When we use one of these functions to create an instance of a COM Class, we pass the CLSID or the ProgID of the relevant class as an argument.

You might ask yourself, how does it know where the function know where is the DLL or EXE of the COM class is located?

The Windows Registry is the primary location of information regarding COM Classes that are installed on the system, part of the Installation of each COM object, is to register itself and its relevant information in the registry.
Registration of a COM server is usually mandatory require modification of the registry, however there are methods to register a COM objects without modifying the registry using regsvr32.exe
The following registry keys are the main keys of interest regarding COM:
When we provide the CLSID or ProgID as an argument, control flow then passes to the SCM (Windows Service Manager) which handles the client's requests.
The SCM queries the relevant location in the registry, and searches for the Identifier we passed.
The registry key that contains the identifier we've passed has value that is mapped to the location of the COM server, so if for example we pass the CLSID - {72c57034-02c4-4e9f-bf9c-ca711031757e}, we'll find the the COM server that is mapped to that CLSID is located at|SystemRoot%\system32\windows.storage.dll

Service Manager Request Handling Sequence

  • A client requests an interface pointer to a COM object from the COM Library(ole32.dll) by calling a function such as CoCreateInstance with the CLSID of the COM object.
  • The COM Library queries the SCM to find the server that corresponds with the requested CLSID.
  • The SCM locates the server and requests the creation of the COM object from the class factory that is provided by the server.
  • If successful, the COM Library returns an interface pointer to the client.

COM Servers Activation Types

When a COM Server is registered (A.K.A adding registry keys with mapping to the server and configurations related to the server), it can specify how this server can be activated.
The various methods of activation are:
  • In-Process (InprocServer32) - The vast majority of native COM servers are activated In-Process which means that the COM server is a DLL file that will be loaded into the client process that instantiated the COM Object, In this activation method, the SCM returns the file path of the DLL that contains the object server implementation. The COM Library loads the DLL and queries it for its class factory interface pointer.
  • Out-of-Process / Local - (LocalServer32) When a COM object is configured as a Local Server it means that the server is an EXE file which will be executed as a different process than the Client that instantiated the COM Object, the SCM starts the local executable which registers a class factory on startup, and its interface pointer is available to the system and clients.
  • Out-of-Process / Remote - The remote aspect here refers to DCOM which is distributed COM, for now, think of it as COM over the network (DCE-RPC). It is the act of activating or accessing a COM object on a remote machine, The local SCM acquires a class factory interface pointer from the SCM that is running on a remote computer.

In-Process Server (DLL)

A very simplified diagram of Loading In-Processes Server
In-Process Server is a COM Server in the form of a DLL being loaded into the client process.
Here's a few important notes regarding this implementation
  • No Marshaling - Since it's just a loaded DLL, there is no marshaling of communication between the client and the server
  • No EDR Visibility - Most EDR / XDR solutions utilize hooking mechanism to monitor certain APIs or Interfaces, usually on commonly targeted processes, however to achieve this they are required to inject their DLL / Protection mechanism into the process they want to protect. And so, unless they inject to every process that spawns (which I highly doubt) they are probably blind to activity of In-Process COM objects in-terms of interface method monitoring.
  • No RPCSS - when using Out-of-Process (LocalServer) COM, or DCOM the communication is mediated via the RPCSS service which lives on one of the svchost.exe processes.

Example: Wscript.Shell

Lets use the most referenced COM object in the history of online COM Guides so if I fail to explain it properly you could search else where until you'll understand :)
Microsoft provides various COM objects, some were meant for automation purposes, and one of them is the Wscript.Shell Class.
Using this COM Class we can instantiate an object that will allow us to execute arbitrary commands in Jscript or other executables
👍
  • CLSID - {72C24DD5-D70A-438B-8A42-98424B88AFB8}
  • ProgID - WScript.Shell
To prove that Wscript.Shell is an In-Process server, we can query the COM class id (CLSID) in the registry and view its configuration like so:
1
Get-ChildItem -Path "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\{72C24DD5-D70A-438B-8A42-98424B88AFB8}"
Copied!
This query results in the following output:
As you can see it has a "Inprocserver32" key which we know refers to a DLL Server component, we can then see what is the Wscript.Shell Class Server DLL which is wshom.ocx Let's instantiate the class inside our Powershell.exe process, To do that, I'll open Powershell.exe and Process Hacker to show you how everything looks.
Activating the COM Class using the CLSID:
1
$comobj = [System.Activator]::CreateInstance([Type]::GetTypeFromCLSID("72C24DD5-D70A-438B-8A42-98424B88AFB8"))
Copied!
Or we can do that with the ProgID:
1
$comobj = [System.Activator]::CreateInstance([Type]::GetTypeFromProgID("WScript.Shell"))
Copied!
If we search for Powershell.exe currently loaded Module via ProcessHacker, we'll see that our COM Server has been loaded to the process
The ".ocx" extension means that this DLL is an ActiveX Control Object.
ActiveX is a technology largely rests on COM, for additional information about ActiveX refer to other sources as this is outside the scope of this article.
Now that we have an Instance of WScript.Shell we can use one of its methods, "Run" to execute a command. lets run notepad.exe
1
$comobj.Run("notepad.exe")
Copied!
And look, notepad.exe is the child of our process who loaded the COM Object, and not a child process of the usual wscript.exe or cscript.exe

Out-of-Process Server

For this activation type i'll use the MMC20 Application as an example, as it is also a very common example as well as a common lateral movement technique.
Note this COM object can be used both locally and remotely (remote machine)
In Out-of-process activation, "Call over the wire" occurs, hence marshaling and proxy stubs are used. To read further about it, check Microsoft's documentation.

Local Out-of-Process

The MMC20 Application CLSID - {49B2791A-B1AE-4C90-9B8E-E860BA07F889}
Lets query the registry again to prove it's an Out-of-process COM server and to understand what process to look for
1
Get-ChildItem -Path "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\{49B2791A-B1AE-4C90-9B8E-E860BA07F889}"
Copied!
Great, Now we know we should look for the creation of "mmc.exe" once we activate the COM Object, so let's activate it
1
$comobj = [System.Activator]::CreateInstance([Type]::GetTypeFromCLSID("49B2791A-B1AE-4C90-9B8E-E860BA07F889"))
Copied!
As expected, we can see the COM Server as mmc.exe, living under svchost.exe which spawned it, exactly as we described in the Activation of a COM Object Section.

Remote Out-of-Process

Remote activation should be mostly transparent to the user. I wont go in-depth in this article on DCOM however I will demonstrate a simple remote activation of mmc on a remote machine.
1
$comobj = [System.Activator]::CreateInstance([Type]::GetTypeFromCLSID("49B2791A-B1AE-4C90-9B8E-E860BA07F889", "10.10.0.1"))
Copied!
All that's changed is that I added a destination IP, notice that this is the case when using these .NET wrappers, when using Win32 APIs CoCreateInstance creates a local instance while CoCreateInstanceEx is used to create Remote Instances.
And as mentioned above The local service manager acquires a class factory interface pointer from the service manager that is running on a remote computer which it will then use to create the instance on the remote computer given he has the appropriate permissions.
Firewall Rules and Lack of adequate security permissions will prevent you from activating COM objects or even accessing them.

DLLSurrogate

COM Enables us to use a surrogate for our COM Server, which means running our COM Server on a host process outside of the client process.
You can use either the native host process by windows which is dllhost.exe or specify a surrogate of your own. The configuration for a surrogate is performed in the AppID Key in the registry.
To Identify if a COM Server uses a surrogate you can check it's AppID entry (if it has one) and look for the DLLSurrogate value
1
HKEY_LOCAL_MACHINE\SOFTWARE\Classes\AppID
2
   {AppID_GUID}
3
      DllSurrogate = path
Copied!
Many windows processes use dllhost.exe as a COM Surrogate to execute their COM Servers, you can identify which COM server is executed on an individual dllhost.exe by checking out its command-line argument

0x03: COM Security

This section is an important one, as it details the aspects and defaults of the Components Object Model security.
Whether you succeed in creating or using a COM object will depend on upholding the security pre-requisites that COM entails, so understanding how COM authenticate users and how it checks for authorization of access to other COM object is essential.
Generally speaking, COM provide two forms of application security
  • Activation Security - Activation security determines whether a client can launch a server at all
  • Call Security - After a server has been launched, you can use call security to control access to a server's objects.
COM security relies on authentication (the process of verifying a caller's identity) and authorization (the process of determining whether a caller is authorized to do what it is asking to do)

How does COM enforce security

As mentioned above, COM has two form of application security, Activation security and Call Security. To enforce those, COM verifies the Identity of the caller and its authorization level.
These security measures can be defined as system-wide or process-wide.
Identity verification
COM will use one of its supported Security Packages, most commonly Kerberos v5 protocol and NTLMSSP to verify the identity of the caller. The authentication mechanism can be specified by the client or a default protocol would be used (Kerberos v5)
Authorization Verification
COM is a securable object, and access to it is enforced by checking the client's Access Control Entry(ACE) and the object's ACE in the format of a Security Descriptor Definition Language that is retrieved from the registry. The server achieves that by impersonating the client and attempting to access the object.
The object's ACE can be either the default value from the default COM settings or a custom when that will be defined in the AppID Key that is associated with the COM object.
Types of Authorization Checks
Referring back to the Activation & Call Security aspects, COM has various types of authorization checking.
  • AccessPermission - Describes the Access Control List (ACL) of the principals that can access instances of this class. This ACL is used only by applications that do not call CoInitializeSecurity.
  • LaunchPermission - Describes the Access Control List (ACL) of the principals that can start new servers for this class
Each COM object also has in part of its ACL its access rights which detail if the COM object can be executed or activated locally or remotely, this is an important factor in your ability to execute a COM object from a remote computer.
1
COM_RIGHTS_EXECUTE 1
2
COM_RIGHTS_EXECUTE_LOCAL 2
3
COM_RIGHTS_EXECUTE_REMOTE 4
4
COM_RIGHTS_ACTIVATE_LOCAL 8
5
COM_RIGHTS_ACTIVATE_REMOTE 16
Copied!

COM Security Defaults

COM uses pre determined security settings defaults unless specified other wise, those defaults includes authentication methods, authorization (by specifying an Access Control Entry), Impersonation settings and more.
  1. 1.
    Authentication - For authentication, COM chooses either Kerberos v5 protocol or NTLMSSP. The Kerberos protocol being the default choice.
  2. 2.
    Authentication Level - The default authentication level is RPC_C_AUTHN_LEVEL_CONNECT which means that at the call for a COM object, an authentication check is performed and once it performed it will not attempt to authenticate again. The default level can be changed by modifying the LegacyAuthenticationLevel registry value however this is not recommended because it will affect ALL COM servers in the system, instead you can specify the value inside the corressponding AppID Key in the AuthenticationLevel value.
  3. 3.
    Impersonation - COM uses the RPC_C_IMP_LEVEL_IDENTIFY impersonation level which enables the COM Server to impersonate the client's security token in order to perform checks against the Server's Access Control List (ACL).
  4. 4.
    Default Access Permissions - If the AccessPermission named value under AppID exists and has been set, that value is used. Otherwise, COM checks for a DefaultAccessPermission entry. If present, that value is used. If this value is not present, COM constructs an ACL that grants permissions to the server identity and the local system.
  5. 5.
    Software Restriction Policy - If the SRPTrustLevel named value under AppID exists and has been set, that value is used. Otherwise, the Software Restriction Policy (SRP) trust level is set to Disallowed (SAFER_LEVELID_DISALLOWED), which indicates that the application is run in a constrained environment and is disallowed from accessing any security-sensitive user privileges of the user.

Editing COM Security Settings

COM Security settings can be set as system-wide or process-wide, you can edit these settings by:
  • Directly modifying the relevant registry entries
  • Using DCOMCNFG.EXE

DCOM

Distributed COM is the same thing as COM, with some extra steps.
Essentially DCOM is a mechanism of execution COM objects outside your process. This can be either on your local machine on a different process than the client, or on a remote computer.
On a separate article I will dive in-depth into DCOM in the context of lateral movement, for now, here are few things I think are important to know when messing around with DCOM
  1. 1.
    COM and DCOM object are mostly the same.
  2. 2.
    DCOM operated over the Windows DCE-RPC Protocol (Transport layer may vary, can be HTTP, TCP, SMB/TCP, and more)
  3. 3.
    DCOM Object MUST have an AppID with defined AccessPermissions and LaunchPermissions otherwise will not be accessible remotely
  4. 4.
    Firewall Rules may Block DCOM traffic, which is over port 135 (RPC)
  5. 5.
    DCOM can be enabled or disabled on a given machine. Configuration can be located in the registry
If you can't wait to read about Lateral Movement with DCOM already, I highly suggest this article
Scorpiones Group | Lateral Movement using DCOM Objects - Guide to using DCOM Objects in order to Move Laterally in the Network.
Scorpiones Group

0x04: Conclusion & Next Steps

In this article we learned a great deal about the basics of Component Object Model, we got to learn:
In the upcoming articles I will discuss In-Depth about the offensive aspects of COM usage such as:
  • COM Hijacking
  • UAC Bypasses with COM objects
  • Lateral Movement with DCOM

Additional Read

At the terminology section you will find COM related terms that I use quite often in this article, take a glance at it when every you need. Additionally you can check Microsoft's COM Glossary page which can be found in the References Section.
At the References section you can find additional reading material about COM Internals which I personally enjoyed and think are a good read.

Terminology

Term
Description
CLSID
A globally unique identifier (GUID) that Identifies a COM Class
AppID
A globally unique identifier (GUID) that identifies a registry key that contains various configuration for an individual or a group of COM classes
ProgID
A friendly name for a COM Class which can be used in a similar manner as CLSID
out-of-process server (LocalServer32)
A server, implemented as an .EXE application, which runs outside the process of its client, either on the same computer or a remote computer.
Marshalling
Marshaling is the procedure for packaging the call stack for transmission from proxy to stub. Unmarshaling is the unpackaging that occurs at the receiving end.
in-process server (InprocServer32)
A server implemented as a DLL that runs in the process space of the client.
Class Factory
A COM object that implements the IClassFactory interface and that creates one or more instances of an object identified by a given class identifier(CLSID).
Activation
The process of loading an object in memory, which puts it into the running state.
COM object
A COM object is an instance of an object definition

References

A list of recommended reading material
Component Object Model (COM) - Win32 apps
docsmsft
Playing around COM objects - PART 1
Red Teaming's Dojo
Discovering and exploiting McAfee COM-objects (CVE-2021-23874)
the-deniss.github.io
GitHub - tyranid/oleviewdotnet: A .net OLE/COM viewer and inspector to merge functionality of OleView and Test Container
GitHub