Adversaries may exploit the Application Gateway WAF to inject malicious payloads via cross-site scripting (XSS) to compromise user sessions or exfiltrate data. Proactively hunting for this behavior in Azure Sentinel helps identify and mitigate potential XSS attacks before they lead to data breaches or system compromise.
KQL Query
let Threshold = 1;
AzureDiagnostics
| where Category == "ApplicationGatewayFirewallLog"
| where action_s == "Matched"
| project transactionId_g, hostname_s, requestUri_s, TimeGenerated, clientIp_s, Message, details_message_s, details_data_s
| join kind = inner(
AzureDiagnostics
| where Category == "ApplicationGatewayFirewallLog"
| where action_s == "Blocked"
| parse Message with MessageText 'Total Inbound Score: ' TotalInboundScore ' - SQLI=' SQLI_Score ',XSS=' XSS_Score ',RFI=' RFI_Score ',LFI=' LFI_Score ',RCE=' RCE_Score ',PHPI=' PHPI_Score ',HTTP=' HTTP_Score ',SESS=' SESS_Score '): ' Blocked_Reason '; individual paranoia level scores:' Paranoia_Score
| where Blocked_Reason contains "XSS" and toint(TotalInboundScore) >=15 and toint(XSS_Score) >= 10 and toint(SQLI_Score) <= 5) on transactionId_g
| extend Uri = strcat(hostname_s,requestUri_s)
| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), TransactionID = make_set(transactionId_g), Message = make_set(Message), Detail_Message = make_set(details_message_s), Detail_Data = make_set(details_data_s), Total_TransactionId = dcount(transactionId_g) by clientIp_s, Uri, action_s, SQLI_Score, XSS_Score, TotalInboundScore
| where Total_TransactionId >= Threshold
id: d2bc08fa-030a-4eea-931a-762d27c6a042
name: Application Gateway WAF - XSS Detection
description: |
'Identifies a match for XSS attack in the Application gateway WAF logs. The Threshold value in the query can be changed as per your infrastructure's requirement.
References: https://owasp.org/www-project-top-ten/2017/A7_2017-Cross-Site_Scripting_(XSS)'
severity: High
requiredDataConnectors:
- connectorId: WAF
dataTypes:
- AzureDiagnostics
queryFrequency: 6h
queryPeriod: 6h
triggerOperator: gt
triggerThreshold: 0
tactics:
- InitialAccess
- Execution
relevantTechniques:
- T1189
- T1203
- T0853
tags:
- Cross Site Scripting
query: |
let Threshold = 1;
AzureDiagnostics
| where Category == "ApplicationGatewayFirewallLog"
| where action_s == "Matched"
| project transactionId_g, hostname_s, requestUri_s, TimeGenerated, clientIp_s, Message, details_message_s, details_data_s
| join kind = inner(
AzureDiagnostics
| where Category == "ApplicationGatewayFirewallLog"
| where action_s == "Blocked"
| parse Message with MessageText 'Total Inbound Score: ' TotalInboundScore ' - SQLI=' SQLI_Score ',XSS=' XSS_Score ',RFI=' RFI_Score ',LFI=' LFI_Score ',RCE=' RCE_Score ',PHPI=' PHPI_Score ',HTTP=' HTTP_Score ',SESS=' SESS_Score '): ' Blocked_Reason '; individual paranoia level scores:' Paranoia_Score
| where Blocked_Reason contains "XSS" and toint(TotalInboundScore) >=15 and toint(XSS_Score) >= 10 and toint(SQLI_Score) <= 5) on transactionId_g
| extend Uri = strcat(hostname_s,requestUri_s)
| summarize StartTime = min(TimeGenerated), EndTime = max(TimeGenerated), TransactionID = make_set(transactionId_g), Message = make_set(Message), Detail_Message = make_set(details_message_s), Detail_Data = make_set(details_data_s), Total_TransactionId = dcount(transactionId_g) by clientIp_s, Uri, action_s, SQLI_Score, XSS_Score, TotalInboundScore
| 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_s
version: 1.0.1
kind: Scheduled
metadata:
source:
kind: Community
author:
name: shabaz-github
support:
tier: Community
categories:
domains: [ "Security - Threat Protection", "Platform" ]
| Sentinel Table | Notes |
|---|---|
AzureDiagnostics | Ensure this data connector is enabled |
Scenario: Legitimate User Input with Special Characters
Description: A user submits a form with special characters (e.g., <, >, &) that are not malicious but are part of valid input (e.g., HTML entities in a form field).
Filter/Exclusion: Use a filter to exclude traffic from known internal users or IP ranges, or apply a regex to identify valid HTML entities in the request body.
Scenario: Scheduled Job Sending Test Payloads
Description: A scheduled job or automation tool (e.g., Ansible, Jenkins, or a CI/CD pipeline) sends test payloads to the WAF for validation or health checks, which may include XSS-like strings.
Filter/Exclusion: Exclude traffic from the job’s IP address or source host, or use a custom field in the log to tag test traffic.
Scenario: Admin Task with JavaScript in Logs
Description: An administrator uses a tool like curl or Postman to manually test the WAF by sending a request with a JavaScript payload for debugging or validation.
Filter/Exclusion: Exclude traffic from the admin’s IP or use a custom tag in the log to identify manual testing requests.
Scenario: Legitimate API Request with URL Encoding
Description: A legitimate API call includes URL-encoded parameters that contain special characters (e.g., +, %20, &) which are misinterpreted by the WAF as potential XSS vectors.
Filter/Exclusion: Filter by the API endpoint or use a regex to identify URL-encoded parameters in the request.
Scenario: Web Application with Dynamic Content Rendering
Description: A web application dynamically renders content using JavaScript (e.g., React, Angular) and includes user-generated content that is properly escaped, but the WAF incorrectly flags it as XSS.
*