Detection rules › Kusto

Detect potential file enumeration activity (ASIM Web Session)

Status
available
Severity
medium
Time window
1h
Group by
DstHostname, SrcHostname, SrcIpAddr, SrcUsername
Source
github.com/Azure/Azure-Sentinel

'This detection method identifies potential cases of file enumeration activity. The query is designed to identify client sources that generate multiple requests resulting in 404 error codes'

MITRE ATT&CK coverage

TacticTechniques
Credential AccessT1110 Brute Force
DiscoveryT1083 File and Directory Discovery
Command & ControlT1071 Application Layer Protocol

Rule body kusto

id: b3731ce1-1f04-47c4-95c2-9827408c4375
name: Detect potential file enumeration activity (ASIM Web Session)
description: |
  'This detection method identifies potential cases of file enumeration activity. The query is designed to identify client sources that generate multiple requests resulting in 404 error codes'
severity: Medium
status: Available 
tags:
  - Schema: WebSession
    SchemaVersion: 0.2.6
requiredDataConnectors: []
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Discovery
  - CommandAndControl
  - CredentialAccess
relevantTechniques:
  - T1083
  - T1071
  - T1110
query: |
  // HTTP response status codes indicate whether a specific HTTP request has been successfully completed.
  // Please refer this for more details: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status
  let HTTPErrorCode=dynamic(["404"]);
  let threshold = 10; // You can update this threshold to the value that suites your environment
  // let FileNotFoundRequests = 
  _Im_WebSession (starttime=ago(1h))
  | where EventResultDetails in~ (HTTPErrorCode)
  // Filter the logs to include only HTTP GET requests with an HTTP status code of 404 and '/' in the URL
  | where HttpRequestMethod =~ "GET" and Url contains "/"
  | summarize
      RequestCount = count(),
      FileCount=dcount(Url),
      EventStartTime=min(TimeGenerated),
      EventEndTime=max(TimeGenerated),
      RequestURLs = make_set(Url, 100),
      DestinationIPList=make_set(DstIpAddr, 100)
      by SrcIpAddr, SrcUsername, SrcHostname, DstHostname
  | where RequestCount > threshold  // Adjust the threshold as per your requirements
  | 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: Account
    fieldMappings:
      - identifier: Name
        columnName: Name
      - identifier: UPNSuffix
        columnName: UPNSuffix
  - entityType: Host
    fieldMappings:
      - identifier: HostName
        columnName: DstHostname
  - entityType: Host
    fieldMappings:
      - identifier: HostName
        columnName: SrcHostname
eventGroupingSettings:
  aggregationKind: AlertPerResult
customDetails:
  RequestCount: RequestCount
  RequestURLs: RequestURLs
  FileCount: FileCount
  EventStartTime: EventStartTime
  EventEndTime: EventEndTime
  DestinationIPList: DestinationIPList
alertDetailsOverride:
  alertDisplayNameFormat: "User '{{SrcUsername}}' with IP '{{SrcIpAddr}}' has been observed with performing file enumeration activity"
  alertDescriptionFormat: "User generated multiple requests '{{RequestCount}}' that has resulted in error code '404', suggesting the possibility of file enumeration activity. It's important to investigate the source and patterns of these extensive 404 errors to identify potential security threats. Details about this error code could be found [here](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status)"
version: 1.0.0
kind: Scheduled

Stages and Predicates

Parameters

let HTTPErrorCode = dynamic(["404"]);
let threshold = 10;

Stage 1: source

_Im_WebSession (starttime=ago(1h))

Stage 2: where

| where EventResultDetails in~ (HTTPErrorCode)

Stage 3: where

| where HttpRequestMethod =~ "GET" and Url contains "/"

Stage 4: summarize

| summarize
    RequestCount = count(),
    FileCount=dcount(Url),
    EventStartTime=min(TimeGenerated),
    EventEndTime=max(TimeGenerated),
    RequestURLs = make_set(Url, 100),
    DestinationIPList=make_set(DstIpAddr, 100)
    by SrcIpAddr, SrcUsername, SrcHostname, DstHostname
Threshold
gt 10

Stage 5: where

| where RequestCount > threshold

Stage 6: 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.

FieldKindValues
EventResultDetailsin
  • HTTPErrorCode
HttpRequestMethodeq
  • GET
RequestCountgt
  • 10 transforms: cased
Urlcontains
  • /

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.

FieldSource
DestinationIPListsummarize
DstHostnamesummarize
EventEndTimesummarize
EventStartTimesummarize
FileCountsummarize
RequestCountsummarize
RequestURLssummarize
SrcHostnamesummarize
SrcIpAddrsummarize
SrcUsernamesummarize
Nameextend
UPNSuffixextend