Bypassing AMSI via COM Server Hijacking

Microsoft’s Antimalware Scan Interface (AMSI) was introduced in Windows 10 as a standard interface that provides the ability for AV engines to apply signatures to buffers both in memory and on disk. This gives AV products the ability to “hook” right before script interpretation, meaning that any obfuscation or encryption has gone through their respective deobfuscation and decryption routines. If desired, you can read more on AMSI here and here. This post will highlight a way to bypass AMSI by hijacking the AMSI COM server, analyze how Microsoft fixed it in build #16232 and then how to bypass that fix.

This issue was reported to Microsoft on May 3rd, and has been fixed as a Defense in Depth patch in build #16232.

To get started, this is what an AMSI test sample through PowerShell will look like when AMSI takes the exposed scriptblock and passes it to Defender to be analyzed:

As you can see, AMSI took the code and passed it along to be inspected before Invoke-Expression was called on it. Since the code was deemed malicious, it was prevented from executing.

That begs the question: how does this work? Looking at the exports of amsi.dll, you can see the various function calls that AMSI exports:

One thing that stood out to me immediately is amsi!DllGetClassObject and amsi!DllRegisterServer, as these are all COM entry points and used to facilitate instantiation of a COM object. Fortunately, COM servers are easy to hijack since medium integrity processes default to searching the current user registry hive (HKCU) for the COM server before looking in HKCR/HKLM.

Looking in IDA, we can see the COM Interface ID (IID) and ClassID (CLSID) being passed to CoCreateInstance():

We can verify this by looking at ProcMon:

What ends up happening is that AMSI’s scanning functionality appears to be implemented via its own COM server, which is exposed when the COM server is instantiated. When AMSI gets loaded up, it instantiates its COM component, which exposes methods such as amsi!AmsiOpenSession, amsi!AmsiScanBuffer, amsi!AmsiScanString and amsi!AmsiCloseSession. If we can force the COM instantiation to fail, AMSI will not have access to the methods it needs to scan malicious content.

Since the COM server is resolved via the HKCU hive first, a normal user can hijack the InProcServer32 key and register a non-existent DLL (or a malicious one if you like code execution). In order to do this, there are two registry entries that need to be made:

When AMSI attempts to instantiate its COM component, it will query its registered CLSID and return a non-existent COM server. This causes a load failure and prevents any scanning methods from being accessed, ultimately rendering AMSI useless.

As you can see, importing the above registry change causes “C:\IDontExist” to be returned as the COM server:

Now, when we try to run our “malicious” AMSI test sample, you will notice that it is allowed to execute because AMSI is unable to access any of the scanning methods via its COM interface:

You can find the registry changes here:

Now that the bug is understood, we can go about looking at how Microsoft fixed it in build #16232. Since amsi.dll is AMSI’s COM server as well, diffing the two DLLs seemed like a good place to start. Looking at the diff, the AmsiInitialize function stood out as it likely contains logic to actually instantiate AMSI.

On the left, we have the old AMSI DLL and on the right, we have the newly updated AMSI DLL. As you can see, Microsoft appears to have removed the call to CoCreateInstance() and replaced it with a direct call to DllGetClassObject(). CoCreateInstance() can be defined as a high-level function used to instantiate COM objects that is implemented using CoGetClassObject(). After resolution finishes (partially via registry CLSID lookups) and the COM server is located, the server’s exported function “DllGetClassObject()” is called. By replacing CoCreateInstance with a direct call to amsi.dll’s DllGetClassObject() function, the registry resolution is avoided. Since AMSI is no longer querying the CLSID in the registry for the COM server, we are no longer able to hijack it.

Now that we know the fix, how do we go about bypassing it? Before proceeding, it is important to understand that this particular bug has been publicized and talked about since 2016. Essentially, scripting interpreters, such as PowerShell, load amsi.dll from the working directory instead of loading it from a safe path such as System32. Due to this, we can copy PowerShell.exe to a directory we can write to and bring back the vulnerable version of amsi.dll. At this point, we can either hijack the DLL off the bat, or we can create our same registry keys to hijack AMSI’s COM component. Since this vulnerable AMSI version still calls CoCreateInstance(), we can hijack the registry search order again.

First, we can verify that the patched amsi.dll version doesn’t query the COM server via the registry by creating a ProcMon filter for powershell.exe and AMSI’s CLSID. When PowerShell starts, you will notice no entries come up:

Next, we drop the vulnerable AMSI DLL and move PowerShell to the same directory. As you can see, it is now querying the registry to locate AMSI’s COM server:

With the vulnerable AMSI DLL back, we can now execute the COM server hijack:

Detection: Despite fixing this in build# 16232, it is still possible to execute this by executing a DLL hijack using the old, vulnerable AMSI DLL. For detection, it would be ideal to monitor (via command line logging, etc.) for any binaries (wscript, cscript, PowerShell) that are executed outside of their normal directories. Since the bypass to the fix requires moving the binary to a user writeable location, alerting on these executing in non-standard locations would catch this.

