Detection rules › Kusto

Excessive number of HTTP authentication failures from a source (ASIM Web Session schema)

Severity
low
Time window
1d
Group by
SrcIpAddr, UrlHost
Author
Yaron
Source
github.com/Azure/Azure-Sentinel

This rule identifies a source that repeatedly fails to authenticate to a web service (HTTP response code 403). This may indicate a brute force or credential stuffing attack. This rule uses the Advanced Security Information Model (ASIM) and supports any web session source that complies with ASIM.

MITRE ATT&CK coverage

Rule body kusto

id: a1bddaf8-982b-4089-ba9e-6590dfcf80ea
name: Excessive number of HTTP authentication failures from a source (ASIM Web Session schema)
description: |
    This rule identifies a source that repeatedly fails to authenticate to a web service (HTTP response code 403). This may indicate a [brute force](https://en.wikipedia.org/wiki/Brute-force_attack) or [credential stuffing](https://en.wikipedia.org/wiki/Credential_stuffing) attack.
    This rule uses the [Advanced Security Information Model (ASIM)](https://aka.ms/AboutASIM) and supports any web session source that complies with ASIM.
severity: Low
requiredDataConnectors:
  - connectorId: SquidProxy
    dataTypes:
      - SquidProxy_CL
  - connectorId: Zscaler
    dataTypes:
      - CommonSecurityLog
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
    - Persistence
    - CredentialAccess
relevantTechniques:
    - T1110
    - T1556
tags:
    - ParentAlert: https://github.com/Azure/Azure-Sentinel/blob/master/Detections/CommonSecurityLog/Wazuh-Large%20Number%20of%20Web%20errors%20from%20an%20IP.yaml
      ParentVersion: 1.1.0
    - Schema: ASIMWebSession
      SchemaVersion: 0.2.1
query: |
    let error403_count_threshold=200;
    _Im_WebSession(eventresultdetails_in=dynamic(["403"]))
    | extend ParsedUrl=parse_url(Url)
    | extend UrlHost=tostring(ParsedUrl["Host"]), UrlSchema=tostring(ParsedUrl["Schema"])
    | summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), NumberOfErrors = count(), Urls=makeset(Url) by UrlHost, SrcIpAddr
    | where NumberOfErrors > error403_count_threshold
    | sort by NumberOfErrors desc
    | extend Url=tostring(Urls[0])
entityMappings:
    - entityType: URL
      fieldMappings:
          - identifier: Url
            columnName: Url
    - entityType: IP
      fieldMappings:
          - identifier: Address
            columnName: SrcIpAddr
customDetails:
  NumberOfErrors: NumberOfErrors
alertDetailsOverride:
    alertDisplayNameFormat: Excessive number of HTTP authentication failures from {{SrcIpAddr}
    alertDescriptionFormat: A client with address {{SrcIpAddr}} generated a large number of failed authentication HTTP requests. This may indicate a [brute force](https://en.wikipedia.org/wiki/Brute-force_attack) or [credential stuffing](https://en.wikipedia.org/wiki/Credential_stuffing) attack.
version: 1.0.6
kind: Scheduled
metadata:
    source:
        kind: Community
    author:
        name: Yaron
    support:
        tier: Community
    categories:
        domains: [ "Security - Others" ]

Stages and Predicates

Parameters

let error403_count_threshold = 200;

Stage 1: source

_Im_WebSession(eventresultdetails_in=dynamic(["403"]))

Stage 2: extend

| extend ParsedUrl=parse_url(Url)

Stage 3: extend

| extend UrlHost=tostring(ParsedUrl["Host"]), UrlSchema=tostring(ParsedUrl["Schema"])

Stage 4: summarize

| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), NumberOfErrors = count(), Urls=makeset(Url) by UrlHost, SrcIpAddr
Threshold
gt 200

Stage 5: where

| where NumberOfErrors > error403_count_threshold

Stage 6: sort

| sort by NumberOfErrors desc

Stage 7: extend

| extend Url=tostring(Urls[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
NumberOfErrorsgt
  • 200 transforms: cased

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
EndTimesummarize
NumberOfErrorssummarize
SrcIpAddrsummarize
StartTimesummarize
UrlHostsummarize
Urlssummarize
Urlextend