Web shell command alert enrichment
Description
'Extracts MDATP Alerts that indicate a command was executed by a web shell. Uses time window based querying to idneitfy the potential web shell location on the server, then enriches with Attacker IP and User Agent'
Query · kql
let scriptExtensions = dynamic([".php", ".jsp", ".js", ".aspx", ".asmx", ".asax", ".cfm", ".shtml"]); let lookupWindow = 1m; let lookupBin = lookupWindow / 2.0; let distinctIpThreshold = 3; let alerts = SecurityAlert | extend alertData = parse_json(Entities), recordGuid = new_guid(); let shellAlerts = alerts | where ProviderName =~ "MDATP" | mvexpand alertData | where alertData.Type =~ "file" and alertData.Name =~ "w3wp.exe" | distinct SystemAlertId | join kind=inner (alerts) on SystemAlertId; let alldata = shellAlerts | mvexpand alertData | extend Type = alertData.Type; let filedata = alldata | extend id = tostring(alertData.$id) | extend ImageName = alertData.Name | where Type =~ "file" and ImageName != "w3wp.exe" | extend imagefileref = id; let commanddata = alldata | extend CommandLine = tostring(alertData.CommandLine) | extend creationtime = tostring(alertData.CreationTimeUtc) | where Type =~ "process" | where isnotempty(CommandLine) | extend imagefileref = tostring(alertData.ImageFile.$ref); let hostdata = alldata | where Type =~ "host" | project HostName = tostring(alertData.HostName), DnsDomain = tostring(alertData.DnsDomain), SystemAlertId | distinct HostName, DnsDomain, SystemAlertId; let commandKeyedData = filedata | join kind=inner ( commanddata ) on imagefileref | join kind=inner (hostdata) on SystemAlertId | project recordGuid, TimeGenerated, ImageName, CommandLine, TimeKey = bin(TimeGenerated, lookupBin), HostName, DnsDomain | extend Start = TimeGenerated; let baseline = W3CIISLog | project-rename SourceIP=cIP, PageAccessed=csUriStem | summarize dcount(SourceIP) by PageAccessed | where dcount_SourceIP <= distinctIpThreshold; commandKeyedData | join kind=inner ( W3CIISLog | where csUriStem has_any(scriptExtensions) | extend splitUriStem = split(csUriStem, "/") | extend FileName = splitUriStem[-1] | extend firstDir = splitUriStem[-2] | extend TimeKey = range(bin(TimeGenerated-lookupWindow, lookupBin), bin(TimeGenerated, lookupBin),lookupBin) | mv-expand TimeKey to typeof(datetime) | summarize StartTime=min(TimeGenerated), EndTime=max(TimeGenerated) by Site=sSiteName, HostName=sComputerName, AttackerIP=cIP, AttackerUserAgent=csUserAgent, csUriStem, filename=tostring(FileName), tostring(firstDir), TimeKey ) on TimeKey, HostName | where (StartTime - EndTime) between (0min .. lookupWindow) | extend IPCustomEntity = AttackerIP, timestamp = StartTime | extend attackerP = pack(AttackerIP, AttackerUserAgent) | summarize Site=make_set(Site), Attacker=make_bag(attackerP) by csUriStem, filename, tostring(ImageName), CommandLine, HostName, IPCustomEntity, timestamp | project Site, ShellLocation=csUriStem, ShellName=filename, ParentProcess=ImageName, CommandLine, Attacker, HostName, IPCustomEntity, timestamp | join kind=inner (baseline) on $left.ShellLocation == $right.PageAccessed
Rule dependencies
Depends on
-
correlates · Sentinel SecurityAlert
An external product alert, not indexed on this site.