CAN_RDP
Summary
FSProtect ACL Alias
CAN_RDP
Affected Object Types
Users, Groups
Exploitation Certainty
Certain
Description
The CAN_RDP
permission in Active Directory grants an account the ability to initiate and manage Remote Desktop Protocol (RDP) sessions on designated systems. This permission is useful for system administrators because it enables remote troubleshooting, maintenance, and management of servers and workstations, providing efficient access to critical infrastructure from remote locations. To use the CAN_RDP
permission, the user must be added to the local "Remote Desktop Users" group on the target system, which allows them to establish RDP connections.
However, if misconfigured, the CAN_RDP
permission can introduce significant security risks. An attacker who gains or abuses this permission could use RDP to bypass local access controls, execute arbitrary code, or exploit known vulnerabilities in the RDP service. Such exploitation might allow unauthorized remote access, facilitate lateral movement across the network, and lead to privilege escalation — potentially compromising sensitive data and critical systems.
Identification
Powershell
Active Directory Module
Using the ActiveDirectory PowerShell module, you can enumerate CAN_RDP
entries.
1. Find-CAN_RDP function
function Find-CAN_RDP {
[CmdletBinding()]
param( [string[]]$Target = $null, [string] $SearchBase = $null, [string] $OutputPath = "CAN_RDP.csv", [int] $TimeoutSec = 6 )
Import-Module ActiveDirectory -ErrorAction Stop
Write-Host "Gathering computer objects from Active Directory..."
try {
$computers = @()
if ($Target) {
foreach ($t in $Target) {
try {
$computers += (Get-ADComputer -Identity $t -ErrorAction Stop | Select-Object -ExpandProperty Name)
} catch {
Write-Warning "Target computer '$t' not found in AD: $($_.Exception.Message)"
}
}
if ($computers) { Write-Host "Using explicit target computer(s): $($computers -join ', ')" }
}
elseif ($SearchBase) {
Write-Host "Searching for computers within '$SearchBase'."
$computers = Get-ADComputer -Filter * -SearchBase $SearchBase -ErrorAction Stop | Select-Object -ExpandProperty Name
}
else {
Write-Host "Searching for all computers in the domain."
$computers = Get-ADComputer -Filter * -ErrorAction Stop | Select-Object -ExpandProperty Name
}
} catch {
Write-Error "Failed to retrieve computer objects from Active Directory: $($_.Exception.Message)"
return
}
if (-not $computers) {
Write-Output "No computer objects found to process."
return
}
$computers = $computers | Select-Object -Unique
Write-Host "Enumerating local 'Remote Desktop Users' on $($computers.Count) computer(s)..."
$results = New-Object System.Collections.Generic.List[object]
foreach ($c in $computers) {
try {
$opt = New-CimSessionOption -Protocol Dcom
$sess = New-CimSession -ComputerName $c -SessionOption $opt -OperationTimeoutSec $TimeoutSec -ErrorAction Stop
try {
# Don't use LocalAccount=TRUE to avoid excluding DCs (no local SAM)
$grp = Get-CimInstance -CimSession $sess -ClassName Win32_Group -Filter "Name='Remote Desktop Users'" -ErrorAction Stop
if (-not $grp) {
Write-Warning "'Remote Desktop 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 'Remote Desktop 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-Output "Results exported to '$OutputPath'"
} catch { Write-Error "Failed to export results to CSV: $($_.Exception.Message)"}
} else {Write-Output "No 'Remote Desktop Users' members found across the scanned computers."}
}
2. Scan all computers in the domain
Find-CAN_RDP
3. Scan a specific computer object
Find-CAN_RDP -Target FSWS01
4. Using SearchBase
to limit the Scope
Find-CAN_RDP -SearchBase "CN=Computers,DC=forestall,DC=labs"
.NET Directory Services
By leveraging PowerShell’s built-in .NET DirectoryServices namespace, you can enumerate CAN_RDP
entries without relying on any external modules or dependencies.
1. Find-CAN_RDPSimple function
function Find-CAN_RDPSimple {
[CmdletBinding()]
param( [string]$Target,[string]$SearchBase,[string]$OutputPath = "CAN_RDP.csv", [int] $TimeoutSec = 6 )
Write-Verbose "Building list of computers via LDAP (no AD module)..."
try {
$entries = @()
if ($Target) {
try {
$targetEntry = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$Target")
$oc = @($targetEntry.Properties["objectClass"])
if ($oc -and ($oc -contains "computer")) {
$entries = @($targetEntry)
Write-Verbose "Target is a single computer DN: $Target"
}
else {
Write-Verbose "Target treated as container/OU. Enumerating descendant computers under $Target"
$searcher = [System.DirectoryServices.DirectorySearcher]::new($targetEntry)
$searcher.Filter = "(objectCategory=computer)"
$searcher.PageSize = 1000
[void]$searcher.PropertiesToLoad.AddRange(@("distinguishedName","dNSHostName","name"))
$hits = $searcher.FindAll()
$entries = foreach ($h in $hits) { try { $h.GetDirectoryEntry() } catch { } }
}
} catch {
Write-Error "Failed to bind to Target DN '$Target': $_"
return
}
}
else {
$root = New-Object System.DirectoryServices.DirectoryEntry("LDAP://RootDSE")
$baseDN = if ($SearchBase) { $SearchBase } else { $root.Properties["defaultNamingContext"].Value }
$base = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$baseDN")
$searcher = [System.DirectoryServices.DirectorySearcher]::new($base)
$searcher.Filter = "(objectCategory=computer)"
$searcher.PageSize = 1000
[void]$searcher.PropertiesToLoad.AddRange(@("distinguishedName","dNSHostName","name"))
$hits = $searcher.FindAll()
$entries = foreach ($h in $hits) { try { $h.GetDirectoryEntry() } catch { } }
}
if (-not $entries -or $entries.Count -eq 0) {
Write-Output "No computer objects found to process."
return
}
$computers =
$entries |
ForEach-Object {
$dns = $_.Properties["dNSHostName"]
$nm = $_.Properties["name"]
if ($dns -and $dns.Count -gt 0 -and $dns[0]) { [string]$dns[0] }
elseif ($nm -and $nm.Count -gt 0 -and $nm[0]) { [string]$nm[0] }
} |
Where-Object { $_ } |
Select-Object -Unique
} catch {
Write-Error "LDAP enumeration failed: $_"
return
}
Write-Host "Enumerating local 'Remote Desktop Users' on $($computers.Count) computer(s)..."
$results = New-Object System.Collections.Generic.List[object]
foreach ($c in $computers) {
try {
$opt = New-CimSessionOption -Protocol Dcom
$sess = New-CimSession -ComputerName $c -SessionOption $opt -OperationTimeoutSec $TimeoutSec -ErrorAction Stop
try {
$grp = Get-CimInstance -CimSession $sess -ClassName Win32_Group -Filter "Name='Remote Desktop Users'" -ErrorAction Stop
if (-not $grp) {
Write-Warning "'Remote Desktop 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 'Remote Desktop 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-Output "Results exported to '$OutputPath'"
} catch { Write-Error "Failed to export results to CSV: $($_.Exception.Message)" }
}
else { Write-Output "No 'Remote Desktop Users' members found across the scanned computers."}
}
2. Scan all Computers in the domain
Find-CAN_RDPSimple
3. Scan specific computer object
Find-CAN_RDPSimple -Target "CN=fssql,CN=Computers,DC=Forestall,Dc=labs"
4. Using SearchBase
to limit the Scope
Find-CAN_RDPSimple -SearchBase "CN=Computers,DC=forestall,DC=labs"
Computer Management
Note: This edge cannot be identified directly using ADUC
but can be identified using Computer Management.
1. Open Computer Management.
2. From the Action menu, choose "Connect to Another Computer" if you need to manage a different machine.
3. Select the desired computer to manage.
4. In the Computer Management window, navigate to Local Users and Groups.
5. Open "Remote Desktop Users" under Local Users and Groups.
6. In the Members list, locate the users and groups that have access.
7. Click OK to close the dialogs.

Exploitation
Windows
Using the Remote Desktop Client on Windows (mstsc.exe):

Linux
Using xfreerdp
on Linux (example from Kali):
xfreerdp3 /v:<ip> /u:<user> /p:'<pass>' /d:<domain>
Example:
xfreerdp3 /v:192.168.121.110 /u:adam /p:'Temp123!' /d:forestall.labs

Mitigation
You can mitigate risks associated with CAN_RDP
by removing unwanted users or groups from the local "Remote Desktop Users" group on the target machine.
1. Open Computer Management.
2. From the Action menu, choose "Connect to Another Computer" if you need to manage a different machine.
3. Select the desired computer to manage.
4. In the Computer Management window, navigate to Local Users and Groups.
5. Open "Remote Desktop Users."
6. In the Members list, select any unwanted user or group and click Remove to revoke access.
7. Click OK to close the dialogs.

Detection
Adding new Access Control Entries (ACEs) on Active Directory objects changes the ntSecurityDescriptor
attribute of the objects themselves. These changes can be detected using Event IDs 5136 and 4662 to identify potentially risky modifications. Event ID 4624 indicates a successful logon to a Windows system (Logon Type 10 refers to RemoteInteractive), while 1149 indicates a Remote Desktop (RDP) logon.
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
4624
An account was successfully logged on.
Logon Type (type 10 refers to the RemoteInteractive), Subject, New Logon
https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-10/security/threat-protection/auditing/event-4624
1149
User Authentication succeeded
User, Computer
https://www.cybertriage.com/artifact/terminalservices_remoteconnectionmanager_log/terminalservices_remoteconnectionmanager_operational_1149/
References
Last updated
Was this helpful?