-Matt N.

Phishing Against Protected View

Microsoft Office has a security feature called Protected View. This feature opens an Office document that originates from the internet in a restricted manner. The idea is that it will prevent automatic exploitation of things such as OLE, Flash and ActiveX by restricting Office components that are allowed to execute. In 2016, Microsoft Patched a bug in Protected View around Excel Add-in files via CVE-2016-4117. @HaifeiLi has done some great research in this area, which you can read about here. MWR Labs also has a great white paper on understanding the Protected View Sandbox, which you can read about here. In this post, I will highlight some techniques you can employ to circumvent Protected View while still having access to the techniques us red teamers have grown to know and love. 

In my experience, end users are less likely to exit Protected View than they are to click through an Office dialogue box. I believe the reason for this is that they can access the document’s content while in Protected View, which is all they really need. When phishing, reducing the number of clicks for a user is always helpful. Protected View presents one additional click; if we can get rid of it, the better off we will be.

Full Disclosure: These were reported to MSRC on April 20th, 2017 and all of these have been deemed not a security issue. Features, not bugs 😉

Before I get into these techniques, it’s important to understand the normal behavior. Attackers often use a number of tricks to get code-execution on a target system. This often ranges from Office macros, to OLE objects and Excel formula injection via DDE. If we embed a LNK into an Excel document via OLE, we will see this locally:


Now, if we host the above document, Protected View will activate and the embedded OLE object will not be able to activate via a double-click until Protected View is exited:

This is what should happen when a document comes in from the internet; things such as OLE, ActiveX and DDE should be blocked until “Enable Editing” is clicked.

Now that we know what normal Protected View behavior looks like, we can dive into some ways around it. The first one I want to cover is executing a file via OLE from a Publisher file. Like Word and Excel, Microsoft Publisher often comes with Microsoft Office and includes similar functionality, such as OLE embedding. Attackers often use LNK files embedded via OLE, so we will do the same in this example. Publisher offers many features to make the OLE object enticing to the user. For simplicity, I will not go into these features.

For this example, we will use a LNK payload that simply executes: “C:\Windows\System32\cmd.exe /c calc.exe”. I won’t go into embedding OLE inside Publisher either as it’s nearly identical to the other Office formats. If we host the Publisher file with the OLE embedded LNK, you will notice that Protected View does not activate. Clicking on the OLE object displays 1 prompt to the user:

Clicking “Open” will cause the LNK to execute:

As you can see, double clicking the OLE object resulted in the LNK being executed (after a “Open File” prompt). Normally, Protected View would have prevented the OLE object from being activated until the user explicitly exited it.

Next, we will go into OneNote. OneNote allows for attaching files to note files. LNK files look a bit weird when attached to OneNote, so we will use a VBScript instead. For this example, this VBScript file will simply execute calc.exe via the Run method of the WScript.Shell COM object. For simplicity, I won’t go into dressing the document up to entice the user.

If we host the OneNote file (.ONE) with the attached VBScript file, you will notice that Protected View does not activate. The user is presented with 1 dialogue:

Clicking “OK” will result in the VBScript being executed:


So far, we have Publisher files and OneNote files that don’t trigger Protected View, but allow for OLE embedding, or something similar. Finally, there are Excel Symbolic Link files. This file format somewhat restricts the content it can host. In my testing, SLK files will strip OLE objects and any existing macro when saved. Fortunately, there are still attacks like Excel Formula Injection via DDE. If you don’t know about this technique, you can read more about it here.

Normally, Protected View will prevent automatic cell updating, which renders this attack useless while in Protected View. If we add a malicious formula and save it as a Symbolic Link (.SLK) file, we can get around the Protected View portion of the attack.

In this example, the Excel formula will be something like this:

=cmd|‘ /C calc’!A0

It is important to note that the DDE injection attack does present the user with 2 security warnings. There may be additional functionality in Excel SLK outside of DDE that won’t prompt 2 security dialogues…I encourage you to use your imagination 🙂

If we save the file as a normal Excel file, you will notice that Protected View blocks the automatic “Enable” prompt, and requires the user to exit Protected View first:

Now, if we save the file as .SLK and host it, you will notice that Protected View is not activated, and the user is automatically presented with the “Enable, Disable” prompt. 

Clicking “Enable” will present the user with the following dialogue. I’ll be the first to say that users love to click “Yes” on this prompt 😉

Clicking “Yes” will result in the command being executed:

While the user is presented with 2 prompts for the .SLK attack, users are often less likely to exit Protected View then click on the displayed prompts. From a Red Team perspective, any way to get around Protected View is worth the investment in terms of payload delivery.

Prevention: I am not currently aware of a way to manually enroll Publisher, OneNote and .SLK files into Protected View. User awareness training is recommended. If your end users do not use OneNote and Publisher, one solution would be to uninstall these.

  • Matt N.