Skip to content

Instantly share code, notes, and snippets.

@TheCloudScout
Last active December 13, 2021 14:15
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save TheCloudScout/9919554171f8f1498ce7f165882cc624 to your computer and use it in GitHub Desktop.
Save TheCloudScout/9919554171f8f1498ce7f165882cc624 to your computer and use it in GitHub Desktop.
Azure Firewall | Check log4j IoCs
let timeWindow = datetime("2021-12-09");
let intel = ((externaldata (URL:string,values: dynamic) [@"https://gist.githubusercontent.com/superducktoes/9b742f7b44c71b4a0d19790228ce85d8/raw/f1751cc072bd33256c0005021f33348c22aa76fc/Callback%2520Domains%2520log4j"]));
let IPList = externaldata(IPAddress:string)[@"https://raw.githubusercontent.com/Azure/Azure-Sentinel/master/Sample%20Data/Feeds/Log4j_IOC_List.csv"] with (format="csv", ignoreFirstRecord=True);
let domains = intel | extend domains=extract(@"([0-9a-z]+\.[0-9a-z]+\.[a-z]+)",0,URL)| distinct domains| where domains != "" | project domains;
let ips = intel | extend ips=extract(@"([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})",0,URL) | distinct ips | where ips != "" | project ips;
let ports = intel | extend ports=extract(@":([0-9]{2,5})\/",1,URL) | distinct ports | where ports != "" | project ports;
// to parse Azure Firewall logs with "AzureFirewallApplicationRule" category, we need several different parsing methods based on the type of entries
let applicationRuleAllowsHttp = AzureDiagnostics
| where ResourceType == "AZUREFIREWALLS"
| where Category == "AzureFirewallApplicationRule"
| parse msg_s with * " request from " ip_source ":" port_source " to " ip_destination ":" port_destination ". Url: " url ". Action: " action ". Policy: " policy ". Rule Collection Group: " rulecollectiongroup "Rule Collection: " rulecollection ". Rule: " rule
| where action == "Allow";
let applicationRuleAllowsHttps = AzureDiagnostics
| where ResourceType == "AZUREFIREWALLS"
| where Category == "AzureFirewallApplicationRule"
| parse msg_s with * " request from " ip_source ":" port_source " to " ip_destination ":" port_destination ". Action: " action ". Policy: " policy ". Rule Collection Group: " rulecollectiongroup "Rule Collection: " rulecollection ". Rule: " rule
| where action == "Allow" and port_destination !hasprefix "80" and rule !has "Web Category";
let applicationRuleAllowsHttpsWebCategory = AzureDiagnostics
| where ResourceType == "AZUREFIREWALLS"
| where Category == "AzureFirewallApplicationRule"
| parse msg_s with * " request from " ip_source ":" port_source " to " ip_destination ":" port_destination ". Action: " action ". Policy: " policy ". Rule Collection Group: " rulecollectiongroup "Rule Collection: " rulecollection ". Rule: " rule ". Web Category: " webcategory
| where action == "Allow";
let applicationRuleDeniesHttp = AzureDiagnostics
| where ResourceType == "AZUREFIREWALLS"
| where Category == "AzureFirewallApplicationRule"
| parse msg_s with * " request from " ip_source ":" port_source " to " ip_destination ":" port_destination ". Url: " url ". Action: " action ". " rule ". " *
| where action == "Deny" and port_destination == 80;
let applicationRuleDeniesHttps = AzureDiagnostics
| where ResourceType == "AZUREFIREWALLS"
| where Category == "AzureFirewallApplicationRule"
| parse msg_s with * " request from " ip_source ":" port_source " to " ip_destination ":" port_destination ". Action: " action ". " rule ". " *
| where action == "Deny" and port_destination !hasprefix "80";
let applicationRuleDenies = AzureDiagnostics
| where ResourceType == "AZUREFIREWALLS"
| where Category == "AzureFirewallApplicationRule"
| parse msg_s with * " request from " ip_source ":" port_source ". Action: " action ". Reason: " rule "." *
| where isnotempty(ip_source);
let azureFirewallApplicationRules = applicationRuleAllowsHttp
| union applicationRuleAllowsHttps, applicationRuleAllowsHttpsWebCategory, applicationRuleDeniesHttp, applicationRuleDeniesHttps, applicationRuleDenies
| project-away msg_s;
// Only TCP traffic is needed so we only parse those
let azureFirewallNetworkRules = AzureDiagnostics
| where ResourceType == "AZUREFIREWALLS"
| where Category == "AzureFirewallNetworkRule"
| extend protocol = case( msg_s hasprefix 'TCP', 'TCP',
msg_s hasprefix 'UDP', 'UDP',
msg_s hasprefix 'ICMP', 'ICMP',
'Unknown') // else
| where protocol == "TCP"
| parse msg_s with * " request from " ip_source ":" port_source " to " ip_destination ":" port_destination ". Action: " action "." *
| project-away msg_s;
// Azure Firewall DNS proxy events are quite straightforward
let azureFirewallDnsProxy = AzureDiagnostics
| where ResourceType == "AZUREFIREWALLS"
| where Category == "AzureFirewallDnsProxy"
| parse msg_s with * "DNS Request: " clientip ":" clientport " - " queryid " " recordtype " " requestclass " " lookupname ". " protocol " " requestsizebytes " " dnssecflag " "buffersize " " response " " responseflags " "responsesize " " responseduration
| project-away msg_s;
// Query start
azureFirewallDnsProxy
| where TimeGenerated > timeWindow
| where lookupname has_any (domains)
// combine alle three log categories in a single view
| union (azureFirewallNetworkRules
| where TimeGenerated > timeWindow
| where ip_destination has_any (ips) or
ip_destination has_any (IPList)),
(azureFirewallApplicationRules
| where TimeGenerated > timeWindow
| where ip_destination has_any (ips) or
ip_destination has_any (IPList) or
ip_destination has_any (domains))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment