Skip to main content

Alternative Execution: A Macro Saga (part 5)

Thanks for returning to the series! Over the last few posts we discussed making use events from the InkPicture, Windows Media Player, and System Monitor controls to obtain macro execution, and ultimately code execution. Then we discussed how to remediate these issues with group policy changes that would block macro execution and/or ActiveX control rendering entirely, as well as implementing custom ActiveX Kill Bit settings to neutralize the usage of the previously mentioned controls.

In this post, we’ll be covering a process I used to continue scraping away at ActiveX control loading in Microsoft Office documents.


I had been aware of malware making use of InkPicture controls to obtain execution for a while, but while working through the first phase of research I had no idea if any of the other controls could be used the same way. Since there did not appear to be any research on this topic, I walked through each of the embeddable controls listed in Office (e.g. Excel, Word, etc) to see which had methods and/or event handlers accessible to users. Although several of them had accessible methods and event handlers, these three were the only ones I could find that had event handlers that could be used to trigger execution at some point after opening a word doc.

As it turns out, some of the ActiveX controls that could have been fun to tinker with were unusable. Specifically, if you’d select them you’d receive a notice that they were banned via policy:

The policy settings in question are directly related to applied ActiveX Kill Bits that we walked through in the 4th blog in this series. There may have been a few ActiveX controls that I missed, but it appeared that the three we covered are the only ones that Office allows you to insert into a document.

When I originally worked through the ActiveX controls I was unaware of how to apply Kill Bits, and as a result wasn’t aware what was strictly disallowed. Also, I was unaware that the Kill Bit policy settings prevented insertion of banned ActiveX controls in addition to rendering them, but suspected perhaps I could insert one of these controls manually. During most of the previous blog posts I had been making use of Doc97 Word documents, however as I’m sure you’re aware both the DOC and DOCM Word formats are Zip files.


The Word DOCM format is essentially a Zip file containing a directory structure, XML files, image content, binary blobs, and other miscellaneous files that make up the modern Word format. To poke around, let’s make ourselves a new Word doc, insert a Windows Media Player Control, double-click to populate the VBA project, and save as a Macro-Enabled Document file (DOCM).

Once we have this file saved, let’s drop this file in a Linux VM, and unzip it.

Immediately you should notice files like word/vbaProject.bin, and word/activeX/activeX1.xml. The vbaProject.bin file is a binary blob file representing the VBA Project. This includes both the source code, and a compiled copy of the VBA project that you saved with this document. As interesting as that file may seem, it’s not what we’re after. Let’s take a look at that activeX1.xml file:

Interesting, so we see there’s a classid (in other words CLSID) in that file: 6BF52A52-394A-11D3-B153-00C04F79FAA6. Not too surprisingly, that’s the CLSID for Windows Media Player Control:

So the first thing I wondered was, could I insert a banned control and have it render? I tried using the Microsoft Scriptlet Component control (CLSID: AE24FDAE-03C6-11D1-8B76-0080C744F389), and it failed to load correctly, due to it being banned. Then I attempted to swap in the CLSID for the System Monitor Control (CLSID: C4D2D8E0-D1DD-11CE-940F-008029004347), and lo and behold the document rendered! Let’s take a look at how to do this.

Here’s the activeX1.xml file after having been modified with the CLSID for System Monitor Control.

Now we zip up that folder.

And let’s open that Doc2.docm file:

It looks like it hasn’t been changed. Let’s click the “Enable Content” button and see what happens:

That looks like what we’re expecting. If we remember back to the 4th blog in this series, the reason why the document shows the Windows Media Player (WMP) Control initially is because Word first loads a cached image for the WMP Control when the document opens, but once we click “Enable Content” the ActiveX control is loaded and System Monitor Control is rendered correctly.


At some point I got to thinking something along the lines of “If all I have to do is plug in a CLSID, can I just plug in an arbitrary CLSID that’s NOT in Word/Excel’s list? Can I get ANY ActiveX/COM object to load?” Well, as it turns out, yes. If you can find a COM Class that Office applications are willing to render, it will load, provided that it’s not in the Kill Bit list. This basically means that the Office contains a list of “good” ActiveX controls that Microsoft allows you to insert through the UI, but that you’re not limited to incorporating them in Word, Excel, Visio, PowerPoint, etc.

Now the hard part begins: finding usable controls. If you remember from the 3rd blog in this series, there are a LOT of COM classes. Just look at the CLSID or ProgID count:

You could manually insert every CLSID into the activeX1.xml file in our sample Word document, zip it up and test it, but that would take FOREVER. You could also try picking a CLSID at random, or even trying to look at ProgIDs (e.g. friendly COM class name) until you find one that looks like it would work. That would also take a really long time.

And I’ll be completely honest about COM object enumeration: there’s no good way to do it. Back in 2016/2017 several researchers were looking into using DCOM (e.g. Distributed COM) for the purposes of lateral movement. I joined in privately, and identified quite a few COM objects that could be instantiated on remote hosts for lateral movement, but in the process learned a few things about COM:

  1. You never know what will happen when you load a COM object.

  2. Sometimes the COM object will crash your loading app.

  3. In other cases COM will cause your loading app to hang.

  4. When you instantiate a COM class, you load a DLL, which may spawn a binary. This can make it difficult to determine which COM class corresponded to which application.

The simplest approach involves using PowerShell to enumerate all subkeys in HKCR\CLSID:

And then to load them serially:

Get-ChildItem -Path “Registry::HKEY_CLASSES_ROOT\CLSID”|split-path -leaf | % {

$clsid = $_;

try {

$com = [Type]::GetTypeFromCLSID($clsid);

$inst = [System.Activator]::CreateInstance($com)

Write-Host “[+] $clsid Loaded”

} catch {

Write-Host “[-] $clsid Failed”



This will take you a long way, but you’ll eventually realize you need to output this to a file, and build a CLSID Deny List for both COM classes that will cause PowerShell to crash, as well as hang upon instantiation before you can run the script and walk away for a few hours. Additionally, you’ll absolutely want to run this in a throwaway VM, because this can cause some permanent changes (e.g. applications that open every time you open your computer) that are problematic to troubleshoot. Going through this enumeration process, I settled on 1059 CLSIDs that I could reliably load with PowerShell and avoid any hanging/crashing.

With this list, I automated the process of generating DOCM files. I started with a DOCM file in which I embedded a WMP control, unzipped it, and created a template file called activeX_template.xml:

Then I wrote a python script that ingested a file called clsids.txt, containing all the CLSIDs that were safe to load with PowerShell:

Running this quickly generated 1059 DOCM files to work with on my Windows 10 (using Office 2016) VM:

Now it’s a matter of opening each of them and seeing which ones load an ActiveX control. Yes, open all 1059 of them. If you spend 10 second opening each of them individually, it’ll take nearly 3 hours to get through them all. And that’s assuming maximum efficiency. There’s gotta be a better way to handle all these samples.


There are a lot of ways to approach this problem, but I decided that there was a quick way to handle this. Since our base file originally contained a WMP Control, there would be an image for this WMP control that renders first, before Word tries to load the ActiveX control corresponding to the CLSID we provided. If the control can be loaded that image of the WMP control will be replaced with the rendered control, but if the control can’t be loaded then the image will not change. So if we do the following, we’ll get a nice folder full of thumbnails to spot-check for differences:

  1. Change Word’s Macro and ActiveX settings to ALWAYS open/render (lowest security setting)

  2. Programmatically open each DOCM file

  3. Set a timer, and take a screenshot

  4. Repeat

This is doable with a little bit of PowerShell:


function screenshot([Drawing.Rectangle]$bounds, $path) {

$bmp = New-Object Drawing.Bitmap $bounds.width, $bounds.height

$graphics = [Drawing.Graphics]::FromImage($bmp)

$graphics.CopyFromScreen($bounds.Location, [Drawing.Point]::Empty, $bounds.size)





function RunTests() {

$ctr = 1

while ($ctr -lt 1060) {

# Adjust these bounds if the resolution changes

$bounds = [Drawing.Rectangle]::FromLTRB(0, 0, 1700, 956)

$f = “Doc$ctr.png”

$app = start-process ‘C:\Program Files (x86)\Microsoft Office\root\Office16\winword.exe’ “c:\users\user\desktop\testcases\Doc$ctr.docm” -passthru

Start-Sleep -s 5

screenshot $bounds “c:\users\user\desktop\testcases\results\$f”

taskkill /f /pid $app.Id




After I got this working, I let it run over night and came back the next morning to the following folder full of screenshots:

With these screenshots, it made it pretty easy to find what appear to be usable COM classes to load as ActiveX controls in Office. As you can see, Doc27.docm appears to be a candidate, given that the screenshot differs from all of the others. And since it’s the 27th item in the list of CLSIDs (e.g. 00021A20-0000-0000-C000-000000000046), it’s pretty easy to lookup to determine which COM class it corresponds to:

So it seems that we can load up Visio via an ActiveX control.

Unfortunately, this isn’t very practical, as it seems to just spawn Visio and I’d imagine most enterprises don’t install Visio on standard images. But it’s progress!


Throughout this blog post we spent some time getting to understand how we can coerce Office applications (Word specifically) to load COM classes of our choosing as ActiveX controls. Additionally, we spent some time discussing COM class enumeration, generation of a large batch of test cases for arbitrary COM class insertion into Word, and one method to handle/analyze the test cases. In the last two posts in this series we’ll cover some of the interesting controls found through this process, and how we can use them for code execution.