Detection rules › Kusto
Detect known risky user agents (ASIM Web Session)
'This rule is designed to flag web requests that contain a user agent header that is recognized as malicious. It relies on a predefined list of known user agents, which is referenced from a specific CSV file'
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Initial Access | T1133 External Remote Services, T1190 Exploit Public-Facing Application |
| Command & Control | T1071 Application Layer Protocol |
Rule body kusto
id: 6a4dbcf8-f5e2-4b33-b34f-2db6487613f0
name: Detect known risky user agents (ASIM Web Session)
description: |
'This rule is designed to flag web requests that contain a user agent header that is recognized as malicious. It relies on a predefined list of known user agents, which is referenced from a specific CSV file'
severity: Medium
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 knownUserAgentsIndicators = materialize(externaldata(UserAgent: string, UserAgentCategory: string)
[@"https://raw.githubusercontent.com/Azure/Azure-Sentinel/master/Sample%20Data/Feeds/UnusualUserAgents.csv"]
with(format="csv", ignoreFirstRecord=True)
| extend joiner = 1);
let CustomUserAgentIndicators = (_ASIM_GetWatchlistRaw("UnusualUserAgents") // Create new Watchlist and add your custom indicators(Optional)
| extend
UserAgent = tostring(WatchlistItem["UserAgent"]),
UserAgentCategory = tostring(WatchlistItem["UserAgentCategory"])
| project UserAgent, UserAgentCategory, joiner = 1
| where isnotempty(UserAgent) and isnotempty(UserAgentCategory));
let CombinedUAList = union knownUserAgentsIndicators, CustomUserAgentIndicators;
let knownUserAgents=toscalar(CombinedUAList
| where isnotempty(UserAgent)
| summarize make_list(UserAgent, 1000));
_Im_WebSession (starttime=ago(lookback), httpuseragent_has_any=knownUserAgents)
| project SrcIpAddr, SrcUsername, SrcHostname, HttpUserAgent, Url, DstIpAddr, DstPortNumber, TimeGenerated
| extend joiner = 1
| join kind=inner CombinedUAList on joiner
| where HttpUserAgent has UserAgent
| summarize
EventCount=count(),
EventStartTime = min(TimeGenerated),
EventEndTime=max(TimeGenerated)
by SrcIpAddr, SrcUsername, SrcHostname, Url, HttpUserAgent, DstIpAddr, DstPortNumber, UserAgentCategory
| extend Name = iif(SrcUsername contains "@", tostring(split(SrcUsername,'@',0)[0]),SrcUsername), UPNSuffix = iif(SrcUsername contains "@",tostring(split(SrcUsername,'@',1)[0]),"")
entityMappings:
- entityType: URL
fieldMappings:
- identifier: Url
columnName: Url
- entityType: IP
fieldMappings:
- identifier: Address
columnName: SrcIpAddr
- entityType: IP
fieldMappings:
- identifier: Address
columnName: DstIpAddr
- entityType: Account
fieldMappings:
- identifier: Name
columnName: Name
- identifier: UPNSuffix
columnName: UPNSuffix
- entityType: Host
fieldMappings:
- identifier: HostName
columnName: SrcHostname
eventGroupingSettings:
aggregationKind: AlertPerResult
customDetails:
EventCount: EventCount
EventStartTime: EventStartTime
EventEndTime: EventEndTime
HttpUserAgent: HttpUserAgent
UserAgentCategory: UserAgentCategory
alertDetailsOverride:
alertDisplayNameFormat: "User '{{SrcUsername}}' with IP '{{SrcIpAddr}}' has been observed using User Agent categorized as '{{UserAgentCategory}}'"
alertDescriptionFormat: "User accessed this URL '{{Url}}' using User Agent '{{HttpUserAgent}}'. Perform a thorough analysis of the requests associated with this user agent header"
version: 1.0.0
kind: Scheduled
Stages and Predicates
Parameters
let lookback = 5m;
let CombinedUAList = union knownUserAgentsIndicators, CustomUserAgentIndicators;
Let binding: knownUserAgentsIndicators
let knownUserAgentsIndicators = materialize(externaldata(UserAgent: string, UserAgentCategory: string)
[@"https://raw.githubusercontent.com/Azure/Azure-Sentinel/master/Sample%20Data/Feeds/UnusualUserAgents.csv"]
with(format="csv", ignoreFirstRecord=True)
| extend joiner = 1);
Let binding: CustomUserAgentIndicators
let CustomUserAgentIndicators = (_ASIM_GetWatchlistRaw("UnusualUserAgents")
| extend
UserAgent = tostring(WatchlistItem["UserAgent"]),
UserAgentCategory = tostring(WatchlistItem["UserAgentCategory"])
| project UserAgent, UserAgentCategory, joiner = 1
| where isnotempty(UserAgent) and isnotempty(UserAgentCategory));
Let binding: knownUserAgents
let knownUserAgents = toscalar(CombinedUAList
| where isnotempty(UserAgent)
| summarize make_list(UserAgent, 1000));
Derived from CombinedUAList.
Stage 1: source
_Im_WebSession (starttime=ago(lookback), httpuseragent_has_any=knownUserAgents)
Stage 2: project
| project SrcIpAddr, SrcUsername, SrcHostname, HttpUserAgent, Url, DstIpAddr, DstPortNumber, TimeGenerated
Stage 3: extend
| extend joiner = 1
Stage 4: join
| join kind=inner CombinedUAList on joiner
Stage 5: where
| where HttpUserAgent has UserAgent
Stage 6: summarize
| summarize
EventCount=count(),
EventStartTime = min(TimeGenerated),
EventEndTime=max(TimeGenerated)
by SrcIpAddr, SrcUsername, SrcHostname, Url, HttpUserAgent, DstIpAddr, DstPortNumber, UserAgentCategory
Stage 7: extend
| extend Name = iif(SrcUsername contains "@", tostring(split(SrcUsername,'@',0)[0]),SrcUsername), UPNSuffix = iif(SrcUsername contains "@",tostring(split(SrcUsername,'@',1)[0]),"")
Indicators
Each row is a field, operator, and value that the rule matches. The corpus column counts how many other rules in the catalog look for the same combination: high numbers point to widely-used, community-vetted indicators. Blank or 1 shows that the indicator is specific to this rule.
| Field | Kind | Values |
|---|---|---|
HttpUserAgent | match |
|
UserAgent | is_not_null | |
UserAgentCategory | is_not_null |
Output fields
Fields the rule emits when it matches. Chronicle authors list these in the outcome block; they appear on the detection and $risk_score drives alerting. Sentinel / Defender XDR rules build them up through project / summarize / extend stages. Sentinel maps these into alert fields via entityMappings and customDetails; Defender XDR custom detections surface them as alert fields directly.
| Field | Source |
|---|---|
DstIpAddr | summarize |
DstPortNumber | summarize |
EventCount | summarize |
EventEndTime | summarize |
EventStartTime | summarize |
HttpUserAgent | summarize |
SrcHostname | summarize |
SrcIpAddr | summarize |
SrcUsername | summarize |
Url | summarize |
UserAgentCategory | summarize |
Name | extend |
UPNSuffix | extend |