Detection rules › Kusto
Detect URLs containing known malicious keywords or commands (ASIM Web Session)
'The utilization of system commands or functions in the request URL may suggest that an attacker is trying to gain unauthorized access to the environment by exploiting a vulnerable service.'
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Initial Access | |
| Command & Control |
Rule body
id: 32c08696-2e37-4730-86f8-97d9c8b184c9
name: Detect URLs containing known malicious keywords or commands (ASIM Web Session)
description: |
'The utilization of system commands or functions in the request URL may suggest that an attacker is trying to gain unauthorized access to the environment by exploiting a vulnerable service.'
severity: High
status: Available
tags:
- Schema: WebSession
SchemaVersion: 0.2.6
requiredDataConnectors: []
queryFrequency: 5m
queryPeriod: 5m
triggerOperator: gt
triggerThreshold: 0
tactics:
- InitialAccess
- CommandAndControl
relevantTechniques:
- T1190
- T1133
- T1071
query: |
let lookback = 5m;
let RiskyCommandsInUrl = materialize(externaldata(Commands: string)
[@"https://raw.githubusercontent.com/Azure/Azure-Sentinel/master/Sample%20Data/Feeds/CommandsInURL.csv"]
with(format="csv", ignoreFirstRecord=True));
let CustomRiskyCommandsInUrl = (_ASIM_GetWatchlistRaw("Web_RiskyCommandsInUrl") // Create new Watchlist and add your custom indicators(Optional)
| extend
Commands = tostring(WatchlistItem["Commands"])
| project Commands
| where isnotempty(Commands));
let CombinedRiskyCommandsInUrl = union RiskyCommandsInUrl, CustomRiskyCommandsInUrl;
let knownRiskyCommandsInUrl=toscalar(CombinedRiskyCommandsInUrl
| where isnotempty(Commands)
| summarize make_set(Commands, 1000));
// You can add more keywords to the query as necessary, depending on the specific indicators you want to detect.
_Im_WebSession (starttime=ago(lookback), eventresult='Success', url_has_any=knownRiskyCommandsInUrl)
| where isnotempty(Url)
| project Url, SrcIpAddr, SrcUsername, SrcHostname, DstIpAddr, TimeGenerated
| extend Decoded_url = url_decode(Url)
| where Decoded_url has_any (knownRiskyCommandsInUrl)
| summarize
EventCount=count(),
EventStartTime=min(TimeGenerated),
EventEndTime=max(TimeGenerated)
by SrcIpAddr, SrcUsername, SrcHostname, Url, Decoded_url, DstIpAddr
| extend
Name = iif(SrcUsername contains "@", tostring(split(SrcUsername, '@', 0)[0]), SrcUsername),
UPNSuffix = iif(SrcUsername contains "@", tostring(split(SrcUsername, '@', 1)[0]), "")
entityMappings:
- entityType: IP
fieldMappings:
- identifier: Address
columnName: SrcIpAddr
- entityType: IP
fieldMappings:
- identifier: Address
columnName: DstIpAddr
- entityType: URL
fieldMappings:
- identifier: Url
columnName: Url
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: SrcUsername
- identifier: Name
columnName: Name
- identifier: UPNSuffix
columnName: UPNSuffix
eventGroupingSettings:
aggregationKind: AlertPerResult
customDetails:
EventCount: EventCount
Decoded_url: Decoded_url
EventStartTime: EventStartTime
EventEndTime: EventEndTime
alertDetailsOverride:
alertDisplayNameFormat: "User '{{SrcUsername}}' with IP '{{SrcIpAddr}}' has been identified as making request for URL '{{Url}}' that includes a recognizable malicious command"
version: 1.0.1
kind: Scheduled
Stages and Predicates
Parameters
let lookback = 5m;
let CombinedRiskyCommandsInUrl = union RiskyCommandsInUrl, CustomRiskyCommandsInUrl;
Let binding: RiskyCommandsInUrl
let RiskyCommandsInUrl = materialize(externaldata(Commands: string)
[@"https://raw.githubusercontent.com/Azure/Azure-Sentinel/master/Sample%20Data/Feeds/CommandsInURL.csv"]
with(format="csv", ignoreFirstRecord=True));
Let binding: CustomRiskyCommandsInUrl
let CustomRiskyCommandsInUrl = (_ASIM_GetWatchlistRaw("Web_RiskyCommandsInUrl")
| extend
Commands = tostring(WatchlistItem["Commands"])
| project Commands
| where isnotempty(Commands));
Let binding: knownRiskyCommandsInUrl
let knownRiskyCommandsInUrl = toscalar(CombinedRiskyCommandsInUrl
| where isnotempty(Commands)
| summarize make_set(Commands, 1000));
Stage 1: source
_Im_WebSession (starttime=ago(lookback), eventresult='Success', url_has_any=knownRiskyCommandsInUrl)
Stage 2: where
| where isnotempty(Url)
Stage 3: project
| project Url, SrcIpAddr, SrcUsername, SrcHostname, DstIpAddr, TimeGenerated
Stage 4: extend
| extend Decoded_url = url_decode(Url)
Stage 5: where
| where Decoded_url has_any (knownRiskyCommandsInUrl)
Stage 6: summarize
| summarize
EventCount=count(),
EventStartTime=min(TimeGenerated),
EventEndTime=max(TimeGenerated)
by SrcIpAddr, SrcUsername, SrcHostname, Url, Decoded_url, DstIpAddr
Stage 7: extend
| extend
Name = iif(SrcUsername contains "@", tostring(split(SrcUsername, '@', 0)[0]), SrcUsername),
UPNSuffix = iif(SrcUsername contains "@", tostring(split(SrcUsername, '@', 1)[0]), "")
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Decoded_url | match |
| field:"Decoded_url" kind:match value:"knownRiskyCommandsInUrl" |
Url | is_not_null | field:"Url" kind:is_not_null |
Output fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
Decoded_url | summarize |
DstIpAddr | summarize |
EventCount | summarize |
EventEndTime | summarize |
EventStartTime | summarize |
SrcHostname | summarize |
SrcIpAddr | summarize |
SrcUsername | summarize |
Url | summarize |
Name | extend |
UPNSuffix | extend |