// Illustrative hunt; not executed in a live Sentinel or Falcon tenant. // Sentinel: ASIM _Im_WebSession; last 24 hours, one app and one source IP, at most 100 pairs. // Requires absolute Url, SrcIpAddr, HttpRequestMethod and numeric HTTP status in EventResultDetails. let AppHost = "REPLACE_WITH_APP_HOST"; let SourceIP = "REPLACE_WITH_SOURCE_IP"; let Events = materialize(_Im_WebSession(starttime=ago(24h), endtime=now()) | where SrcIpAddr == SourceIP | extend Parsed = parse_url(Url) | extend Host = tolower(tostring(Parsed.Host)), Path = tostring(Parsed.Path), Method = toupper(HttpRequestMethod), Status = toint(EventResultDetails) | where Host == tolower(AppHost) | project TimeGenerated, SrcIpAddr, Host, Path, Method, Status); let Discovery = Events | where Method == "GET" and Status between (200 .. 299) | where Path matches regex @"(?i)(\.js\.map$|/openapi\.json$|/swagger\.json$|/\.env(?:\.[^/]+)?$|/\.git/config$|\.(?:sql|bak)(?:\.gz)?$)" | project SrcIpAddr, Host, ReadTime = TimeGenerated, ReadPath = Path; Discovery | join kind=inner (Events | where Method in ("POST", "PUT", "PATCH", "DELETE") and Status between (200 .. 299) | where Path startswith "/api/" | project SrcIpAddr, Host, WriteTime = TimeGenerated, WritePath = Path, Method, Status ) on SrcIpAddr, Host | where WriteTime between (ReadTime .. (ReadTime + 10m)) | project ReadTime, WriteTime, SrcIpAddr, Host, ReadPath, WritePath, Method, Status | order by WriteTime desc | take 100