Detection rules › Kusto
Detect web requests to potentially harmful files (ASIM Web Session)
'This rule detects web requests made to URLs containing file types such as .ps1, .bat, .vbs,.scr etc. which have the potential to be harmful if downloaded. This rule uses the Advanced Security Information Model (ASIM) and supports any web session source that complies with ASIM.'
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Initial Access | |
| Execution | |
| Persistence |
Rule body
id: c6608467-3678-45fe-b038-b590ce6d00fb
name: Detect web requests to potentially harmful files (ASIM Web Session)
description: |
'This rule detects web requests made to URLs containing file types such as .ps1, .bat, .vbs,.scr etc. which have the potential to be harmful if downloaded. This rule uses the [Advanced Security Information Model (ASIM)](https://aka.ms/AboutASIM) and supports any web session source that complies with ASIM.'
severity: Medium
status: Available
tags:
- Schema: WebSession
SchemaVersion: 0.2.6
requiredDataConnectors: []
queryFrequency: 5m
queryPeriod: 5m
triggerOperator: gt
triggerThreshold: 0
tactics:
- InitialAccess
- Persistence
- Execution
relevantTechniques:
- T1133
- T1203
- T1566
query: |
let lookback = 5m;
let RiskyFileExtensions = materialize(externaldata(Extensions: string)
[@"https://raw.githubusercontent.com/Azure/Azure-Sentinel/master/Sample%20Data/Feeds/RiskyFileExtensionsInUrl.csv"]
with(format="csv", ignoreFirstRecord=True));
let CustomRiskyFileExtensions = (_ASIM_GetWatchlistRaw("Web_RiskyFileExtensions") // Create new Watchlist and add your custom indicators(Optional)
| extend
Extensions = tostring(WatchlistItem["Extensions"])
| project Extensions
| where isnotempty(Extensions));
let CombinedRiskyFileExtensions = union RiskyFileExtensions, CustomRiskyFileExtensions;
let knownRiskyFileExtensions=toscalar(CombinedRiskyFileExtensions
| where isnotempty(Extensions)
| summarize make_set(Extensions, 1000));
let Whitelisted_Domains = materialize(externaldata(WhiteListedDomains: string)
[@"https://raw.githubusercontent.com/Azure/Azure-Sentinel/master/Sample%20Data/Feeds/WhiteListedDomainsForWebSessionUseCases.csv"]
with(format="csv", ignoreFirstRecord=True));
let CustomWhiteListedDomains = (_ASIM_GetWatchlistRaw("Web_WhiteListedDomains") // Create new Watchlist and add your white listed domains(Optional)
| extend
WhiteListedDomains = tostring(WatchlistItem["WhiteListedDomains"])
| project WhiteListedDomains
| where isnotempty(WhiteListedDomains));
let CombinedWhitelisted_Domains = union Whitelisted_Domains, CustomWhiteListedDomains;
let knownWhitelisted_Domains=toscalar(CombinedWhitelisted_Domains
| where isnotempty(WhiteListedDomains)
| summarize make_set(WhiteListedDomains, 1000));
_Im_WebSession (starttime=ago(lookback), url_has_any=knownRiskyFileExtensions, eventresult='Success')
| project
Url,
SrcIpAddr,
TimeGenerated,
SrcUsername,
SrcHostname,
DstIpAddr,
DstPortNumber
| extend requestedFileName=tostring(split(tostring(parse_url(Url)["Path"]), '/')[-1])
| extend requestedFileExt=extract(@'(\.\w+)$', 1, requestedFileName, typeof(string))
| where requestedFileExt in~ (knownRiskyFileExtensions)
| summarize
EventCount=count(),
EventStartTime=min(TimeGenerated),
EventEndTime=max(TimeGenerated)
by
SrcUsername,
SrcIpAddr,
SrcHostname,
DstIpAddr,
DstPortNumber,
Url,
requestedFileName
| extend FQDN = split(parse_url(Url)["Host"], '.')
| extend Domain = iif(array_length(FQDN) > 1, strcat(FQDN[-2], '.', FQDN[-1]), FQDN)
| where Domain !in~ (knownWhitelisted_Domains)
| project-away FQDN
| 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: URL
fieldMappings:
- identifier: Url
columnName: Url
- entityType: File
fieldMappings:
- identifier: Name
columnName: requestedFileName
- entityType: Host
fieldMappings:
- identifier: HostName
columnName: SrcHostname
- entityType: Account
fieldMappings:
- identifier: Name
columnName: Name
- identifier: UPNSuffix
columnName: UPNSuffix
eventGroupingSettings:
aggregationKind: AlertPerResult
customDetails:
EventCount: EventCount
EventStartTime: EventStartTime
EventEndTime: EventEndTime
DstIpAddr: DstIpAddr
alertDetailsOverride:
alertDisplayNameFormat: "User '{{SrcUsername}}' with IP address '{{SrcIpAddr}}' accessed a potentially harmful URL"
alertDescriptionFormat: "User accessed URL - '{{Url}}' that contains a file - '{{requestedFileName}}' with risky extension. Downloading this file could pose a potential risk"
version: 1.0.0
kind: Scheduled
Stages and Predicates
Parameters
let lookback = 5m;
let CombinedRiskyFileExtensions = union RiskyFileExtensions, CustomRiskyFileExtensions;
let CombinedWhitelisted_Domains = union Whitelisted_Domains, CustomWhiteListedDomains;
Let binding: RiskyFileExtensions
let RiskyFileExtensions = materialize(externaldata(Extensions: string)
[@"https://raw.githubusercontent.com/Azure/Azure-Sentinel/master/Sample%20Data/Feeds/RiskyFileExtensionsInUrl.csv"]
with(format="csv", ignoreFirstRecord=True));
Let binding: CustomRiskyFileExtensions
let CustomRiskyFileExtensions = (_ASIM_GetWatchlistRaw("Web_RiskyFileExtensions")
| extend
Extensions = tostring(WatchlistItem["Extensions"])
| project Extensions
| where isnotempty(Extensions));
Let binding: knownRiskyFileExtensions
let knownRiskyFileExtensions = toscalar(CombinedRiskyFileExtensions
| where isnotempty(Extensions)
| summarize make_set(Extensions, 1000));
Let binding: Whitelisted_Domains
let Whitelisted_Domains = materialize(externaldata(WhiteListedDomains: string)
[@"https://raw.githubusercontent.com/Azure/Azure-Sentinel/master/Sample%20Data/Feeds/WhiteListedDomainsForWebSessionUseCases.csv"]
with(format="csv", ignoreFirstRecord=True));
Let binding: CustomWhiteListedDomains
let CustomWhiteListedDomains = (_ASIM_GetWatchlistRaw("Web_WhiteListedDomains")
| extend
WhiteListedDomains = tostring(WatchlistItem["WhiteListedDomains"])
| project WhiteListedDomains
| where isnotempty(WhiteListedDomains));
Let binding: knownWhitelisted_Domains
let knownWhitelisted_Domains = toscalar(CombinedWhitelisted_Domains
| where isnotempty(WhiteListedDomains)
| summarize make_set(WhiteListedDomains, 1000));
Stage 1: source
_Im_WebSession (starttime=ago(lookback), url_has_any=knownRiskyFileExtensions, eventresult='Success')
Stage 2: project
| project
Url,
SrcIpAddr,
TimeGenerated,
SrcUsername,
SrcHostname,
DstIpAddr,
DstPortNumber
Stage 3: extend
| extend requestedFileName=tostring(split(tostring(parse_url(Url)["Path"]), '/')[-1])
Stage 4: extend
| extend requestedFileExt=extract(@'(\.\w+)$', 1, requestedFileName, typeof(string))
Stage 5: where
| where requestedFileExt in~ (knownRiskyFileExtensions)
Stage 6: summarize
| summarize
EventCount=count(),
EventStartTime=min(TimeGenerated),
EventEndTime=max(TimeGenerated)
by
SrcUsername,
SrcIpAddr,
SrcHostname,
DstIpAddr,
DstPortNumber,
Url,
requestedFileName
Stage 7: extend
| extend FQDN = split(parse_url(Url)["Host"], '.')
Stage 8: extend
| extend Domain = iif(array_length(FQDN) > 1, strcat(FQDN[-2], '.', FQDN[-1]), FQDN)
Stage 9: where
| where Domain !in~ (knownWhitelisted_Domains)
Stage 10: project-away
| project-away FQDN
Stage 11: extend
| extend Name = iif(SrcUsername contains "@", tostring(split(SrcUsername,'@',0)[0]),SrcUsername), UPNSuffix = iif(SrcUsername contains "@",tostring(split(SrcUsername,'@',1)[0]),"")
Exclusions
The rule actively suppresses these predicates.
| Field | Kind | Excluded values | Search |
|---|---|---|---|
Domain | eq | knownWhitelisted_Domains | excludes:Domain field:"Domain" value:"knownWhitelisted_Domains" |
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
requestedFileExt | in |
| field:"requestedFileExt" kind:in value:"knownRiskyFileExtensions" |
Output fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
DstIpAddr | summarize |
DstPortNumber | summarize |
EventCount | summarize |
EventEndTime | summarize |
EventStartTime | summarize |
SrcHostname | summarize |
SrcIpAddr | summarize |
SrcUsername | summarize |
Url | summarize |
requestedFileName | summarize |
Domain | extend |
Name | extend |
UPNSuffix | extend |