External Remote Services
Adversaries may leverage external-facing remote services to initially access and/or persist within a network. Remote services such as VPNs, Citrix, and other access mechanisms allow users to connect to internal enterprise network resources from external locations. Adversaries typically obtain valid credentials first via phishing, credential stuffing, or prior compromise, then authenticate to these services from external infrastructure. This technique covers VPN gateways (GlobalProtect, AnyConnect, Pulse Secure, SoftEther), Remote Desktop Protocol, Windows Remote Management, Citrix, VNC, SSH, and exposed container APIs (Docker daemon on TCP 2375/2376, Kubernetes API server on 6443, kubelet on 10250). Threat groups including LAPSUS$, Volt Typhoon, Ember Bear, OilRig, GALLIUM, Scattered Spider, APT41, and Sandworm Team have been observed abusing legitimate remote access mechanisms for initial access and persistent footholds. In containerized environments, adversaries may target exposed Docker APIs or Kubernetes management interfaces that accept anonymous or unauthenticated connections. Adversaries may also establish persistence through Tor hidden services using tools like ShadowLink, which may masquerade as legitimate Windows Defender components to forward inbound RDP connections over the Tor network.
What is T1133 External Remote Services?
External Remote Services (T1133) maps to the Persistence and Initial Access tactics — the adversary is trying to maintain their foothold in MITRE ATT&CK.
This page provides production-ready detection logic for External Remote Services, covering the data sources and telemetry it touches: Authentication: Authentication, Logon Session: Logon Session Creation, Network Traffic: Network Traffic Flow, Azure Active Directory Sign-in Logs, Microsoft Defender for Endpoint. The queries below are rated high severity at medium confidence, and ship for 7 SIEM platforms — KQL, SPL, Elastic, QRadar, Sumo, YARA-L, LogScale.
MITRE ATT&CK
- Tactic
- Persistence Initial Access
- Technique
- T1133 External Remote Services
- Canonical reference
- https://attack.mitre.org/techniques/T1133/
let PrivateRanges = dynamic(["10.", "172.16.", "172.17.", "172.18.", "172.19.", "172.20.",
"172.21.", "172.22.", "172.23.", "172.24.", "172.25.", "172.26.", "172.27.", "172.28.",
"172.29.", "172.30.", "172.31.", "192.168.", "127.", "::1", "fe80"]);
let RemoteAccessApps = dynamic([
"GlobalProtect", "Pulse Secure", "Cisco AnyConnect", "Fortinet SSL VPN",
"Check Point Remote Access VPN", "F5 BIG-IP APM", "Citrix Gateway",
"VMware Horizon", "RDP Gateway", "SoftEther VPN", "Juniper SSL VPN"]);
// Branch 1: Azure AD sign-ins to remote access applications from external IPs
let AADSignIns = SigninLogs
| where TimeGenerated > ago(24h)
| where ResultType == 0
| where AppDisplayName has_any (RemoteAccessApps)
| where not(IPAddress has_any (PrivateRanges))
| extend DetectionBranch = "AAD_VPN_Citrix_External"
| project TimeGenerated, AccountName = UserPrincipalName, SourceIP = IPAddress,
TargetService = AppDisplayName, DetectionBranch, Location,
RiskLevel = RiskLevelDuringSignIn;
// Branch 2: RDP logons (LogonType 10 = RemoteInteractive) from external IPs
let ExternalRDP = SecurityEvent
| where TimeGenerated > ago(24h)
| where EventID == 4624
| where LogonType == 10
| where IpAddress !in ("", "-", "127.0.0.1", "::1")
| where not(IpAddress has_any (PrivateRanges))
| extend DetectionBranch = "SecurityEvent_RDP_External"
| project TimeGenerated, AccountName = TargetUserName, SourceIP = IpAddress,
TargetService = "RDP_RemoteInteractive", DetectionBranch, Computer;
// Branch 3: Network logons (LogonType 3) from external IPs — covers WinRM, SMB, Net Use
let ExternalNetworkLogon = SecurityEvent
| where TimeGenerated > ago(24h)
| where EventID == 4624
| where LogonType == 3
| where IpAddress !in ("", "-", "127.0.0.1", "::1")
| where not(IpAddress has_any (PrivateRanges))
| where TargetUserName !endswith "$" // Exclude machine accounts
| extend DetectionBranch = "SecurityEvent_NetworkLogon_External"
| project TimeGenerated, AccountName = TargetUserName, SourceIP = IpAddress,
TargetService = "Network_WinRM", DetectionBranch, Computer;
// Branch 4: MDE endpoint-side remote logon telemetry
let MDERemoteLogons = DeviceLogonEvents
| where Timestamp > ago(24h)
| where ActionType == "LogonSuccess"
| where LogonType in ("RemoteInteractive", "Network")
| where RemoteIPType == "Public"
| extend DetectionBranch = "MDE_RemoteLogon_External"
| project TimeGenerated = Timestamp, AccountName, SourceIP = RemoteIP,
TargetService = strcat("MDE_", LogonType), DetectionBranch,
Computer = DeviceName;
union AADSignIns, ExternalRDP, ExternalNetworkLogon, MDERemoteLogons
| sort by TimeGenerated desc Multi-branch detection for T1133 External Remote Services abuse. Branch 1 monitors Azure AD SigninLogs for successful authentication to known VPN and Citrix applications from non-RFC1918 IPs. Branch 2 detects Security Event 4624 LogonType 10 (RemoteInteractive/RDP) from external IPs. Branch 3 detects Security Event 4624 LogonType 3 (Network/WinRM) from external IPs, excluding machine accounts. Branch 4 uses Microsoft Defender for Endpoint DeviceLogonEvents for endpoint-side telemetry filtered to RemoteIPType == Public. All branches exclude RFC 1918 private address ranges, loopback, and link-local addresses.
Data Sources
Required Tables
False Positives
- Legitimate remote workers connecting to corporate VPN or Citrix from home or hotel networks — the external IP is expected and authorized
- IT administrators using RDP or WinRM from authorized jump hosts or bastion servers with external-routable IPs
- Third-party vendors and contractors with documented remote access agreements connecting from their own infrastructure
- Cloud-hosted management planes (Azure DevOps agents, AWS Systems Manager, etc.) whose gateway IPs appear external
- Employees traveling internationally whose access from a foreign country IP triggers the detection despite valid authorization
Sigma rule & cross-platform mapping
The detection logic for External Remote Services (T1133) above is provided in a vendor-neutral
form so you can deploy it on any SIEM. The same logic is shipped here as native
KQL (Microsoft Sentinel / Defender), SPL (Splunk), Elastic (Elastic Security (EQL)), QRadar (IBM QRadar (AQL)), Sumo (Sumo Logic CSE), YARA-L (Google Chronicle / SecOps), LogScale (CrowdStrike LogScale (CQL)) queries. In Sigma terms, this detection targets the
following logsource:
logsource:
category: network_connection
product: windows Browse the community-maintained Sigma rules for this technique:
Platform-specific guides for T1133
References (11)
- https://attack.mitre.org/techniques/T1133/
- https://www.volexity.com/blog/2015/10/07/virtual-private-keylogging-cisco-web-vpns-leveraged-for-access-and-persistence/
- https://unit42.paloaltonetworks.com/hildegard-malware-teamtnt/
- https://www.trendmicro.com/en_us/research/20/f/xorddos-kaiji-botnet-malware-variants-target-exposed-docker-servers.html
- https://www.microsoft.com/en-us/security/blog/2025/02/12/the-badpilot-campaign-seashell-blizzard-subgroup-conducts-multiyear-global-access-operation/
- https://www.microsoft.com/en-us/security/security-insider/intelligence-reports/russian-threat-actors-dig-in-prepare-to-seize-on-war-fatigue
- https://media.defense.gov/2024/Feb/07/2003377460/-1/-1/0/CSA-PRC-CRITICAL-INFRASTRUCTURE.PDF
- https://learn.microsoft.com/en-us/windows/security/threat-protection/auditing/event-4624
- https://learn.microsoft.com/en-us/azure/active-directory/reports-monitoring/concept-sign-ins
- https://github.com/redcanaryco/atomic-red-team/blob/master/atomics/T1133/T1133.md
- https://sygnia.co/threat-intelligence-reports/velvet-ant/
Testing Methodology
Validate this detection against 4 adversary techniques from Atomic Red Team. Each test below lists the behaviour to exercise and the telemetry you should expect to see. Executable commands and cleanup steps are available with Pro.
- Test 1Enable and Connect via RDP to Generate LogonType 10 Event (Windows)
Expected signal: Windows Security EventID 4624 with LogonType=10 (RemoteInteractive) and IpAddress=127.0.0.1 in Security event log. Microsoft-Windows-TerminalServices-RemoteConnectionManager/Operational EventID 1149 recording the RDP connection with username and source address. Microsoft-Windows-TerminalServices-LocalSessionManager/Operational EventID 21 (session logon) and 22 (shell start) on successful session establishment.
- Test 2WinRM Network Logon to Generate LogonType 3 Event (Windows)
Expected signal: Windows Security EventID 4624 with LogonType=3 (Network) and AuthenticationPackageName=NTLM or Kerberos. Sysmon EventID 3 (Network Connection) from wsmprovhost.exe (WinRM provider host). Sysmon EventID 1 (Process Create) for wsmprovhost.exe. PowerShell ScriptBlock Log EventID 4104 for executed commands. Windows Remote Management log in Microsoft-Windows-WinRM/Operational.
- Test 3Query Exposed Docker API to Simulate TeamTNT Initial Access (Linux)
Expected signal: Docker daemon log (/var/log/docker.log or journalctl -u docker --since '5 minutes ago'): GET /version and GET /containers/json HTTP requests logged with source IP and timestamp. Network connection to TCP 2375 visible in ss -tnp or netstat output. Auditd syscall events for the accept() and read() syscalls if network auditing is enabled. Sysmon for Linux EventID 3 if deployed.
- Test 4SSH Repeated Failed Authentication Followed by Success (Linux)
Expected signal: Linux auth log (/var/log/auth.log on Debian/Ubuntu or /var/log/secure on RHEL/CentOS): multiple 'Invalid user nonexistentuser_N from 127.0.0.1' and 'Failed none for invalid user' entries. Sysmon for Linux EventID 3 (Network Connection) from ssh client process to port 22. Auditd USER_AUTH records for each failed attempt with res=failed. fail2ban log entries if deployed.
Response Playbook
Triage
- Geolocate and classify the SourceIP: perform reverse DNS lookup, WHOIS/ASN lookup, and check against threat intelligence feeds. Determine if it is a known corporate VPN exit node, residential ISP, cloud provider (AWS/Azure/GCP), VPS hosting provider, Tor exit node, or bulletproof hosting. Tor exit nodes and VPS providers with no prior history are highest risk.
- Verify account authorization: confirm whether this account is expected to use remote access from an external IP. Check HR records, VPN policy, approved contractor lists, and any active change tickets authorizing remote access for this user.
- Check for impossible travel: query SigninLogs or Security Events for this account over the past 4 hours. If the account authenticated from a geographically distant location earlier (e.g., New York then Moscow within 2 hours), treat this as high confidence credential compromise.
- Examine the pre-success authentication pattern: query Security Event 4625 from the same SourceIP in the 60 minutes prior to the 4624. Five or more failures from the same external IP before a success indicates credential stuffing or brute-force, not legitimate access.
- If LogonType 10 (RDP): immediately query DeviceProcessEvents or Sysmon Event ID 1 for processes spawned under this account after the logon timestamp. Watch for: cmd.exe, powershell.exe, whoami, net user, net localgroup, ipconfig /all, systeminfo — these are post-access discovery commands.
- Check whether the accessed system should be internet-facing at all: RDP and WinRM should not accept direct external connections in a well-hardened environment. If this system is directly reachable from the internet on port 3389 or 5985, that is itself a critical finding independent of whether this specific logon is malicious.
Containment
- If credential stuffing or unauthorized access confirmed: disable the compromised account in Active Directory immediately. For Azure AD accounts, also run Revoke-AzureADUserAllRefreshToken to invalidate all active sessions and tokens across all services.
- Block the SourceIP at the perimeter firewall and add to threat intelligence blocklist. If the IP belongs to a VPS or hosting provider CIDR used solely for adversary infrastructure, consider blocking the entire /24. Document the block with case reference.
- Isolate the accessed endpoint from the network using EDR network isolation (Microsoft Defender: Isolate Device action) or emergency VLAN change. Do NOT power off — volatile memory may contain adversary tooling, C2 configuration, or credential material.
- If VPN credential compromise confirmed: revoke VPN certificates, tokens, or gateway cookies for the account. Force certificate re-enrollment. For RADIUS-based VPN: disable the user's RADIUS attribute. For certificate-based VPN: revoke the client certificate from the CA.
- If exposed Docker API (port 2375) was accessed: stop the Docker daemon, rebind to Unix socket only (remove -H tcp://0.0.0.0:2375), enable TLS client certificate authentication if TCP access is required, audit all running containers for new images and modifications using 'docker diff' and 'docker history'.
- If Kubernetes API access confirmed: immediately rotate all service account tokens in the affected namespace, review and revoke ClusterRoleBindings that permit anonymous access, patch kubelet with --anonymous-auth=false, and audit the cluster for new pods, deployments, or cronjobs created by the adversary.
Evidence Collection
- Windows Security Event Log: EventID 4624 (successful logon), 4625 (failed logon), 4634/4647 (logoff), 4648 (explicit credential use from the accessed host) — full authentication timeline for the SourceIP and account
- Windows Security Event Log: EventID 4672 (special privileges assigned to new logon) — indicates if privileged access was obtained on the initial session
- Windows Security Event Log: EventID 4768/4769 (Kerberos TGT/service ticket requests) — post-logon Kerberos activity reveals what services the adversary accessed after initial remote access
- RDP-specific: Microsoft-Windows-TerminalServices-RemoteConnectionManager/Operational EventID 1149 — records the username and IP address for each RDP authentication attempt
- RDP-specific: Microsoft-Windows-TerminalServices-LocalSessionManager/Operational EventIDs 21 (session logon), 22 (shell start), 23 (session logoff), 24 (disconnect), 25 (reconnect) — full RDP session lifecycle
- RDP artifacts: C:\Users\<username>\AppData\Local\Microsoft\Terminal Server Client\Cache\*.bin — RDP bitmap cache from client side may contain screenshots of accessed session content
- Network logs: Firewall/proxy logs for the SourceIP covering the full session — identify all systems contacted, data volumes transferred, and any exfiltration over HTTP/HTTPS/FTP
- VPN/Citrix gateway logs: Extract authentication logs from the remote access gateway for the account and IP — compare with claimed access hours, geolocation, and device fingerprint
- Azure AD Sign-in logs: Full 90-day sign-in history for the compromised account — identify other anomalous access patterns, MFA bypasses, conditional access policy circumventions, and token theft indicators
Escalation Criteria
- ! Confirmed impossible travel: the same account authenticated from two geographically distant locations within a time window physically impossible to traverse (e.g., Europe and Asia within 1 hour)
- ! Post-logon discovery or credential dumping observed: execution of whoami, net user, net localgroup administrators, nltest /domain_trusts, or LSASS process access (Sysmon Event ID 10) within 10 minutes of the initial remote logon
- ! Lateral movement originating from the initially accessed host: subsequent remote logons (EventID 4624) or network connections from the accessed host to other internal systems using the initial account or pass-the-hash/pass-the-ticket techniques
- ! Source IP is a Tor exit node, known VPN anonymizer, or bulletproof hosting provider — adversary is actively concealing origin, significantly elevating threat confidence
- ! The compromised account is a domain administrator, service account, or shared privileged account — potential blast radius is organization-wide
- ! Ransomware precursor behaviors following access: volume shadow copy deletion (vssadmin delete shadows), backup agent termination, or bulk file enumeration across network shares
Investigation Guide
Forensic Artifacts
- >
Windows Security Event Log: EventID 4624 with LogonType 10 or 3, IpAddress field showing external source - >
Windows Security Event Log: EventID 4625 showing failed logon attempts from same external IP — establishes brute-force timeline - >
Windows Event Log: Microsoft-Windows-TerminalServices-RemoteConnectionManager/Operational EventID 1149 (RDP auth success with source IP and username) - >
Windows Event Log: Microsoft-Windows-TerminalServices-LocalSessionManager/Operational EventIDs 21/22/23/24/25 (full RDP session lifecycle) - >
Registry: HKLM\SYSTEM\CurrentControlSet\Control\Terminal Server — fDenyTSConnections (0 = RDP enabled), PortNumber (custom RDP port) - >
Registry: HKLM\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp — SecurityLayer and UserAuthentication (NLA enabled/disabled) - >
File System: C:\Users\<username>\AppData\Local\Microsoft\Terminal Server Client\Default\* — RDP connection MRU list on client-side - >
File System: C:\Users\<username>\AppData\Local\Microsoft\Terminal Server Client\Cache\*.bin — RDP bitmap cache (may reveal session screen content for forensic reconstruction) - >
Network perimeter: Firewall logs for TCP/3389 (RDP), TCP/5985-5986 (WinRM HTTP/HTTPS), TCP/22 (SSH), TCP/2375-2376 (Docker API), TCP/6443 (Kubernetes API), TCP/10250 (kubelet) - >
Container artifact: Docker daemon access log at /var/log/docker.log or journalctl -u docker — shows REST API calls including unauthenticated container listing and exec operations
Tuning Guidance
T1133 detection generates significant false positive volume due to the prevalence of legitimate remote work. Tuning approach: (1) Build and maintain an allowlist of authorized external access IP ranges — corporate VPN exit nodes, known contractor IP blocks, and cloud provider CIDR ranges for your specific cloud management tools. Apply these as exclusions to reduce noise. (2) For RDP-specific detection (LogonType 10 from external): if your environment enforces all RDP through an RDP Gateway or Citrix proxy, the only legitimate source IP for direct endpoint RDP is the gateway address. Block-list all other external RDP sources by default. (3) Use Conditional Access policies or equivalent to enforce named locations — sign-ins that bypass named location policies are higher confidence. (4) Baseline external access countries per user over 30 days via SigninLogs. Alert only on first-appearance of a new country in the 7-day window rather than all external logons. (5) For Docker API hunting: NEVER suppress alerts on port 2375 access from external IPs — this port should be universally blocked at the perimeter. Any allowed connection is critical regardless of the source IP appearing legitimate. (6) Integrate threat intelligence feeds covering known VPN anonymizer IPs, Tor exit nodes, and datacenter/hosting CIDR blocks (ASNs like AS14618 AWS, AS16509 AWS, AS8075 Azure are fine; hosting providers with no legitimate reason to reach your VPN should increase severity). (7) Correlate with T1110 detections: credential stuffing or spraying alerts followed within 30 minutes by a successful T1133 logon from the same IP should auto-escalate to critical and page on-call.
Hunting Queries
Hunt for brute-force-then-success patterns against external remote services. Identifies SourceIP and account combinations where 5+ failed logon attempts (EventID 4625) preceded a successful logon (EventID 4624 LogonType 3 or 10) within a 2-hour window. This pattern is the fingerprint of credential stuffing, password spraying (T1110.003), and brute-force attacks (T1110.001) used to gain T1133 access. High-confidence indicator — legitimate users rarely have 5+ failed logons immediately before success.
// Hunt: Brute-force to success — failed logons from external IP preceding a successful logon
let LookbackWindow = 7d;
let FailThreshold = 5;
let BruteForceWindow = 2h;
let PrivateRanges = dynamic(["10.", "172.16.", "172.17.", "172.18.", "172.19.", "172.20.",
"172.21.", "172.22.", "172.23.", "172.24.", "172.25.", "172.26.", "172.27.", "172.28.",
"172.29.", "172.30.", "172.31.", "192.168.", "127."]);
let SuccessfulLogons = SecurityEvent
| where TimeGenerated > ago(LookbackWindow)
| where EventID == 4624 and LogonType in (3, 10)
| where IpAddress !in ("", "-", "127.0.0.1", "::1")
| where not(IpAddress has_any (PrivateRanges))
| where TargetUserName !endswith "$"
| project SuccessTime = TimeGenerated, Computer, TargetUserName, SourceIP = IpAddress;
let FailedLogons = SecurityEvent
| where TimeGenerated > ago(LookbackWindow)
| where EventID == 4625
| where IpAddress !in ("", "-", "127.0.0.1", "::1")
| where not(IpAddress has_any (PrivateRanges))
| project FailTime = TimeGenerated, Computer, TargetUserName, SourceIP = IpAddress;
SuccessfulLogons
| join kind=inner FailedLogons on SourceIP, TargetUserName
| where FailTime between (datetime_add('hour', -2, SuccessTime) .. SuccessTime)
| summarize FailCount = count(), FirstFail = min(FailTime), SuccessAt = max(SuccessTime)
by Computer, TargetUserName, SourceIP
| where FailCount >= FailThreshold
| extend MinutesBetweenFirstFailAndSuccess = datetime_diff('minute', SuccessAt, FirstFail)
| sort by FailCount desc index=wineventlog sourcetype="WinEventLog:Security" (EventCode=4624 OR EventCode=4625) (LogonType=10 OR LogonType=3) earliest=-7d
| eval SourceIP=coalesce(IpAddress, src_ip, "-")
| where SourceIP != "-" AND NOT match(SourceIP, "^(10\.|172\.(1[6-9]|2[0-9]|3[01])\.|192\.168\.|127\.)")
| eval AccountName=coalesce(TargetUserName, user)
| where NOT match(AccountName, "\$$")
| eval EventType=if(EventCode=4624, "Success", "Failure")
| stats count(eval(EventType="Failure")) as FailCount,
count(eval(EventType="Success")) as SuccessCount,
min(_time) as FirstEvent, max(_time) as LastEvent
by host, AccountName, SourceIP
| where FailCount >= 5 AND SuccessCount >= 1
| eval AttackPattern="BruteForce_Then_Success"
| eval MinutesSpan=round((LastEvent-FirstEvent)/60, 0)
| table host, AccountName, SourceIP, FailCount, SuccessCount, MinutesSpan, FirstEvent, LastEvent, AttackPattern
| sort - FailCount Hunt for accounts accessing remote services from countries or IP ranges with no prior activity in the baseline 30-day period. Adversaries using stolen credentials typically operate from infrastructure in geographies the victim account has never accessed from. New-country access is a high-fidelity indicator of account compromise, particularly effective at identifying APT groups (Volt Typhoon, Ember Bear, GALLIUM) operating from foreign infrastructure against previously legitimate account holders.
// Hunt: New country or IP range first-time access — account accessing from geography not seen in prior 30 days
let RecentWindow = 7d;
let BaselineWindow = 30d;
let PrivateRanges = dynamic(["10.", "172.16.", "172.17.", "172.18.", "172.19.", "172.20.",
"172.21.", "172.22.", "172.23.", "172.24.", "172.25.", "172.26.", "172.27.", "172.28.",
"172.29.", "172.30.", "172.31.", "192.168.", "127."]);
let BaselineCountries = SigninLogs
| where TimeGenerated between (ago(BaselineWindow) .. ago(RecentWindow))
| where ResultType == 0
| where not(IPAddress has_any (PrivateRanges))
| extend Country = tostring(LocationDetails.countryOrRegion)
| summarize BaselineCountrySet = make_set(Country) by UserPrincipalName;
let RecentCountries = SigninLogs
| where TimeGenerated > ago(RecentWindow)
| where ResultType == 0
| where not(IPAddress has_any (PrivateRanges))
| extend Country = tostring(LocationDetails.countryOrRegion)
| summarize RecentCountrySet = make_set(Country), RecentIPs = make_set(IPAddress),
AccessCount = count() by UserPrincipalName;
RecentCountries
| join kind=leftouter BaselineCountries on UserPrincipalName
| extend BaselineCountrySet = coalesce(BaselineCountrySet, dynamic([]))
| extend NewCountries = set_difference(RecentCountrySet, BaselineCountrySet)
| where array_length(NewCountries) > 0
| project UserPrincipalName, NewCountries, RecentCountrySet, BaselineCountrySet, RecentIPs, AccessCount
| sort by array_length(NewCountries) desc index=wineventlog sourcetype="WinEventLog:Security" EventCode=4624 (LogonType=10 OR LogonType=3) earliest=-37d
| eval SourceIP=coalesce(IpAddress, src_ip, "-")
| where SourceIP != "-" AND NOT match(SourceIP, "^(10\.|172\.(1[6-9]|2[0-9]|3[01])\.|192\.168\.|127\.)")
| eval AccountName=coalesce(TargetUserName, user)
| where NOT match(AccountName, "\$$")
| eval Period=if(_time > relative_time(now(), "-7d"), "recent", "historical")
| stats dc(SourceIP) as UniqueIPs, values(SourceIP) as IPList, count as LoginCount
by AccountName, host, Period
| eventstats max(eval(if(Period="historical", UniqueIPs, 0))) as HistoricalUniqueIPs by AccountName
| where Period="recent" AND UniqueIPs > (HistoricalUniqueIPs + 1)
| table AccountName, host, UniqueIPs, HistoricalUniqueIPs, IPList, LoginCount
| sort - UniqueIPs Hunt for external network connections to container and orchestration management API ports. Exposed Docker API on TCP 2375 (unauthenticated) is actively targeted by TeamTNT and Hildegard malware for container escape and cryptomining. Kubernetes API server (6443), kubelet (10250/10255), and etcd (2379) exposure can lead to full cluster compromise. Any external connection permitted to port 2375 is critical — this port should never be reachable from the internet. Connections to 6443 or 10250 from external IPs warrant immediate investigation of cluster RBAC and anonymous access configuration.
// Hunt: External connections to exposed container and orchestration management APIs
let ContainerMgmtPorts = dynamic([2375, 2376, 6443, 8080, 10250, 10255, 2379, 2380]);
DeviceNetworkEvents
| where Timestamp > ago(7d)
| where ActionType in ("InboundConnectionAccepted", "NetworkSignatureInspected")
| where LocalPort in (ContainerMgmtPorts)
| where RemoteIPType == "Public"
| extend ServiceName = case(
LocalPort == 2375, "Docker_API_Unencrypted_CRITICAL",
LocalPort == 2376, "Docker_API_TLS",
LocalPort == 6443, "Kubernetes_API_Server",
LocalPort == 8080, "Kubernetes_API_Insecure",
LocalPort == 10250, "Kubelet_API",
LocalPort == 10255, "Kubelet_ReadOnly_API",
LocalPort == 2379, "etcd_Client",
LocalPort == 2380, "etcd_Peer",
true(), "Unknown_Container_Port")
| summarize ConnectionCount = count(), UniqueSourceIPs = dcount(RemoteIP),
SourceIPs = make_set(RemoteIP), FirstSeen = min(Timestamp), LastSeen = max(Timestamp)
by DeviceName, ServiceName, LocalPort
| sort by ConnectionCount desc (index=network OR index=firewall OR index=ids) dest_port IN (2375, 2376, 6443, 8080, 10250, 10255, 2379, 2380)
| where NOT match(src_ip, "^(10\.|172\.(1[6-9]|2[0-9]|3[01])\.|192\.168\.|127\.)")
| eval ServiceName=case(
dest_port=2375, "Docker_API_Unencrypted_CRITICAL",
dest_port=2376, "Docker_API_TLS",
dest_port=6443, "Kubernetes_API_Server",
dest_port=8080, "Kubernetes_API_Insecure",
dest_port=10250, "Kubelet_API",
dest_port=10255, "Kubelet_ReadOnly",
dest_port=2379, "etcd_Client",
dest_port=2380, "etcd_Peer",
true(), "Other_Container_Port")
| stats count as ConnectionCount, dc(src_ip) as UniqueSourceIPs, values(src_ip) as SourceIPs,
min(_time) as FirstSeen, max(_time) as LastSeen
by dest, ServiceName, dest_port
| sort - ConnectionCount Atomic Red Team Tests
Enables RDP on the local system, adds a firewall rule, and initiates a loopback RDP connection to generate Windows Security Event ID 4624 with LogonType 10 (RemoteInteractive). This simulates the authentication telemetry produced when an adversary logs in via an exposed RDP service. In production testing, replace 127.0.0.1 with an external-routable test machine IP to generate a non-private SourceIP in the event log, which triggers the detection.
Command
reg add "HKLM\SYSTEM\CurrentControlSet\Control\Terminal Server" /v fDenyTSConnections /t REG_DWORD /d 0 /f && netsh advfirewall firewall add rule name="Atomic-T1133-RDP-Test" dir=in action=allow protocol=tcp localport=3389 && mstsc /v:127.0.0.1 Cleanup
reg add "HKLM\SYSTEM\CurrentControlSet\Control\Terminal Server" /v fDenyTSConnections /t REG_DWORD /d 1 /f && netsh advfirewall firewall delete rule name="Atomic-T1133-RDP-Test" Expected Telemetry
Windows Security EventID 4624 with LogonType=10 (RemoteInteractive) and IpAddress=127.0.0.1 in Security event log. Microsoft-Windows-TerminalServices-RemoteConnectionManager/Operational EventID 1149 recording the RDP connection with username and source address. Microsoft-Windows-TerminalServices-LocalSessionManager/Operational EventID 21 (session logon) and 22 (shell start) on successful session establishment.
Expected Detection
KQL ExternalRDP branch: fires on EventID 4624 LogonType==10. Note: 127.0.0.1 is filtered by PrivateRanges — use an external test machine IP to pass the filter and trigger the alert. SPL: ServiceType=RDP_RemoteInteractive row generated. IsHighValue=YES. Alert fires when IpAddress is not in private range.
Enables PowerShell Remoting (WinRM) and executes a remote command via Invoke-Command to the local machine, generating Windows Security Event ID 4624 with LogonType 3 (Network). Adversaries including Volt Typhoon, Scattered Spider, and GALLIUM use WinRM for persistent access. For real detection validation, invoke this command from an external test machine targeting the monitored host to generate an external SourceIP in the event.
Command
Enable-PSRemoting -Force -SkipNetworkProfileCheck; Invoke-Command -ComputerName . -ScriptBlock { whoami; hostname; Get-Date; ipconfig } -Authentication Negotiate Cleanup
Disable-PSRemoting -Force; Set-Item WSMan:\localhost\Service\Auth\Basic -Value false Expected Telemetry
Windows Security EventID 4624 with LogonType=3 (Network) and AuthenticationPackageName=NTLM or Kerberos. Sysmon EventID 3 (Network Connection) from wsmprovhost.exe (WinRM provider host). Sysmon EventID 1 (Process Create) for wsmprovhost.exe. PowerShell ScriptBlock Log EventID 4104 for executed commands. Windows Remote Management log in Microsoft-Windows-WinRM/Operational.
Expected Detection
KQL ExternalNetworkLogon branch: fires on EventID 4624 LogonType==3 with external IpAddress and account not ending in $. SPL: ServiceType=Network_WinRM_SMB with external SourceIP. AuthPackage field reveals Negotiate/NTLM vs Kerberos. For real validation run from an external host to generate non-private SourceIP.
Tests for an exposed Docker API by querying the daemon REST endpoint on TCP 2375 (unauthenticated). TeamTNT and Hildegard malware use this technique to enumerate containers and deploy malicious workloads. This test requires the Docker daemon to be running with TCP exposure (docker daemon -H tcp://0.0.0.0:2375 or equivalent in /etc/docker/daemon.json). Tests both version endpoint and container listing which are the first API calls adversaries make.
Command
curl -s http://127.0.0.1:2375/version 2>/dev/null && curl -s http://127.0.0.1:2375/containers/json 2>/dev/null | python3 -m json.tool 2>/dev/null || echo 'Docker API not exposed on 2375 - test cannot proceed (this is the expected secure configuration)' Expected Telemetry
Docker daemon log (/var/log/docker.log or journalctl -u docker --since '5 minutes ago'): GET /version and GET /containers/json HTTP requests logged with source IP and timestamp. Network connection to TCP 2375 visible in ss -tnp or netstat output. Auditd syscall events for the accept() and read() syscalls if network auditing is enabled. Sysmon for Linux EventID 3 if deployed.
Expected Detection
KQL container hunting query: DeviceNetworkEvents fires on LocalPort==2375 with RemoteIPType==Public. SPL container hunting query: dest_port=2375 from non-RFC1918 source generates ServiceName=Docker_API_Unencrypted_CRITICAL. Any external access to port 2375 should auto-escalate to critical severity regardless of other context.
Simulates the brute-force-then-success authentication pattern against SSH that is detected by the hunting query. Generates multiple failed SSH authentication attempts followed by a successful connection, producing the Security Event pattern (multiple 4625/auth failures + 4624/success) that indicates T1133 credential stuffing. In production testing, execute from a separate external machine targeting the monitored SSH host to generate external SourceIP in auth logs.
Command
for i in $(seq 1 6); do ssh -o StrictHostKeyChecking=no -o ConnectTimeout=3 -o BatchMode=yes -o PreferredAuthentications=publickey [email protected] 2>/dev/null; done; echo 'Failed auth attempts generated. Check /var/log/auth.log or journalctl -u ssh for sshd authentication failure events.' Expected Telemetry
Linux auth log (/var/log/auth.log on Debian/Ubuntu or /var/log/secure on RHEL/CentOS): multiple 'Invalid user nonexistentuser_N from 127.0.0.1' and 'Failed none for invalid user' entries. Sysmon for Linux EventID 3 (Network Connection) from ssh client process to port 22. Auditd USER_AUTH records for each failed attempt with res=failed. fail2ban log entries if deployed.
Expected Detection
SPL brute-force hunting query: FailCount >= 5 with SuccessCount=0 (or add a successful SSH connection to complete the pattern). KQL: FailedLogons join SuccessfulLogons pattern triggers when FailCount >= 5 in 2h window from same SourceIP to same account. For full brute-force-then-success test, follow with: ssh -o StrictHostKeyChecking=no $(whoami)@127.0.0.1 'whoami' to generate the success event.