A potentially malicious web request was executed against a web server
Id | 46ac55ae-47b8-414a-8f94-89ccd1962178 |
Rulename | A potentially malicious web request was executed against a web server |
Description | Detects unobstructed Web Application Firewall (WAF) activity in sessions where the WAF blocked incoming requests by computing the ratio between blocked requests and unobstructed WAF requests in these sessions (BlockvsSuccessRatio metric). A high ratio value for a given client IP and hostname calls for further investigation of the WAF data in that session, due to the significantly high number of blocked requests and a few unobstructed logs that may be malicious but have passed undetected through the WAF. The successCode variable defines what the detection thinks is a successful status code and should be altered to fit the environment. |
Severity | Medium |
Tactics | InitialAccess |
Techniques | T1190 |
Required data connectors | WAF |
Kind | Scheduled |
Query frequency | 1d |
Query period | 1d |
Trigger threshold | 0 |
Trigger operator | gt |
Source Uri | https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Azure Web Application Firewall (WAF)/Analytic Rules/MaliciousWAFSessions.yaml |
Version | 1.0.3 |
Arm template | 46ac55ae-47b8-414a-8f94-89ccd1962178.json |
let queryperiod = 1d;
let mode = 'Blocked';
let successCode = dynamic(['200', '101','204', '400','504','304','401','500']);
let sessionBin = 30m;
AzureDiagnostics
| where TimeGenerated > ago(queryperiod)
| where Category =~ 'ApplicationGatewayFirewallLog' and action_s == mode
| sort by hostname_s asc, clientIp_s asc, TimeGenerated asc
| extend SessionBlockedStarted = row_window_session(TimeGenerated, queryperiod, 10m, ((clientIp_s != prev(clientIp_s)) or (hostname_s != prev(hostname_s))))
| summarize SessionBlockedEnded = max(TimeGenerated), SessionBlockedCount = count() by hostname_s, clientIp_s, SessionBlockedStarted
| extend TimeKey = range(bin(SessionBlockedStarted, sessionBin), bin(SessionBlockedEnded, sessionBin), sessionBin)
| mv-expand TimeKey to typeof(datetime)
| join kind = inner(
AzureDiagnostics
| where TimeGenerated > ago(queryperiod)
| where Category =~ 'ApplicationGatewayAccessLog' and (isempty(httpStatus_d) or httpStatus_d in (successCode))
| extend TimeKey = bin(TimeGenerated, sessionBin)
| extend hostname_s = coalesce(hostname_s,host_s), clientIp_s = coalesce(clientIp_s,clientIP_s)
) on TimeKey, hostname_s , clientIp_s
| where TimeGenerated between (SessionBlockedStarted..SessionBlockedEnded)
| extend
originalRequestUriWithArgs_s = column_ifexists("originalRequestUriWithArgs_s", ""),
serverStatus_s = column_ifexists("serverStatus_s", "")
| summarize
SuccessfulAccessCount = count(),
UserAgents = make_set(userAgent_s, 250),
RequestURIs = make_set(requestUri_s, 250),
OriginalRequestURIs = make_set(originalRequestUriWithArgs_s, 250),
SuccessCodes = make_set(httpStatus_d, 250),
SuccessCodes_BackendServer = make_set(serverStatus_s, 250),
take_any(SessionBlockedEnded, SessionBlockedCount)
by hostname_s, clientIp_s, SessionBlockedStarted
| where SessionBlockedCount > SuccessfulAccessCount
| extend timestamp = SessionBlockedStarted, IPCustomEntity = clientIp_s
| extend BlockvsSuccessRatio = SessionBlockedCount/toreal(SuccessfulAccessCount)
| sort by BlockvsSuccessRatio desc, timestamp asc
| project-reorder SessionBlockedStarted, SessionBlockedEnded, hostname_s, clientIp_s, SessionBlockedCount, SuccessfulAccessCount, BlockvsSuccessRatio, SuccessCodes, RequestURIs, OriginalRequestURIs, UserAgents
OriginalUri: https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Azure Web Application Firewall (WAF)/Analytic Rules/MaliciousWAFSessions.yaml
entityMappings:
- fieldMappings:
- identifier: Address
columnName: clientIp_s
entityType: IP
kind: Scheduled
query: |
let queryperiod = 1d;
let mode = 'Blocked';
let successCode = dynamic(['200', '101','204', '400','504','304','401','500']);
let sessionBin = 30m;
AzureDiagnostics
| where TimeGenerated > ago(queryperiod)
| where Category =~ 'ApplicationGatewayFirewallLog' and action_s == mode
| sort by hostname_s asc, clientIp_s asc, TimeGenerated asc
| extend SessionBlockedStarted = row_window_session(TimeGenerated, queryperiod, 10m, ((clientIp_s != prev(clientIp_s)) or (hostname_s != prev(hostname_s))))
| summarize SessionBlockedEnded = max(TimeGenerated), SessionBlockedCount = count() by hostname_s, clientIp_s, SessionBlockedStarted
| extend TimeKey = range(bin(SessionBlockedStarted, sessionBin), bin(SessionBlockedEnded, sessionBin), sessionBin)
| mv-expand TimeKey to typeof(datetime)
| join kind = inner(
AzureDiagnostics
| where TimeGenerated > ago(queryperiod)
| where Category =~ 'ApplicationGatewayAccessLog' and (isempty(httpStatus_d) or httpStatus_d in (successCode))
| extend TimeKey = bin(TimeGenerated, sessionBin)
| extend hostname_s = coalesce(hostname_s,host_s), clientIp_s = coalesce(clientIp_s,clientIP_s)
) on TimeKey, hostname_s , clientIp_s
| where TimeGenerated between (SessionBlockedStarted..SessionBlockedEnded)
| extend
originalRequestUriWithArgs_s = column_ifexists("originalRequestUriWithArgs_s", ""),
serverStatus_s = column_ifexists("serverStatus_s", "")
| summarize
SuccessfulAccessCount = count(),
UserAgents = make_set(userAgent_s, 250),
RequestURIs = make_set(requestUri_s, 250),
OriginalRequestURIs = make_set(originalRequestUriWithArgs_s, 250),
SuccessCodes = make_set(httpStatus_d, 250),
SuccessCodes_BackendServer = make_set(serverStatus_s, 250),
take_any(SessionBlockedEnded, SessionBlockedCount)
by hostname_s, clientIp_s, SessionBlockedStarted
| where SessionBlockedCount > SuccessfulAccessCount
| extend timestamp = SessionBlockedStarted, IPCustomEntity = clientIp_s
| extend BlockvsSuccessRatio = SessionBlockedCount/toreal(SuccessfulAccessCount)
| sort by BlockvsSuccessRatio desc, timestamp asc
| project-reorder SessionBlockedStarted, SessionBlockedEnded, hostname_s, clientIp_s, SessionBlockedCount, SuccessfulAccessCount, BlockvsSuccessRatio, SuccessCodes, RequestURIs, OriginalRequestURIs, UserAgents
triggerOperator: gt
version: 1.0.3
queryPeriod: 1d
name: A potentially malicious web request was executed against a web server
severity: Medium
relevantTechniques:
- T1190
requiredDataConnectors:
- dataTypes:
- AzureDiagnostics
connectorId: WAF
triggerThreshold: 0
tactics:
- InitialAccess
queryFrequency: 1d
id: 46ac55ae-47b8-414a-8f94-89ccd1962178
status: Available
description: |
'Detects unobstructed Web Application Firewall (WAF) activity in sessions where the WAF blocked incoming requests by computing the
ratio between blocked requests and unobstructed WAF requests in these sessions (BlockvsSuccessRatio metric). A high ratio value for
a given client IP and hostname calls for further investigation of the WAF data in that session, due to the significantly high number
of blocked requests and a few unobstructed logs that may be malicious but have passed undetected through the WAF. The successCode
variable defines what the detection thinks is a successful status code and should be altered to fit the environment.'
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"workspace": {
"type": "String"
}
},
"resources": [
{
"apiVersion": "2023-02-01-preview",
"id": "[concat(resourceId('Microsoft.OperationalInsights/workspaces/providers', parameters('workspace'), 'Microsoft.SecurityInsights'),'/alertRules/46ac55ae-47b8-414a-8f94-89ccd1962178')]",
"kind": "Scheduled",
"name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/46ac55ae-47b8-414a-8f94-89ccd1962178')]",
"properties": {
"alertRuleTemplateName": "46ac55ae-47b8-414a-8f94-89ccd1962178",
"customDetails": null,
"description": "'Detects unobstructed Web Application Firewall (WAF) activity in sessions where the WAF blocked incoming requests by computing the \nratio between blocked requests and unobstructed WAF requests in these sessions (BlockvsSuccessRatio metric). A high ratio value for \na given client IP and hostname calls for further investigation of the WAF data in that session, due to the significantly high number \nof blocked requests and a few unobstructed logs that may be malicious but have passed undetected through the WAF. The successCode \nvariable defines what the detection thinks is a successful status code and should be altered to fit the environment.'\n",
"displayName": "A potentially malicious web request was executed against a web server",
"enabled": true,
"entityMappings": [
{
"entityType": "IP",
"fieldMappings": [
{
"columnName": "clientIp_s",
"identifier": "Address"
}
]
}
],
"OriginalUri": "https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/Azure Web Application Firewall (WAF)/Analytic Rules/MaliciousWAFSessions.yaml",
"query": "let queryperiod = 1d;\nlet mode = 'Blocked';\nlet successCode = dynamic(['200', '101','204', '400','504','304','401','500']);\nlet sessionBin = 30m;\nAzureDiagnostics\n| where TimeGenerated > ago(queryperiod)\n| where Category =~ 'ApplicationGatewayFirewallLog' and action_s == mode\n| sort by hostname_s asc, clientIp_s asc, TimeGenerated asc\n| extend SessionBlockedStarted = row_window_session(TimeGenerated, queryperiod, 10m, ((clientIp_s != prev(clientIp_s)) or (hostname_s != prev(hostname_s))))\n| summarize SessionBlockedEnded = max(TimeGenerated), SessionBlockedCount = count() by hostname_s, clientIp_s, SessionBlockedStarted\n| extend TimeKey = range(bin(SessionBlockedStarted, sessionBin), bin(SessionBlockedEnded, sessionBin), sessionBin)\n| mv-expand TimeKey to typeof(datetime)\n| join kind = inner(\n AzureDiagnostics\n | where TimeGenerated > ago(queryperiod)\n | where Category =~ 'ApplicationGatewayAccessLog' and (isempty(httpStatus_d) or httpStatus_d in (successCode))\n | extend TimeKey = bin(TimeGenerated, sessionBin)\n | extend hostname_s = coalesce(hostname_s,host_s), clientIp_s = coalesce(clientIp_s,clientIP_s)\n) on TimeKey, hostname_s , clientIp_s\n| where TimeGenerated between (SessionBlockedStarted..SessionBlockedEnded)\n| extend\n originalRequestUriWithArgs_s = column_ifexists(\"originalRequestUriWithArgs_s\", \"\"),\n serverStatus_s = column_ifexists(\"serverStatus_s\", \"\")\n| summarize\n SuccessfulAccessCount = count(),\n UserAgents = make_set(userAgent_s, 250),\n RequestURIs = make_set(requestUri_s, 250),\n OriginalRequestURIs = make_set(originalRequestUriWithArgs_s, 250),\n SuccessCodes = make_set(httpStatus_d, 250),\n SuccessCodes_BackendServer = make_set(serverStatus_s, 250),\n take_any(SessionBlockedEnded, SessionBlockedCount)\n by hostname_s, clientIp_s, SessionBlockedStarted\n| where SessionBlockedCount > SuccessfulAccessCount\n| extend timestamp = SessionBlockedStarted, IPCustomEntity = clientIp_s\n| extend BlockvsSuccessRatio = SessionBlockedCount/toreal(SuccessfulAccessCount)\n| sort by BlockvsSuccessRatio desc, timestamp asc\n| project-reorder SessionBlockedStarted, SessionBlockedEnded, hostname_s, clientIp_s, SessionBlockedCount, SuccessfulAccessCount, BlockvsSuccessRatio, SuccessCodes, RequestURIs, OriginalRequestURIs, UserAgents\n",
"queryFrequency": "P1D",
"queryPeriod": "P1D",
"severity": "Medium",
"status": "Available",
"suppressionDuration": "PT1H",
"suppressionEnabled": false,
"tactics": [
"InitialAccess"
],
"techniques": [
"T1190"
],
"templateVersion": "1.0.3",
"triggerOperator": "GreaterThan",
"triggerThreshold": 0
},
"type": "Microsoft.OperationalInsights/workspaces/providers/alertRules"
}
]
}