Detection rules › Kusto
Azure WAF matching for Log4j vuln(CVE-2021-44228)
'This query will alert on a positive pattern match by Azure WAF for CVE-2021-44228 log4j vulnerability exploitation attempt. If possible, it then decodes the malicious command for further analysis. Reference: https://www.microsoft.com/security/blog/2021/12/11/guidance-for-preventing-detecting-and-hunting-for-cve-2021-44228-log4j-2-exploitation/'
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Initial Access |
Rules detecting the same action
These rules filter on the same operation.
Rule body
id: 2de8abd6-a613-450e-95ed-08e503369fb3
name: Azure WAF matching for Log4j vuln(CVE-2021-44228)
description: |
'This query will alert on a positive pattern match by Azure WAF for CVE-2021-44228 log4j vulnerability exploitation attempt. If possible, it then decodes the malicious command for further analysis.
Reference: https://www.microsoft.com/security/blog/2021/12/11/guidance-for-preventing-detecting-and-hunting-for-cve-2021-44228-log4j-2-exploitation/'
severity: High
status: Available
requiredDataConnectors:
- connectorId: WAF
dataTypes:
- AzureDiagnostics
queryFrequency: 6h
queryPeriod: 6h
triggerOperator: gt
triggerThreshold: 0
tactics:
- InitialAccess
relevantTechniques:
- T1190
tags:
- CVE-2021-44228
- log4j
- log4shell
query: |
let log4jioc = dynamic(["jndi","ldap","${::"]);
AzureDiagnostics
| where ResourceProvider == "MICROSOFT.NETWORK" and Category in ("ApplicationGatewayFirewallLog", "FrontdoorWebApplicationFirewallLog")
| extend details_data_s = column_ifexists("details_data_s", tostring(AdditionalFields.details_data))
| extend details_msg_s = column_ifexists("details_msg_s", tostring(AdditionalFields.details_msg))
| extend details_message_s = column_ifexists("details_message_s", tostring(AdditionalFields.details_message))
| where requestUri_s has_any (log4jioc) or details_message_s has_any (log4jioc) or details_msg_s has_any (log4jioc) or details_data_s has_any (log4jioc)
| extend Malicious = iff(isnotempty( details_data_s),details_data_s,iff(isnotempty( requestUri_s),requestUri_s,""))
|parse Malicious with * '${' MaliciousCommand '}' *
| extend EncodeCmd = iff(MaliciousCommand has 'Base64/', split(split(MaliciousCommand, "Base64/",1)[0], "}", 0)[0], "")
| extend EncodeCmd1 = iff(MaliciousCommand has 'base64/', split(split(MaliciousCommand, "base64/",1)[0], "}", 0)[0], "")
| extend CmdLine = iff( isnotempty(EncodeCmd), EncodeCmd, EncodeCmd1)
| extend DecodedCmdLine = base64_decode_tostring(tostring(CmdLine))
| extend DecodedCmdLine = iff( isnotempty(DecodedCmdLine), DecodedCmdLine, "Unable to decode/Doesn't need decoding")
| project TimeGenerated, Target=column_ifexists("hostname_s", tostring(AdditionalFields.hostname)), MaliciousHost = column_ifexists("clientIp_s", tostring(AdditionalFields.clientIp)) , MaliciousCommand, details_data_s = column_ifexists("details_data_s", tostring(AdditionalFields.details_data)), DecodedCmdLine, Message,
ruleSetType_s = column_ifexists("ruleSetType_s", tostring(AdditionalFields.ruleSetType)), OperationName, SubscriptionId, details_message_s = column_ifexists("details_message_s", tostring(AdditionalFields.details_message)), details_msg_s = column_ifexists("details_msg_s", tostring(AdditionalFields.details_msg)), details_file_s = column_ifexists("details_message_s", tostring(AdditionalFields.details_file))
| extend timestamp = TimeGenerated
entityMappings:
- entityType: IP
fieldMappings:
- identifier: Address
columnName: MaliciousHost
version: 1.0.6
kind: Scheduled
Stages and Predicates
Parameters
let log4jioc = dynamic(["jndi","ldap","${::"]);
Stage 1: source
AzureDiagnostics
Stage 2: where
| where ResourceProvider == "MICROSOFT.NETWORK" and Category in ("ApplicationGatewayFirewallLog", "FrontdoorWebApplicationFirewallLog")
Stage 3: extend (3 consecutive steps)
| extend details_data_s = column_ifexists("details_data_s", tostring(AdditionalFields.details_data))
| extend details_msg_s = column_ifexists("details_msg_s", tostring(AdditionalFields.details_msg))
| extend details_message_s = column_ifexists("details_message_s", tostring(AdditionalFields.details_message))
Stage 4: where
| where requestUri_s has_any (log4jioc) or details_message_s has_any (log4jioc) or details_msg_s has_any (log4jioc) or details_data_s has_any (log4jioc)
Stage 5: extend
| extend Malicious = iff(isnotempty( details_data_s),details_data_s,iff(isnotempty( requestUri_s),requestUri_s,""))
Malicious =if
isnotempty(details_data_s)details_data_selse
iff(isnotempty(requestUri_s), requestUri_s, "")Stage 6: parse
| parse Malicious with * '${' MaliciousCommand '}' *
Stage 7: extend (5 consecutive steps)
| extend EncodeCmd = iff(MaliciousCommand has 'Base64/', split(split(MaliciousCommand, "Base64/",1)[0], "}", 0)[0], "")
| extend EncodeCmd1 = iff(MaliciousCommand has 'base64/', split(split(MaliciousCommand, "base64/",1)[0], "}", 0)[0], "")
| extend CmdLine = iff( isnotempty(EncodeCmd), EncodeCmd, EncodeCmd1)
| extend DecodedCmdLine = base64_decode_tostring(tostring(CmdLine))
| extend DecodedCmdLine = iff( isnotempty(DecodedCmdLine), DecodedCmdLine, "Unable to decode/Doesn't need decoding")
EncodeCmd =if
MaliciousCommand has "Base64/"split(split(MaliciousCommand, "Base64/", 1)[0], "}", 0)[0]else
""Stage 8: project
| project TimeGenerated, Target=column_ifexists("hostname_s", tostring(AdditionalFields.hostname)), MaliciousHost = column_ifexists("clientIp_s", tostring(AdditionalFields.clientIp)) , MaliciousCommand, details_data_s = column_ifexists("details_data_s", tostring(AdditionalFields.details_data)), DecodedCmdLine, Message,
ruleSetType_s = column_ifexists("ruleSetType_s", tostring(AdditionalFields.ruleSetType)), OperationName, SubscriptionId, details_message_s = column_ifexists("details_message_s", tostring(AdditionalFields.details_message)), details_msg_s = column_ifexists("details_msg_s", tostring(AdditionalFields.details_msg)), details_file_s = column_ifexists("details_message_s", tostring(AdditionalFields.details_file))
Stage 9: extend
| extend timestamp = TimeGenerated
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
Category | in |
| field:"Category" kind:in |
ResourceProvider | eq |
| field:"ResourceProvider" kind:eq value:"MICROSOFT.NETWORK" |
details_data_s | match |
| field:"details_data_s" kind:match |
details_message_s | match |
| field:"details_message_s" kind:match |
details_msg_s | match |
| field:"details_msg_s" kind:match |
requestUri_s | match |
| field:"requestUri_s" kind:match |
Output fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
DecodedCmdLine | project |
MaliciousCommand | project |
MaliciousHost | project |
Message | project |
OperationName | project |
SubscriptionId | project |
Target | project |
TimeGenerated | project |
details_data_s | project |
details_file_s | project |
details_message_s | project |
details_msg_s | project |
ruleSetType_s | project |
timestamp | extend |