Detection rules › Kusto

App Gateway WAF - SQLi Detection

Status
available
Severity
high
Time window
6h
Group by
Action, ClientIp, Uri
Source
github.com/Azure/Azure-Sentinel

'Identifies a match for a SQL Injection attack in the App Gateway WAF logs. The threshold value in the query can be changed as per your infrastructure's requirements. References: https://owasp.org/Top10/A03_2021-Injection/'

MITRE ATT&CK coverage

Rule body kusto

id: bdb2cd63-99f2-472e-b1b9-acba473b6744
name: App Gateway WAF - SQLi Detection  
description: | 
  'Identifies a match for a SQL Injection attack in the App Gateway WAF logs. The threshold value in the query can be changed as per your infrastructure's requirements.
  References: https://owasp.org/Top10/A03_2021-Injection/'
severity: High
status: Available
requiredDataConnectors: 
  - connectorId: WAF
    dataTypes: 
      - AzureDiagnostics
queryFrequency: 6h 
queryPeriod: 6h 
triggerOperator: gt 
triggerThreshold: 0 
tactics: 
  - DefenseEvasion
  - Execution
  - InitialAccess
  - PrivilegeEscalation 
relevantTechniques:
  - T1211
  - T1059
  - T1190
  - T0890
tags:
  - SQL Injection
query:  | 
 let Threshold = 3;  
 AGWFirewallLogs
 | where Action == "Matched"
 | where Message has "SQL Injection"
 | project TransactionId, Hostname, RequestUri, TimeGenerated, ClientIp, Message
 | join kind = inner(
 AGWFirewallLogs
 | where Action == "Blocked"
 | extend transactionId_g = tostring(TransactionId)) on TransactionId
 | extend Uri = strcat(Hostname,RequestUri)
 | summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), TransactionID = make_set(transactionId_g,100), Message = make_set(Message,100), Total_TransactionId = dcount(transactionId_g) by ClientIp, Uri, Action
 | where Total_TransactionId >= Threshold
# The Threshold value above can be changed as per your infrastructure's requirement
entityMappings:
  - entityType: URL
    fieldMappings:
      - identifier: Url
        columnName: Uri
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: ClientIp 
version: 1.0.2
kind: Scheduled

Stages and Predicates

Parameters

let Threshold = 3;

Stage 1: source

AGWFirewallLogs

Stage 2: where

| where Action == "Matched"

Stage 3: where

| where Message has "SQL Injection"

Stage 4: project

| project TransactionId, Hostname, RequestUri, TimeGenerated, ClientIp, Message

Stage 5: join

| join kind = inner(
AGWFirewallLogs
| where Action == "Blocked"
| extend transactionId_g = tostring(TransactionId)) on TransactionId

Stage 6: extend

| extend Uri = strcat(Hostname,RequestUri)

Stage 7: summarize

| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), TransactionID = make_set(transactionId_g,100), Message = make_set(Message,100), Total_TransactionId = dcount(transactionId_g) by ClientIp, Uri, Action
Threshold
ge 3

Stage 8: where

| where Total_TransactionId >= Threshold

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
Actioneq
  • Blocked transforms: cased
  • Matched transforms: cased
Messagematch
  • SQL Injection transforms: term
Total_TransactionIdge
  • 3 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
Actionsummarize
ClientIpsummarize
EndTimesummarize
Messagesummarize
StartTimesummarize
Total_TransactionIdsummarize
TransactionIDsummarize
Urisummarize