Logon Type 10
Event 4624 Logon Type value for RemoteInteractive (RDP) connections: the primary filter that distinguishes RDP logons from other network authentication in the Security event log
4778/4779
Event IDs for RDP session reconnect (4778) and disconnect (4779): record source IP and session duration, enabling full RDP session timeline reconstruction
Event 21
TerminalServices-LocalSessionManager Event 21: session logon: records the source IP of the RDP client, which Event 4624 does not always include reliably
Jump list
Windows artifact (at %APPDATA%\Microsoft\Windows\Recent\AutomaticDestinations) that records recent RDP destination hostnames: forensic evidence of where an attacker RDP'd to from a compromised machine

SponsoredRetool

Retool's new app builder is where AI-generated code ships safely

Building apps with AI is easy. Getting them to production safely is another story.

Start building for free today

RDP is ubiquitous in Windows environments: administrators use it daily to manage servers and workstations. This makes it an ideal lateral movement vector for attackers: RDP connections blend with legitimate traffic, and most organizations do not have specific detection logic that distinguishes attacker RDP from admin RDP.

The key differentiators: attacker RDP often comes from unusual source machines (a workstation connecting to a domain controller), occurs at unusual times (3 AM on a Saturday), uses newly created or compromised service accounts, and chains quickly from machine to machine (an attacker might RDP through 5 systems within 30 minutes).

Event IDs: Source Machine vs Destination Machine

RDP lateral movement generates events on BOTH the source and destination machines:

On the SOURCE machine (where the attacker initiates RDP):

Event 4648: Explicit credential logon
  Logged when credentials are explicitly provided for a connection
  Subject: The account running the RDP client
  Target: The account being used to authenticate to the remote system
  Fields: TargetServerName (remote host), ProcessName (typically mstsc.exe)

Fields to monitor:
  ProcessName contains mstsc.exe → RDP client
  LogonGuid → correlate with destination 4624 LogonGuid

On the DESTINATION machine (where the attacker lands):

Security Event 4624: Logon success
  LogonType = 10 (RemoteInteractive = RDP)
  AuthenticationPackageName: Negotiate or NTLM
  IpAddress: Source IP of the RDP connection

Security Event 4625: Logon failure (brute force)
  LogonType = 3 or 10
  FailureReason: "Unknown user name or bad password"

TerminalServices-LocalSessionManager Event 21: Session logon
  Source: Microsoft-Windows-TerminalServices-LocalSessionManager
  Event 21: Remote Desktop Services: Session logon succeeded
  Fields: User (domain\username), SessionID, Address (source IP)
  
TerminalServices-LocalSessionManager Event 22: Shell started
  Fires when the desktop shell loads (user is actively in the session)

TerminalServices-LocalSessionManager Event 25: Session reconnection
  When a previous RDP session is resumed from a new IP

Detection: SIEM Queries

Sentinel KQL: RDP logons from workstations to servers:

// Detect workstation-to-server RDP: unusual in legitimate environments
// (Admins should use PAWs/jump hosts, not their user workstations)
let workstations = DeviceInfo
    | where DeviceType == "Workstation"
    | distinct DeviceName;
let servers = DeviceInfo
    | where DeviceType == "Server"
    | distinct DeviceName;

SecurityEvent
| where EventID == 4624
| where LogonType == 10  // RemoteInteractive = RDP
| where TimeGenerated > ago(24h)
// Source is a workstation (the IP resolving to a workstation)
| join kind=inner workstations on $left.IpAddress == $right.DeviceName
// Destination is a server
| join kind=inner servers on $left.Computer == $right.DeviceName
| project TimeGenerated, SourceWorkstation = IpAddress,
    DestinationServer = Computer, TargetUserName, LogonType
| sort by TimeGenerated desc

Detect rapid lateral movement chain (RDP from N machines in short window):

