Lateral Movement
T1021 — Remote Services
Adversaries may use valid accounts to log into remote services — RDP, SSH, SMB/admin shares, WinRM, VNC — for lateral movement across an environment using legitimate remote access functionality.
Review any remote-service logon that is unusual for the source host, destination host, account, or time — particularly an account authenticating to a system it does not normally access, or a workstation connecting directly to another workstation rather than through expected jump hosts.
Platforms
Windows, Linux, macOS
Priority / status
medium / complete
Evidence to collect
- Source and destination host
- Account used
- Remote service/protocol (RDP/SSH/SMB/WinRM/VNC)
- Logon timestamp and type
- Activity performed on the destination after logon
D3-RTSD · detect
Remote Terminal Session Detection
Review remote logon events (RDP/SSH/WinRM/SMB) for unusual source-destination-account combinations.
Tooling: Defender for Endpoint, Sentinel
D3-CAA · detect
Connection Attempt Analysis
Detect unusual internal RDP/SSH/SMB connection patterns indicating lateral movement.
Tooling: Sentinel, Defender for Endpoint
D3-ST · evict
Session Termination
Terminate the remote session and revoke the account's credentials/sessions if malicious.
Tooling: Microsoft Graph, Active Directory
D3-NTF · isolate
Network Traffic Filtering
Restrict remote-service access via firewall/segmentation to expected jump hosts only.
Tooling: Firewall, Group Policy
| ATT&CK Technique | D3FEND Technique | Practical SOC Action | Tooling |
|---|---|---|---|
| T1021 Remote Services | Remote Terminal Session Detection | Review remote logon events (RDP/SSH/WinRM/SMB) for unusual source-destination-account combinations. | Defender for Endpoint, Sentinel |
| T1021 Remote Services | Connection Attempt Analysis | Detect unusual internal RDP/SSH/SMB connection patterns indicating lateral movement. | Sentinel, Defender for Endpoint |
| T1021 Remote Services | Session Termination | Terminate the remote session and revoke the account's credentials/sessions if malicious. | Microsoft Graph, Active Directory |
| T1021 Remote Services | Network Traffic Filtering | Restrict remote-service access via firewall/segmentation to expected jump hosts only. | Firewall, Group Policy |
T1021 Remote Services → D3FEND → SOC action
Investigation steps — Microsoft
- Check device inventory for both source and destination: age, OS, role, owner
- If the source device was recently onboarded, this may be a baseline gap (not an attack)
Investigation steps — generic
- Identify the remote service used, source host, destination host, and account.
- Determine whether this source-destination-account combination is normal for this environment.
- Check for follow-on activity on the destination host after the connection.
- Real detection reference: "Account Remote Logon Fan-Out Across Hosts" -- Flags accounts performing successful remote/network logons to many hosts.
Response actions
| Action | Risk | Automation safe | Approval required |
|---|---|---|---|
| Isolate both source and destination devices | Medium | No | Yes |
| Reset the abused account's password (and all other accounts | Medium | No | Yes |
| Purge Kerberos tickets on both devices: klist purge via Li | Medium | No | Yes |
| Search for further lateral movement hops from the destinatio | Low | No | Yes |
| Enable Credential Guard on all affected devices post-rebuild | Medium | No | Yes |
| Disable NTLMv1 organisation-wide via Group Policy | Medium | No | Yes |
KQL
GEN-LM-001 — Account Remote Logon Fan-Out Across Hosts
let lookback = 1d;
let hostThreshold = 6;
DeviceLogonEvents
| where TimeGenerated >= ago(lookback)
| where ActionType == "LogonSuccess"
| where LogonType in~ ("RemoteInteractive", "Network")
| where isnotempty(AccountUpn) and isnotempty(DeviceName)
| summarize
StartTime = min(TimeGenerated),
EndTime = max(TimeGenerated),
DistinctHosts = dcount(DeviceName),
Hosts = make_set(DeviceName, 100),
SourceIPs = make_set(RemoteIP, 50)
by AccountUpn
| where DistinctHosts >= hostThreshold
| extend AccountName = tostring(split(AccountUpn, "@")[0]),
UPNSuffix = tostring(split(AccountUpn, "@")[1]),
HostCustomEntity = tostring(Hosts[0])
| project StartTime, EndTime, AccountUpn, AccountName, UPNSuffix,
DistinctHosts, Hosts, SourceIPs, HostCustomEntity
| extend timestamp = StartTime, AccountCustomEntity = AccountUpn
| order by DistinctHosts desc GEN-LM-002 — PsExec-Style Remote Service Creation
let SuspiciousServiceNames = dynamic([
"PSEXESVC",
"PAExec",
"RemComSvc",
"WinExeSvc"
]);
DeviceProcessEvents
| where TimeGenerated >= ago(5m)
| where FileName in~ ("services.exe", "sc.exe")
| where ProcessCommandLine has_any (SuspiciousServiceNames) or InitiatingProcessCommandLine has_any (SuspiciousServiceNames)
| project
TimeGenerated,
DeviceName,
DeviceId,
AccountName,
FileName,
ProcessCommandLine,
InitiatingProcessFileName,
InitiatingProcessCommandLine,
InitiatingProcessAccountName,
InitiatingProcessRemoteSessionDeviceName
| extend timestamp = TimeGenerated,
HostCustomEntity = DeviceName,
AccountCustomEntity = AccountName
| order by TimeGenerated desc GEN-LM-003 — Admin Share Access Fan-Out Across Hosts
let lookback = 1d;
let hostThreshold = 5;
DeviceNetworkEvents
| where TimeGenerated >= ago(lookback)
| where ActionType == "ConnectionSuccess"
| where RemotePort == 445
| where isnotempty(InitiatingProcessAccountName) and isnotempty(RemoteIP)
| summarize
StartTime = min(TimeGenerated),
EndTime = max(TimeGenerated),
DistinctHosts = dcount(RemoteIP),
Hosts = make_set(RemoteIP, 100),
SourceDevices = make_set(DeviceName, 50)
by InitiatingProcessAccountName
| where DistinctHosts >= hostThreshold
| extend HostCustomEntity = tostring(SourceDevices[0])
| project StartTime, EndTime, InitiatingProcessAccountName, DistinctHosts, Hosts, SourceDevices, HostCustomEntity
| extend timestamp = StartTime, AccountCustomEntity = InitiatingProcessAccountName
| order by DistinctHosts desc Escalation criteria
- Privileged account used for the remote logon.
- Destination host holds sensitive data or is a domain controller.
- Logon pattern matches known lateral-movement tooling (PsExec-style admin share usage).
- Activity followed recent credential access or discovery on the source host.
False positive considerations
- New workstation — baseline gap — Verify device age in asset inventory; if < 30 days old, this is expected — add to allowlist
- Recently changed hostname — Same device, different name — add old/new pair to baseline
- IT admin NTLM to new server — Verify with IT admin; add approved management server pairs to exclusions watchlist
# T1021 - Remote Services
## SOC Recommendation
Review any remote-service logon that is unusual for the source host, destination host, account, or time — particularly an account authenticating to a system it does not normally access, or a workstation connecting directly to another workstation rather than through expected jump hosts.
## D3FEND Mappings
| D3FEND Technique | Relationship | Practical SOC Action |
|---|---|---|
| Remote Terminal Session Detection | Detect | Review remote logon events (RDP/SSH/WinRM/SMB) for unusual source-destination-account combinations. |
| Connection Attempt Analysis | Detect | Detect unusual internal RDP/SSH/SMB connection patterns indicating lateral movement. |
| Session Termination | Evict | Terminate the remote session and revoke the account's credentials/sessions if malicious. |
| Network Traffic Filtering | Isolate | Restrict remote-service access via firewall/segmentation to expected jump hosts only. |
## Investigation Steps
1. Check device inventory for both source and destination: age, OS, role, owner
2. If the source device was recently onboarded, this may be a baseline gap (not an attack)
## Evidence to Collect
- Source and destination host
- Account used
- Remote service/protocol (RDP/SSH/SMB/WinRM/VNC)
- Logon timestamp and type
- Activity performed on the destination after logon
## Response Actions
| Action | Risk | Automation Safe | Approval Required |
|---|---|---|---|
| Isolate both source and destination devices | Medium | No | Yes |
| Reset the abused account's password (and all other accounts | Medium | No | Yes |
| Purge Kerberos tickets on both devices: klist purge via Li | Medium | No | Yes |
| Search for further lateral movement hops from the destinatio | Low | No | Yes |
| Enable Credential Guard on all affected devices post-rebuild | Medium | No | Yes |
| Disable NTLMv1 organisation-wide via Group Policy | Medium | No | Yes |
## KQL
```kql
let lookback = 1d;
let hostThreshold = 6;
DeviceLogonEvents
| where TimeGenerated >= ago(lookback)
| where ActionType == "LogonSuccess"
| where LogonType in~ ("RemoteInteractive", "Network")
| where isnotempty(AccountUpn) and isnotempty(DeviceName)
| summarize
StartTime = min(TimeGenerated),
EndTime = max(TimeGenerated),
DistinctHosts = dcount(DeviceName),
Hosts = make_set(DeviceName, 100),
SourceIPs = make_set(RemoteIP, 50)
by AccountUpn
| where DistinctHosts >= hostThreshold
| extend AccountName = tostring(split(AccountUpn, "@")[0]),
UPNSuffix = tostring(split(AccountUpn, "@")[1]),
HostCustomEntity = tostring(Hosts[0])
| project StartTime, EndTime, AccountUpn, AccountName, UPNSuffix,
DistinctHosts, Hosts, SourceIPs, HostCustomEntity
| extend timestamp = StartTime, AccountCustomEntity = AccountUpn
| order by DistinctHosts desc
```
```kql
let SuspiciousServiceNames = dynamic([
"PSEXESVC",
"PAExec",
"RemComSvc",
"WinExeSvc"
]);
DeviceProcessEvents
| where TimeGenerated >= ago(5m)
| where FileName in~ ("services.exe", "sc.exe")
| where ProcessCommandLine has_any (SuspiciousServiceNames) or InitiatingProcessCommandLine has_any (SuspiciousServiceNames)
| project
TimeGenerated,
DeviceName,
DeviceId,
AccountName,
FileName,
ProcessCommandLine,
InitiatingProcessFileName,
InitiatingProcessCommandLine,
InitiatingProcessAccountName,
InitiatingProcessRemoteSessionDeviceName
| extend timestamp = TimeGenerated,
HostCustomEntity = DeviceName,
AccountCustomEntity = AccountName
| order by TimeGenerated desc
```
```kql
let lookback = 1d;
let hostThreshold = 5;
DeviceNetworkEvents
| where TimeGenerated >= ago(lookback)
| where ActionType == "ConnectionSuccess"
| where RemotePort == 445
| where isnotempty(InitiatingProcessAccountName) and isnotempty(RemoteIP)
| summarize
StartTime = min(TimeGenerated),
EndTime = max(TimeGenerated),
DistinctHosts = dcount(RemoteIP),
Hosts = make_set(RemoteIP, 100),
SourceDevices = make_set(DeviceName, 50)
by InitiatingProcessAccountName
| where DistinctHosts >= hostThreshold
| extend HostCustomEntity = tostring(SourceDevices[0])
| project StartTime, EndTime, InitiatingProcessAccountName, DistinctHosts, Hosts, SourceDevices, HostCustomEntity
| extend timestamp = StartTime, AccountCustomEntity = InitiatingProcessAccountName
| order by DistinctHosts desc
```
## Escalation Criteria
- Privileged account used for the remote logon.
- Destination host holds sensitive data or is a domain controller.
- Logon pattern matches known lateral-movement tooling (PsExec-style admin share usage).
- Activity followed recent credential access or discovery on the source host.
## False Positive Considerations
- New workstation — baseline gap — Verify device age in asset inventory; if < 30 days old, this is expected — add to allowlist
- Recently changed hostname — Same device, different name — add old/new pair to baseline
- IT admin NTLM to new server — Verify with IT admin; add approved management server pairs to exclusions watchlist
Generated by SOC Response Atlas by Basyrix.
Want to push this directly to Confluence? Upgrade to Basyrix Pro.
- /api/techniques/T1021.json
- /api/recommendations/T1021.json
- /api/d3fend/T1021.json
- /api/mappings/T1021.json
- /api/confluence/T1021.md
Example curl:
curl https://atlas.basyrix.com/api/recommendations/T1021.json Response:
{
"technique_id": "T1021",
"name": "Remote Services",
"priority": "medium",
"status": "complete",
"version": "0.1.0",
"last_reviewed": "2026-07-23",
"generated_by": "SOC Response Atlas by Basyrix",
"tactics": [
"Lateral Movement"
],
"platforms": [
"Windows",
"Linux",
"macOS"
],
"summary": "Adversaries may use valid accounts to log into remote services — RDP, SSH, SMB/admin shares, WinRM, VNC — for lateral movement across an environment using legitimate remote access functionality.\n",
"soc_recommendation": "Review any remote-service logon that is unusual for the source host, destination host, account, or time — particularly an account authenticating to a system it does not normally access, or a workstation connecting directly to another workstation rather than through expected jump hosts.\n",
"d3fend_mappings": [
{
"id": "D3-RTSD",
"name": "Remote Terminal Session Detection",
"relationship": "detect",
"practical_action": "Review remote logon events (RDP/SSH/WinRM/SMB) for unusual source-destination-account combinations.\n",
"tooling": [
"Defender for Endpoint",
"Sentinel"
]
},
{
"id": "D3-CAA",
"name": "Connection Attempt Analysis",
"relationship": "detect",
"practical_action": "Detect unusual internal RDP/SSH/SMB connection patterns indicating lateral movement.",
"tooling": [
"Sentinel",
"Defender for Endpoint"
]
},
{
"id": "D3-ST",
"name": "Session Termination",
"relationship": "evict",
"practical_action": "Terminate the remote session and revoke the account's credentials/sessions if malicious.",
"tooling": [
"Microsoft Graph",
"Active Directory"
]
},
{
"id": "D3-NTF",
"name": "Network Traffic Filtering",
"relationship": "isolate",
"practical_action": "Restrict remote-service access via firewall/segmentation to expected jump hosts only.",
"tooling": [
"Firewall",
"Group Policy"
]
}
],
"investigation_steps": {
"microsoft": [
"Check device inventory for both source and destination: age, OS, role, owner",
"If the source device was recently onboarded, this may be a baseline gap (not an attack)"
],
"generic": [
"Identify the remote service used, source host, destination host, and account.",
"Determine whether this source-destination-account combination is normal for this environment.",
"Check for follow-on activity on the destination host after the connection.",
"Real detection reference: \"Account Remote Logon Fan-Out Across Hosts\" -- Flags accounts performing successful remote/network logons to many hosts."
]
},
"evidence_to_collect": [
"Source and destination host",
"Account used",
"Remote service/protocol (RDP/SSH/SMB/WinRM/VNC)",
"Logon timestamp and type",
"Activity performed on the destination after logon"
],
"response_actions": [
{
"name": "Isolate both source and destination devices",
"category": "Containment",
"risk": "Medium",
"automation_safe": false,
"approval_required": true,
"tool": "See investigation guide"
},
{
"name": "Reset the abused account's password (and all other accounts",
"category": "Containment",
"risk": "Medium",
"automation_safe": false,
"approval_required": true,
"tool": "See investigation guide",
"notes": "Reset the abused account's password (and all other accounts dumped from the source device)"
},
{
"name": "Purge Kerberos tickets on both devices: klist purge via Li",
"category": "Containment",
"risk": "Medium",
"automation_safe": false,
"approval_required": true,
"tool": "Defender for Endpoint",
"notes": "Purge Kerberos tickets on both devices: klist purge via Live Response"
},
{
"name": "Search for further lateral movement hops from the destinatio",
"category": "Response",
"risk": "Low",
"automation_safe": false,
"approval_required": true,
"tool": "See investigation guide",
"notes": "Search for further lateral movement hops from the destination device"
},
{
"name": "Enable Credential Guard on all affected devices post-rebuild",
"category": "Response",
"risk": "Medium",
"automation_safe": false,
"approval_required": true,
"tool": "See investigation guide"
},
{
"name": "Disable NTLMv1 organisation-wide via Group Policy",
"category": "Response",
"risk": "Medium",
"automation_safe": false,
"approval_required": true,
"tool": "See investigation guide"
}
],
"queries": {
"kql": [
{
"name": "GEN-LM-001 — Account Remote Logon Fan-Out Across Hosts",
"description": "Flags accounts performing successful remote/network logons to many hosts. (Source: Bell Integration baseline detection library.)",
"query": "let lookback = 1d;\nlet hostThreshold = 6;\nDeviceLogonEvents\n| where TimeGenerated >= ago(lookback)\n| where ActionType == \"LogonSuccess\"\n| where LogonType in~ (\"RemoteInteractive\", \"Network\")\n| where isnotempty(AccountUpn) and isnotempty(DeviceName)\n| summarize\n StartTime = min(TimeGenerated),\n EndTime = max(TimeGenerated),\n DistinctHosts = dcount(DeviceName),\n Hosts = make_set(DeviceName, 100),\n SourceIPs = make_set(RemoteIP, 50)\n by AccountUpn\n| where DistinctHosts >= hostThreshold\n| extend AccountName = tostring(split(AccountUpn, \"@\")[0]),\n UPNSuffix = tostring(split(AccountUpn, \"@\")[1]),\n HostCustomEntity = tostring(Hosts[0])\n| project StartTime, EndTime, AccountUpn, AccountName, UPNSuffix,\n DistinctHosts, Hosts, SourceIPs, HostCustomEntity\n| extend timestamp = StartTime, AccountCustomEntity = AccountUpn\n| order by DistinctHosts desc"
},
{
"name": "GEN-LM-002 — PsExec-Style Remote Service Creation",
"description": "Flags remote service creation consistent with PsExec/PAExec-style lateral movement tooling. (Source: Bell Integration baseline detection library, mapped via sub-technique T1021.002.)",
"query": "let SuspiciousServiceNames = dynamic([\n \"PSEXESVC\",\n \"PAExec\",\n \"RemComSvc\",\n \"WinExeSvc\"\n]);\nDeviceProcessEvents\n| where TimeGenerated >= ago(5m)\n| where FileName in~ (\"services.exe\", \"sc.exe\")\n| where ProcessCommandLine has_any (SuspiciousServiceNames) or InitiatingProcessCommandLine has_any (SuspiciousServiceNames)\n| project\n TimeGenerated,\n DeviceName,\n DeviceId,\n AccountName,\n FileName,\n ProcessCommandLine,\n InitiatingProcessFileName,\n InitiatingProcessCommandLine,\n InitiatingProcessAccountName,\n InitiatingProcessRemoteSessionDeviceName\n| extend timestamp = TimeGenerated,\n HostCustomEntity = DeviceName,\n AccountCustomEntity = AccountName\n| order by TimeGenerated desc"
},
{
"name": "GEN-LM-003 — Admin Share Access Fan-Out Across Hosts",
"description": "Flags a single account accessing administrative shares (ADMIN$, C$, IPC$) on many distinct hosts in a short window. (Source: Bell Integration baseline detection library, mapped via sub-technique T1021.002.)",
"query": "let lookback = 1d;\nlet hostThreshold = 5;\nDeviceNetworkEvents\n| where TimeGenerated >= ago(lookback)\n| where ActionType == \"ConnectionSuccess\"\n| where RemotePort == 445\n| where isnotempty(InitiatingProcessAccountName) and isnotempty(RemoteIP)\n| summarize\n StartTime = min(TimeGenerated),\n EndTime = max(TimeGenerated),\n DistinctHosts = dcount(RemoteIP),\n Hosts = make_set(RemoteIP, 100),\n SourceDevices = make_set(DeviceName, 50)\n by InitiatingProcessAccountName\n| where DistinctHosts >= hostThreshold\n| extend HostCustomEntity = tostring(SourceDevices[0])\n| project StartTime, EndTime, InitiatingProcessAccountName, DistinctHosts, Hosts, SourceDevices, HostCustomEntity\n| extend timestamp = StartTime, AccountCustomEntity = InitiatingProcessAccountName\n| order by DistinctHosts desc"
}
],
"spl": [
{
"name": "Lateral movement via remote services",
"description": "Review remote logon events in Splunk.",
"query": "index=endpoint logon_type IN (\"rdp\",\"ssh\") earliest=-24h\n| stats values(dest) by user, src\n"
}
],
"esql": [
{
"name": "a1b2c3d4-e5f6-4789-a0b1-c2d3e4f5a6b7 — AWS Lateral Movement from Kubernetes SA via AssumeRoleWithWebIdentity",
"description": "(ESQL) Detects when credentials issued through `AssumeRoleWithWebIdentity` for a Kubernetes service account identity are later used for several distinct AWS control-plane actions on the same session access key... (Source: Elastic's official detection-rules repository (Elastic License v2).)",
"query": "FROM logs-aws.cloudtrail-*\n| WHERE (event.action == \"AssumeRoleWithWebIdentity\" AND user.name like \"system:serviceaccount:*\")\n // S3 PutObject/GetObject is too common in legit pod SA behavior \n OR (event.action IN (\"ListBuckets\", \"DescribeInstances\", \"GetCallerIdentity\",\n \"ListUsers\", \"ListRoles\", \"ListAttachedRolePolicies\", \"GetRolePolicy\",\n \"GetSecretValue\", \"ListSecrets\",\n \"GetParameters\", \"DescribeParameters\", \"ListKeys\", \"Decrypt\",\n \"ListFunctions\", \"GetAuthorizationToken\",\n \"SendCommand\", \"StartSession\",\n \"CreateUser\", \"CreateAccessKey\", \"AttachRolePolicy\", \"CreateRole\",\n \"PutRolePolicy\", \"UpdateAssumeRolePolicy\",\n \"UpdateFunctionCode\", \"UpdateFunctionConfiguration\", \"ModifyInstanceAttribute\",\n \"StopLogging\", \"DeleteTrail\")\n AND aws.cloudtrail.user_identity.type == \"AssumedRole\")\n| GROK aws.cloudtrail.response_elements \"accessKeyId=%{NOTSPACE:issued_key_id},\"\n| EVAL access_key = COALESCE(issued_key_id, aws.cloudtrail.user_identity.access_key_id)\n| EVAL is_assume = CASE(event.action == \"AssumeRoleWithWebIdentity\", 1, 0)\n| EVAL is_post_exploit = CASE(event.action != \"AssumeRoleWithWebIdentity\", 1, 0)\n| EVAL phase = CASE(\n event.action == \"AssumeRoleWithWebIdentity\", \"initial_access\",\n event.action IN (\"ListBuckets\", \"DescribeInstances\", \"ListUsers\", \"ListRoles\",\n \"GetCallerIdentity\", \"ListAttachedRolePolicies\", \"GetRolePolicy\",\n \"ListFunctions\"), \"recon\",\n event.action IN (\"GetSecretValue\", \"ListSecrets\", \"GetParameters\",\n \"GetAuthorizationToken\", \"Decrypt\"), \"credential_access\",\n event.action IN (\"SendCommand\", \"StartSession\"), \"lateral_movement\",\n event.action IN (\"CreateUser\", \"CreateAccessKey\", \"AttachRolePolicy\",\n \"CreateRole\", \"PutRolePolicy\", \"UpdateAssumeRolePolicy\",\n \"UpdateFunctionCode\", \"UpdateFunctionConfiguration\",\n \"ModifyInstanceAttribute\"), \"persistence\",\n event.action IN (\"StopLogging\", \"DeleteTrail\"), \"defense_evasion\"\n )\n| STATS \n Esql.assume_count = SUM(is_assume),\n Esql.post_exploit_count = COUNT_DISTINCT(event.action),\n Esql.attack_phases = VALUES(phase),\n Esql.event_action_values = VALUES(event.action),\n Esql.source_ip_values = VALUES(source.ip),\n Esql.source_as_organization_name_values = VALUES(source.as.organization.name),\n Esql.user_name_values = VALUES(user.name),\n Esql.user_agent_original_values = VALUES(user_agent.original),\n Esql.cloud_account_id_values = VALUES(cloud.account.id),\n Esql.data_stream_namespace_values = VALUES(data_stream.namespace),\n Esql.first_seen = MIN(@timestamp),\n Esql.last_seen = MAX(@timestamp),\n Esql.total_calls = COUNT(*)\n BY access_key\n| WHERE access_key is not null and Esql.assume_count >= 1 AND Esql.post_exploit_count >= 3\n| EVAL aws.cloudtrail.user_identity.access_key_id = MV_FIRST(access_key)\n| KEEP aws.cloudtrail.user_identity.access_key_id, Esql.*"
},
{
"name": "1397e1b9-0c90-4d24-8d7b-80598eb9bc9a — Potential Ransomware Behavior - Note Files by System",
"description": "(ESQL) This rule identifies the creation of multiple files with same name and over SMB by the same user. This behavior may indicate the successful remote execution of a ransomware dropping file notes to different folders. (Source: Elastic's official detection-rules repository (Elastic License v2).)",
"query": "from logs-endpoint.events.file-* metadata _id, _version, _index\n\n// filter for file creation event done remotely over SMB with common user readable file types used to place ransomware notes\n| where event.category == \"file\" and host.os.type == \"windows\" and event.action == \"creation\" and process.pid == 4 and user.id != \"S-1-5-18\" and \n file.extension in (\"txt\", \"htm\", \"html\", \"hta\", \"pdf\", \"jpg\", \"bmp\", \"png\") and\n to_lower(file.path) like \"\"\"c:\\\\*\"\"\" and not to_lower(file.path) like \"\"\"c:\\\\temp\\\\*\"\"\"\n\n// truncate the timestamp to a 60-second window\n| eval Esql.time_window_date_trunc = date_trunc(60 seconds, @timestamp)\n\n| keep user.id, user.name, file.path, file.name, process.entity_id, Esql.time_window_date_trunc, host.name, host.ip, host.id\n\n// filter for same file name dropped in at least 3 unique paths by the System virtual process\n| stats Esql.file_path_count_distinct = COUNT_DISTINCT(file.path), Esql.file_path_values = VALUES(file.path), Esql.host_ip_values = values(host.ip) by host.id, host.name, user.name, user.id, process.entity_id , file.name, Esql.time_window_date_trunc\n| where Esql.file_path_count_distinct >= 3"
}
]
},
"automation": {
"safe": [
"Add recommendation as Sentinel incident comment.",
"Terminate a confirmed-unauthorized remote session.",
"Create ServiceNow SecOps task."
],
"approval_required": [
"Disable the account used.",
"Apply a new firewall/segmentation restriction."
]
},
"escalation_criteria": [
"Privileged account used for the remote logon.",
"Destination host holds sensitive data or is a domain controller.",
"Logon pattern matches known lateral-movement tooling (PsExec-style admin share usage).",
"Activity followed recent credential access or discovery on the source host."
],
"false_positive_considerations": [
"New workstation — baseline gap — Verify device age in asset inventory; if < 30 days old, this is expected — add to allowlist",
"Recently changed hostname — Same device, different name — add old/new pair to baseline",
"IT admin NTLM to new server — Verify with IT admin; add approved management server pairs to exclusions watchlist"
],
"confluence": {
"title": "T1021 - Remote Services Response Guidance",
"labels": [
"mitre",
"attack",
"d3fend",
"secops",
"basyrix"
],
"sections": [
"summary",
"d3fend_mappings",
"investigation_steps",
"response_actions",
"queries",
"automation",
"escalation_criteria",
"false_positive_considerations"
]
}
}