# 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

```powershell
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

```powershell
Find-CAN_RDP
```

**3.** Scan a specific computer object

```powershell
Find-CAN_RDP -Target FSWS01
```

**4.** Using `SearchBase` to limit the Scope

```powershell
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

```powershell
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

```powershell
Find-CAN_RDPSimple
```

**3.** Scan specific computer object

```powershell
 Find-CAN_RDPSimple -Target "CN=fssql,CN=Computers,DC=Forestall,Dc=labs"
```

**4.** Using `SearchBase` to limit the Scope

```powershell
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.

![Computer Management](/files/cccEY9fY6Oe40YeJnSHd)

## Exploitation

### Windows

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

![Remote Desktop From Windows](/files/Wn1AWUXBQ99a5R2H5qAY)

### Linux

Using `xfreerdp` on Linux (example from Kali):

```bash
xfreerdp3 /v:<ip> /u:<user> /p:'<pass>' /d:<domain>
```

Example:

```bash
xfreerdp3 /v:192.168.121.110 /u:adam /p:'Temp123!' /d:forestall.labs
```

![rdp using xfreerdp3](/files/B6PJ0uGSNjrNq4z2L3eC)

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

![Computer Management](/files/cccEY9fY6Oe40YeJnSHd)

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

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

* [Understanding Remote Desktop Protocol - Microsoft Learn](https://learn.microsoft.com/en-us/troubleshoot/windows-server/remote/understanding-remote-desktop-protocol)
* [Passing the Hash with Remote Desktop - Kali Linux Blog](https://www.kali.org/blog/passing-hash-remote-desktop/)
* [Pass-the-Hash Attack Explained - Hornetsecurity Blog](https://www.hornetsecurity.com/en/blog/pass-the-hash-attack/)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.forestall.io/fsprotect/edges/ad/can_rdp.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