// Alert: single account RDP'ing to many machines in 30 minutes
SecurityEvent
| where EventID == 4624
| where LogonType == 10
| where TimeGenerated > ago(24h)
| summarize
    MachineCount = dcount(Computer),
    MachineList = make_set(Computer),
    MinTime = min(TimeGenerated),
    MaxTime = max(TimeGenerated)
    by TargetUserName, bin(TimeGenerated, 30m)
| where MachineCount >= 3  // RDP to 3+ machines in 30 min = suspicious
| extend TimeSpanMinutes = datetime_diff('minute', MaxTime, MinTime)
| sort by MachineCount desc

TerminalServices events for source IP (better than 4624 for IP logging):

// TerminalServices events often have the source IP when 4624 shows only "127.0.0.1"
Event
| where Source == "Microsoft-Windows-TerminalServices-LocalSessionManager"
| where EventID in (21, 22, 25)  // Logon, shell start, reconnection
| extend EventData = parse_xml(EventData)
| extend
    User = tostring(EventData.UserData.EventXML.User),
    SessionID = tostring(EventData.UserData.EventXML.SessionID),
    Address = tostring(EventData.UserData.EventXML.Address)
| where Address != "LOCAL"  // Exclude local sessions
// Enrich with geolocation and alert on unusual source IPs
| summarize count() by User, Address, Computer, EventID
| sort by count_ desc

RDP brute force detection:

SecurityEvent
| where EventID == 4625  // Logon failure
| where LogonType in (3, 10)  // Network or RemoteInteractive
| where TimeGenerated > ago(1h)
| summarize
    FailureCount = count(),
    UniqueAccounts = dcount(TargetUserName),
    Accounts = make_set(TargetUserName, 10)
    by IpAddress, Computer
| where FailureCount > 20  // > 20 failures in 1 hour from same IP
| sort by FailureCount desc
// > 20 failures: brute force threshold
// Many unique accounts from same IP: credential stuffing
Free daily briefing

Briefings like this, every morning before 9am.

Threat intel, active CVEs, and campaign alerts, distilled for practitioners. 50,000+ subscribers. No noise.

Registry and Forensic Artifacts for RDP History

RDP connection history on source machine (attacker's machine):

# MRU (Most Recently Used) RDP connection list:
# Shows where an attacker has connected FROM this machine
Get-ItemProperty -Path 'HKCU:\Software\Microsoft\Terminal Server Client\Servers\*' |
  Select-Object PSChildName, UsernameHint
# Output: server names and the username used to authenticate

# Default.rdp file (saved RDP configuration):
Get-Content "$env:USERPROFILE\Documents\Default.rdp" -ErrorAction SilentlyContinue
# May contain previously used hostnames and credentials (if saved)

# Jump list for mstsc.exe (lists recently connected hosts):
$jumplist = "$env:APPDATA\Microsoft\Windows\Recent\AutomaticDestinations"
Get-ChildItem $jumplist -Filter "*mstsc*" | Select-Object Name, LastWriteTime
# Parse with JumpListExplorer (Eric Zimmerman) for full history

RDP session timeline reconstruction from event logs:

# Extract full RDP session history from Security log:
Get-WinEvent -LogName Security |
  Where-Object { $_.Id -in (4624, 4625, 4634, 4647, 4778, 4779) } |
  Where-Object { $_.Properties[8].Value -in (3, 10) -or $_.Id -in (4778, 4779) } |
  Select-Object TimeCreated, Id,
    @{N='Account'; E={$_.Properties[5].Value}},
    @{N='SourceIP'; E={$_.Properties[18].Value}},
    @{N='LogonType'; E={$_.Properties[8].Value}} |
  Sort-Object TimeCreated |
  Format-Table -AutoSize

# Extract TerminalServices session events:
Get-WinEvent -LogName 'Microsoft-Windows-TerminalServices-LocalSessionManager/Operational' |
  Where-Object { $_.Id -in (21, 22, 24, 25) } |
  Select-Object TimeCreated, Id, Message |
  Sort-Object TimeCreated

Hardening RDP to Limit Lateral Movement

Restrict RDP access via GPO:

GPO path:
Computer Configuration > Windows Settings > Security Settings >
Local Policies > User Rights Assignment >

"Allow log on through Remote Desktop Services":
  Remove: Users, Guests
  Add: Domain Admins, Remote Desktop Users (specific named group)
  
"Deny log on through Remote Desktop Services":
  Add: Local account (prevents local user accounts from RDP-ing)
  This blocks pass-the-hash lateral movement using local admin credentials

Require Network Level Authentication (NLA):

GPO path:
Computer Configuration > Administrative Templates >
Windows Components > Remote Desktop Services > Remote Desktop Session Host >
Security >
"Require use of specific security layer for remote connections"
Setting: SSL (forces TLS: prevents some NTLM relay attacks)

"Require user authentication for remote connections by using NLA"
Setting: Enabled
# NLA authenticates before creating the desktop session: failed auth
# never gets to the Windows login screen

Deploy a dedicated RDP jump server (bastion host):

Instead of allowing direct RDP to any machine from any machine, require all RDP traffic to flow through a monitored bastion host:

Firewall rule: Block inbound TCP 3389 to all machines EXCEPT the bastion host
Firewall rule: Allow TCP 3389 from bastion-host IP to internal servers

Bastion host: Enhanced monitoring, MFA required, all sessions recorded
All RDP must go through: USER → Bastion → Target server

This creates a single choke point where you:
1. Enforce MFA on the jump server login
2. Log all session establishment
3. Record sessions (many PAM tools do this)
4. Alert on off-hours access

The bottom line

RDP lateral movement leaves a clear trail: Event 4624 with Logon Type 10 (destination machine) and Event 4648 with mstsc.exe as ProcessName (source machine) for each hop. TerminalServices-LocalSessionManager Events 21/22/25 provide the source IP more reliably than 4624. Detection logic: alert on workstation-to-server RDP, single account RDP'ing to 3+ machines in 30 minutes, and RDP failure bursts above 20 per hour per source IP. Harden by restricting RDP User Rights Assignment to specific named groups, enabling NLA, and routing all RDP through a monitored jump server with MFA.

Frequently asked questions

What Windows event IDs indicate RDP lateral movement?

On the destination (target) machine: Event 4624 with Logon Type 10 (RemoteInteractive) records successful RDP logins; TerminalServices-LocalSessionManager Events 21 (session logon), 22 (shell start), and 25 (reconnection) record the source IP more reliably. On the source machine: Event 4648 (explicit credential logon) with ProcessName = mstsc.exe records when an account's credentials were used to initiate an RDP connection to another machine. HKCU\Software\Microsoft\Terminal Server Client\Servers records the MRU list of machines connected to.

How do you detect RDP brute force attacks?

Monitor Event 4625 (logon failure) with Logon Type 3 or 10: alert when a single source IP generates more than 20 failures within 60 minutes. If the failures target many different usernames from the same source IP, it indicates credential stuffing. For geographically impossible logons, correlate successful Event 4624 Type 10 events against the user's previous login locations using IP geolocation data.

What are the Windows event IDs that indicate RDP session activity?

RDP session events across multiple logs: Security log — Event 4624 (successful logon) with Logon Type 10 (RemoteInteractive) indicates RDP login; Event 4634 (logoff) with Logon Type 10 closes the session; Event 4625 (failed logon) with Logon Type 10 indicates failed RDP attempts. TerminalServices-LocalSessionManager\Operational log — Event 21 (session logon succeeded), Event 23 (session logoff), Event 25 (session reconnection), Event 24 (session disconnection). TerminalServices-RemoteConnectionManager\Operational log — Event 1149 records the source IP and username for every RDP connection attempt regardless of success. Event 1149 is particularly valuable because it is logged before authentication, capturing brute force attempts that do not appear in the security log.

How do I restrict RDP access to specific source IPs or users?

For server RDP: in Windows Firewall with Advanced Security, modify the Remote Desktop inbound rule (TCP 3389) to restrict the Remote IP Address to specific source IPs or subnets — this blocks all other RDP sources at the firewall level. For user-based restriction: add specific users or groups to the 'Allow log on through Remote Desktop Services' user right (Computer Configuration > Windows Settings > Security Settings > Local Policies > User Rights Assignment). Remove the default 'Remote Desktop Users' group and add only specifically approved users. On domain controllers: disable RDP entirely unless absolutely required; use console access or Server Manager remote management instead.

What is RDP session hijacking and how do you prevent it?

RDP session hijacking (tscon.exe method) allows an attacker with SYSTEM privileges to take over a disconnected RDP session of another user without knowing their password. The attacker runs 'tscon [session ID] /dest:[their session]' which switches the active session to the target's disconnected session. Prevention: configure 'Set time limit for disconnected sessions' GPO to 15 minutes maximum (this terminates sessions rather than leaving them in disconnected state indefinitely); restrict use of tscon.exe via WDAC or AppLocker to SYSTEM only; and monitor for tscon.exe execution via Sysmon Event ID 1 from non-SYSTEM processes.

How do you reconstruct a complete attacker RDP lateral movement chain across multiple machines when logs are fragmented across different Windows event log sources?

Normalize all log sources to a common timeline by exporting Security event logs (Events 4624/4648), TerminalServices-LocalSessionManager logs (Events 21/22/25), and TerminalServices-RemoteConnectionManager logs (Event 1149) from each machine into a central SIEM or a local timeline tool such as log2timeline/Plaso. Use Event 1149 as the anchor: it fires before authentication and records source IP and username for every RDP connection attempt, making it more reliable than 4624 for capturing the origin of each hop. Join each destination machine's Event 21 (session logon) with the source machine's Event 4648 (explicit credential logon with ProcessName = mstsc.exe) by matching the LogonGuid field, which propagates across both events when Kerberos is used. For NTLM-authenticated RDP hops where LogonGuid is empty, correlate by matching the timestamp of Event 4648 on the source with the timestamp of Event 21 on the destination within a five-second window, combined with the source IP in Event 21 matching the NIC IP of the originating machine.

Sources & references

  1. MITRE ATT&CK T1021.001: Remote Desktop Protocol
  2. Microsoft: RDP Session Event IDs Reference
  3. DFIR.training: RDP Forensics Artifacts

Free resources

25
Free download

Critical CVE Reference Card 2025–2026

25 actively exploited vulnerabilities with CVSS scores, exploit status, and patch availability. Print it, pin it, share it with your SOC team.

No spam. Unsubscribe anytime.

Free download

Ransomware Incident Response Playbook

Step-by-step 24-hour IR checklist covering detection, containment, eradication, and recovery. Built for SOC teams, IR leads, and CISOs.

No spam. Unsubscribe anytime.

Free newsletter

Get threat intel before your inbox does.

50,000+ security professionals read Decryption Digest for early warnings on zero-days, ransomware, and nation-state campaigns. Free, daily, no spam.

Unsubscribe anytime. We never sell your data.

Eric Bang
Author

Founder & Cybersecurity Evangelist, Decryption Digest

Cybersecurity professional with expertise in threat intelligence, vulnerability research, and enterprise security. Covers zero-days, ransomware, and nation-state operations for 50,000+ security professionals every morning.

Black Hat Giveaway

Win a $2,495 Black Hat pass.

Full-access to Black Hat USA 2026 in Las Vegas. Subscribe free to enter.

Joins Decryption Digest daily briefing. Unsubscribe anytime.

Giveaway: Black Hat USA 2026 Full-Access Pass ($2,495 value)

Details →
Daily Briefing

Subscribe to enter the giveaway

Every subscriber is automatically entered. You also get daily threat intel every morning: zero-days, ransomware, and nation-state campaigns. Free. No spam.

Already subscribed? You're already entered.

Giveaway

Win a $2,495 Black Hat USA 2026 pass.