From 5d16dd7dd380cf9a4988c8d7f31b09d08ec863ec Mon Sep 17 00:00:00 2001 From: Saleem Bseeu Date: Mon, 23 Feb 2026 13:29:15 +0100 Subject: [PATCH] Add Distributed Low Rate DDoS (Carpet Bombing) detection for Azure Firewall MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This detection identifies distributed low-rate DDoS attacks targeting resources protected by Azure Firewall. These attacks evade traditional detection by distributing traffic across many source IPs, each sending low-volume traffic while the aggregate degrades availability. Detection triggers when: - 50+ unique source IPs target the same destination - Each source sends ≤10 events (low-rate per attacker) - 2+ distinct protocols are used (TCP/UDP/ICMP rotation) - No single protocol exceeds 80% of traffic - 500+ total aggregate events - 3+ distinct source countries (geo-distribution) Includes: - ARM template for Classic AzureDiagnostics schema - ARM template for Resource-Specific tables (AZFWNetworkRule/AZFWApplicationRule) - Comprehensive README with tuning guide and deployment instructions --- .../DistributedLowRateDDoS_Classic.json | 119 ++++++++++ ...stributedLowRateDDoS_ResourceSpecific.json | 119 ++++++++++ .../README.md | 204 ++++++++++++++++++ 3 files changed, 442 insertions(+) create mode 100644 Azure Firewall/Alerts - Queries and Alerts/Alert - Firewall Distributed Low Rate DDoS/DistributedLowRateDDoS_Classic.json create mode 100644 Azure Firewall/Alerts - Queries and Alerts/Alert - Firewall Distributed Low Rate DDoS/DistributedLowRateDDoS_ResourceSpecific.json create mode 100644 Azure Firewall/Alerts - Queries and Alerts/Alert - Firewall Distributed Low Rate DDoS/README.md diff --git a/Azure Firewall/Alerts - Queries and Alerts/Alert - Firewall Distributed Low Rate DDoS/DistributedLowRateDDoS_Classic.json b/Azure Firewall/Alerts - Queries and Alerts/Alert - Firewall Distributed Low Rate DDoS/DistributedLowRateDDoS_Classic.json new file mode 100644 index 00000000..96a015bb --- /dev/null +++ b/Azure Firewall/Alerts - Queries and Alerts/Alert - Firewall Distributed Low Rate DDoS/DistributedLowRateDDoS_Classic.json @@ -0,0 +1,119 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "workspaceName": { + "type": "string", + "metadata": { + "Description": "Name of the Log Analytics workspace" + } + }, + "TimeWindowMinutes": { + "type": "string", + "defaultValue": "5", + "metadata": { + "Description": "Analysis time window in minutes. Shorter windows detect faster attacks, longer windows catch slower carpet bombing." + } + }, + "MinDistinctSources": { + "type": "string", + "defaultValue": "50", + "metadata": { + "Description": "Minimum number of unique source IPs required to trigger alert. Higher values reduce false positives but may miss smaller botnets." + } + }, + "MaxEventsPerSource": { + "type": "string", + "defaultValue": "10", + "metadata": { + "Description": "Maximum events allowed per source IP to qualify as 'low rate'. Sources exceeding this are not considered low-rate attackers." + } + }, + "MinProtocols": { + "type": "string", + "defaultValue": "2", + "metadata": { + "Description": "Minimum number of distinct protocols (TCP/UDP/ICMP) required. Protocol diversity indicates coordinated multi-vector attack." + } + }, + "MaxProtocolSharePct": { + "type": "string", + "defaultValue": "80", + "metadata": { + "Description": "Maximum percentage any single protocol can represent. True rotation means no protocol dominates (e.g., not >80% TCP)." + } + }, + "MinTotalEvents": { + "type": "string", + "defaultValue": "500", + "metadata": { + "Description": "Minimum aggregate events required. Ensures the attack has enough volume to potentially impact the target." + } + }, + "MinDistinctCountries": { + "type": "string", + "defaultValue": "3", + "metadata": { + "Description": "Minimum number of distinct source countries. Higher geo-diversity indicates distributed botnet vs regional traffic." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Location must match the location of the workspace - Do not edit this parameter." + } + } + }, + "variables": { + "alertLocation": "[parameters('location')]", + "alertName": "Distributed Low Rate DDoS - Carpet Bombing Detection", + "alertDescription": "Detects distributed low-rate DDoS attacks (carpet bombing) where many source IPs each send low-rate traffic to the same destination, evading per-IP thresholds while the aggregate impacts availability.\n\nDetection Indicators:\n- Many unique source IPs targeting same destination\n- Each source sends low-rate traffic (below individual thresholds)\n- Multiple protocols used (TCP/UDP/ICMP rotation)\n- No single protocol dominates (true rotation vs incidental)\n- Aggregate volume sufficient to cause impact\n- Geographic diversity of sources\n\nConfigurable Parameters:\n- Time window: Analysis period (default 5 minutes)\n- Min distinct sources: Minimum unique IPs (default 50)\n- Max events per source: Low-rate threshold (default 10)\n- Min protocols: Protocol diversity (default 2)\n- Max protocol share: Max single protocol % (default 80%)\n- Min total events: Aggregate threshold (default 500)\n- Min countries: Geo-diversity threshold (default 3)", + "alertStatus": "true", + "alertSource": { + "Query": "[concat('let TimeWindow = ', parameters('TimeWindowMinutes'), 'm; let MinDistinctSources = ', parameters('MinDistinctSources'), '; let MaxEventsPerSource = ', parameters('MaxEventsPerSource'), '; let MinProtocols = ', parameters('MinProtocols'), '; let MaxProtocolSharePct = ', parameters('MaxProtocolSharePct'), '.0; let MinTotalEvents = ', parameters('MinTotalEvents'), '; let MinDistinctCountries = ', parameters('MinDistinctCountries'), '; let StartTime = ago(TimeWindow); let TrafficLogs = AzureDiagnostics | where TimeGenerated >= StartTime | where OperationName == \"AzureFirewallNetworkRuleLog\" or OperationName == \"AzureFirewallApplicationRuleLog\" | parse msg_s with * \"from \" srcip \":\" srcport \" to \" dstip \":\" dstport \". Action: \" action \".\" * | extend Protocol = case(msg_s has \"UDP\", \"UDP\", msg_s has \"TCP\", \"TCP\", msg_s has \"ICMP\", \"ICMP\", \"Other\") | extend SourceCountry = tostring(geo_info_from_ip_address(srcip).country) | where isnotempty(srcip) and isnotempty(dstip); let DestStats = TrafficLogs | summarize TotalEvents = count(), DistinctSources = dcount(srcip), DistinctProtocols = dcount(Protocol), DistinctCountries = dcount(SourceCountry), Protocols = make_set(Protocol), SampleSources = make_set(srcip, 10), Countries = make_set(SourceCountry, 20), AllowedCount = countif(action == \"Allow\"), DeniedCount = countif(action == \"Deny\") by dstip; let MaxPerSource = TrafficLogs | summarize EventsFromSource = count() by dstip, srcip | summarize MaxEventsFromSingleSource = max(EventsFromSource) by dstip; let ProtocolDominance = TrafficLogs | summarize EventsByProtocol = count() by dstip, Protocol | summarize TotalEventsProto = sum(EventsByProtocol), MaxProtocolEvents = max(EventsByProtocol) by dstip | extend MaxProtocolPct = round(100.0 * MaxProtocolEvents / TotalEventsProto, 2); DestStats | join kind=inner (MaxPerSource) on dstip | join kind=inner (ProtocolDominance) on dstip | where TotalEvents >= MinTotalEvents | where DistinctSources >= MinDistinctSources | where MaxEventsFromSingleSource <= MaxEventsPerSource | where DistinctProtocols >= MinProtocols | where MaxProtocolPct <= MaxProtocolSharePct | where DistinctCountries >= MinDistinctCountries | project TargetDestination = dstip, TotalEvents, UniqueSourceIPs = DistinctSources, MaxEventsFromSingleSource, ProtocolsObserved = Protocols, DistinctProtocols, MaxProtocolDominancePct = MaxProtocolPct, UniqueCountries = DistinctCountries, CountriesList = Countries, SampleSourceIPs = SampleSources, AllowedTraffic = AllowedCount, DeniedTraffic = DeniedCount | extend AlertSeverity = case(TotalEvents > 5000 and UniqueSourceIPs > 200, \"High\", TotalEvents > 2000 and UniqueSourceIPs > 100, \"Medium\", \"Low\") | extend AlertDescription = strcat(\"Potential distributed low-rate DDoS (carpet bombing) detected targeting \", TargetDestination, \". \", UniqueSourceIPs, \" unique source IPs from \", UniqueCountries, \" countries generated \", TotalEvents, \" events. Max events per source: \", MaxEventsFromSingleSource, \". Protocols: \", tostring(ProtocolsObserved), \" (max dominance: \", MaxProtocolDominancePct, \"%). Allowed: \", AllowedTraffic, \", Denied: \", DeniedTraffic)')]", + "SourceId": "[resourceId('Microsoft.OperationalInsights/workspaces',parameters('workspaceName'))]", + "Type": "ResultCount" + }, + "alertSchedule": { + "Frequency": 5, + "Time": 60 + }, + "alertActions": { + "SeverityLevel": "2" + }, + "alertTrigger": { + "Operator": "GreaterThan", + "Threshold": "0" + } + }, + "resources": [ + { + "name": "[variables('alertName')]", + "type": "Microsoft.Insights/scheduledQueryRules", + "apiVersion": "2018-04-16", + "location": "[variables('alertLocation')]", + "properties": { + "description": "[variables('alertDescription')]", + "enabled": "[variables('alertStatus')]", + "source": { + "query": "[variables('alertSource').Query]", + "dataSourceId": "[variables('alertSource').SourceId]", + "queryType": "[variables('alertSource').Type]" + }, + "schedule": { + "frequencyInMinutes": "[variables('alertSchedule').Frequency]", + "timeWindowInMinutes": "[variables('alertSchedule').Time]" + }, + "action": { + "odata.type": "Microsoft.WindowsAzure.Management.Monitoring.Alerts.Models.Microsoft.AppInsights.Nexus.DataContracts.Resources.ScheduledQueryRules.AlertingAction", + "severity": "[variables('alertActions').SeverityLevel]", + "trigger": { + "thresholdOperator": "[variables('alertTrigger').Operator]", + "threshold": "[variables('alertTrigger').Threshold]" + } + } + } + } + ] +} diff --git a/Azure Firewall/Alerts - Queries and Alerts/Alert - Firewall Distributed Low Rate DDoS/DistributedLowRateDDoS_ResourceSpecific.json b/Azure Firewall/Alerts - Queries and Alerts/Alert - Firewall Distributed Low Rate DDoS/DistributedLowRateDDoS_ResourceSpecific.json new file mode 100644 index 00000000..1c133e5a --- /dev/null +++ b/Azure Firewall/Alerts - Queries and Alerts/Alert - Firewall Distributed Low Rate DDoS/DistributedLowRateDDoS_ResourceSpecific.json @@ -0,0 +1,119 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "workspaceName": { + "type": "string", + "metadata": { + "Description": "Name of the Log Analytics workspace" + } + }, + "TimeWindowMinutes": { + "type": "string", + "defaultValue": "5", + "metadata": { + "Description": "Analysis time window in minutes. Shorter windows detect faster attacks, longer windows catch slower carpet bombing." + } + }, + "MinDistinctSources": { + "type": "string", + "defaultValue": "50", + "metadata": { + "Description": "Minimum number of unique source IPs required to trigger alert. Higher values reduce false positives but may miss smaller botnets." + } + }, + "MaxEventsPerSource": { + "type": "string", + "defaultValue": "10", + "metadata": { + "Description": "Maximum events allowed per source IP to qualify as 'low rate'. Sources exceeding this are not considered low-rate attackers." + } + }, + "MinProtocols": { + "type": "string", + "defaultValue": "2", + "metadata": { + "Description": "Minimum number of distinct protocols (TCP/UDP/ICMP) required. Protocol diversity indicates coordinated multi-vector attack." + } + }, + "MaxProtocolSharePct": { + "type": "string", + "defaultValue": "80", + "metadata": { + "Description": "Maximum percentage any single protocol can represent. True rotation means no protocol dominates (e.g., not >80% TCP)." + } + }, + "MinTotalEvents": { + "type": "string", + "defaultValue": "500", + "metadata": { + "Description": "Minimum aggregate events required. Ensures the attack has enough volume to potentially impact the target." + } + }, + "MinDistinctCountries": { + "type": "string", + "defaultValue": "3", + "metadata": { + "Description": "Minimum number of distinct source countries. Higher geo-diversity indicates distributed botnet vs regional traffic." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "Location must match the location of the workspace - Do not edit this parameter." + } + } + }, + "variables": { + "alertLocation": "[parameters('location')]", + "alertName": "Distributed Low Rate DDoS - Carpet Bombing Detection (Resource-Specific)", + "alertDescription": "Detects distributed low-rate DDoS attacks (carpet bombing) where many source IPs each send low-rate traffic to the same destination, evading per-IP thresholds while the aggregate impacts availability.\n\nThis version uses Resource-Specific tables (AZFWNetworkRule, AZFWApplicationRule) for better query performance.\n\nDetection Indicators:\n- Many unique source IPs targeting same destination\n- Each source sends low-rate traffic (below individual thresholds)\n- Multiple protocols used (TCP/UDP/ICMP rotation)\n- No single protocol dominates (true rotation vs incidental)\n- Aggregate volume sufficient to cause impact\n- Geographic diversity of sources\n\nConfigurable Parameters:\n- Time window: Analysis period (default 5 minutes)\n- Min distinct sources: Minimum unique IPs (default 50)\n- Max events per source: Low-rate threshold (default 10)\n- Min protocols: Protocol diversity (default 2)\n- Max protocol share: Max single protocol % (default 80%)\n- Min total events: Aggregate threshold (default 500)\n- Min countries: Geo-diversity threshold (default 3)", + "alertStatus": "true", + "alertSource": { + "Query": "[concat('let TimeWindow = ', parameters('TimeWindowMinutes'), 'm; let MinDistinctSources = ', parameters('MinDistinctSources'), '; let MaxEventsPerSource = ', parameters('MaxEventsPerSource'), '; let MinProtocols = ', parameters('MinProtocols'), '; let MaxProtocolSharePct = ', parameters('MaxProtocolSharePct'), '.0; let MinTotalEvents = ', parameters('MinTotalEvents'), '; let MinDistinctCountries = ', parameters('MinDistinctCountries'), '; let StartTime = ago(TimeWindow); let NetworkRuleLogs = AZFWNetworkRule | where TimeGenerated >= StartTime | project TimeGenerated, SourceIp, DestinationIp, Protocol, Action, DestinationPort | extend LogType = \"NetworkRule\"; let AppRuleLogs = AZFWApplicationRule | where TimeGenerated >= StartTime | project TimeGenerated, SourceIp, DestinationIp = Fqdn, Protocol, Action, DestinationPort | extend LogType = \"ApplicationRule\"; let TrafficLogs = union NetworkRuleLogs, AppRuleLogs | extend SourceCountry = tostring(geo_info_from_ip_address(SourceIp).country) | where isnotempty(SourceIp) and isnotempty(DestinationIp); let DestStats = TrafficLogs | summarize TotalEvents = count(), DistinctSources = dcount(SourceIp), DistinctProtocols = dcount(Protocol), DistinctCountries = dcount(SourceCountry), Protocols = make_set(Protocol), SampleSources = make_set(SourceIp, 10), Countries = make_set(SourceCountry, 20), AllowedCount = countif(Action == \"Allow\"), DeniedCount = countif(Action == \"Deny\") by DestinationIp; let MaxPerSource = TrafficLogs | summarize EventsFromSource = count() by DestinationIp, SourceIp | summarize MaxEventsFromSingleSource = max(EventsFromSource) by DestinationIp; let ProtocolDominance = TrafficLogs | summarize EventsByProtocol = count() by DestinationIp, Protocol | summarize TotalEventsProto = sum(EventsByProtocol), MaxProtocolEvents = max(EventsByProtocol) by DestinationIp | extend MaxProtocolPct = round(100.0 * MaxProtocolEvents / TotalEventsProto, 2); DestStats | join kind=inner (MaxPerSource) on DestinationIp | join kind=inner (ProtocolDominance) on DestinationIp | where TotalEvents >= MinTotalEvents | where DistinctSources >= MinDistinctSources | where MaxEventsFromSingleSource <= MaxEventsPerSource | where DistinctProtocols >= MinProtocols | where MaxProtocolPct <= MaxProtocolSharePct | where DistinctCountries >= MinDistinctCountries | project TargetDestination = DestinationIp, TotalEvents, UniqueSourceIPs = DistinctSources, MaxEventsFromSingleSource, ProtocolsObserved = Protocols, DistinctProtocols, MaxProtocolDominancePct = MaxProtocolPct, UniqueCountries = DistinctCountries, CountriesList = Countries, SampleSourceIPs = SampleSources, AllowedTraffic = AllowedCount, DeniedTraffic = DeniedCount | extend AlertSeverity = case(TotalEvents > 5000 and UniqueSourceIPs > 200, \"High\", TotalEvents > 2000 and UniqueSourceIPs > 100, \"Medium\", \"Low\") | extend AlertDescription = strcat(\"Potential distributed low-rate DDoS (carpet bombing) detected targeting \", TargetDestination, \". \", UniqueSourceIPs, \" unique source IPs from \", UniqueCountries, \" countries generated \", TotalEvents, \" events. Max events per source: \", MaxEventsFromSingleSource, \". Protocols: \", tostring(ProtocolsObserved), \" (max dominance: \", MaxProtocolDominancePct, \"%). Allowed: \", AllowedTraffic, \", Denied: \", DeniedTraffic)')]", + "SourceId": "[resourceId('Microsoft.OperationalInsights/workspaces',parameters('workspaceName'))]", + "Type": "ResultCount" + }, + "alertSchedule": { + "Frequency": 5, + "Time": 60 + }, + "alertActions": { + "SeverityLevel": "2" + }, + "alertTrigger": { + "Operator": "GreaterThan", + "Threshold": "0" + } + }, + "resources": [ + { + "name": "[variables('alertName')]", + "type": "Microsoft.Insights/scheduledQueryRules", + "apiVersion": "2018-04-16", + "location": "[variables('alertLocation')]", + "properties": { + "description": "[variables('alertDescription')]", + "enabled": "[variables('alertStatus')]", + "source": { + "query": "[variables('alertSource').Query]", + "dataSourceId": "[variables('alertSource').SourceId]", + "queryType": "[variables('alertSource').Type]" + }, + "schedule": { + "frequencyInMinutes": "[variables('alertSchedule').Frequency]", + "timeWindowInMinutes": "[variables('alertSchedule').Time]" + }, + "action": { + "odata.type": "Microsoft.WindowsAzure.Management.Monitoring.Alerts.Models.Microsoft.AppInsights.Nexus.DataContracts.Resources.ScheduledQueryRules.AlertingAction", + "severity": "[variables('alertActions').SeverityLevel]", + "trigger": { + "thresholdOperator": "[variables('alertTrigger').Operator]", + "threshold": "[variables('alertTrigger').Threshold]" + } + } + } + } + ] +} diff --git a/Azure Firewall/Alerts - Queries and Alerts/Alert - Firewall Distributed Low Rate DDoS/README.md b/Azure Firewall/Alerts - Queries and Alerts/Alert - Firewall Distributed Low Rate DDoS/README.md new file mode 100644 index 00000000..d9268507 --- /dev/null +++ b/Azure Firewall/Alerts - Queries and Alerts/Alert - Firewall Distributed Low Rate DDoS/README.md @@ -0,0 +1,204 @@ +# Alert: Distributed Low Rate DDoS - Carpet Bombing Detection + +## Overview + +This detection identifies **distributed low-rate DDoS attacks** (commonly known as "carpet bombing") targeting resources protected by Azure Firewall. These attacks are characterized by many source IPs each sending low-volume traffic to the same destination, deliberately staying below per-IP detection thresholds while the aggregate traffic degrades availability. + +## Why This Detection Matters + +Traditional DDoS detection focuses on volumetric spikes from single sources or obvious traffic anomalies. Carpet bombing attacks evade these defenses by: + +- **Distributing traffic** across hundreds or thousands of source IPs +- **Keeping per-source rates low** (each IP sends only a few requests) +- **Rotating protocols** (TCP, UDP, ICMP) to avoid protocol-specific signatures +- **Leveraging geo-distributed botnets** to appear as diverse legitimate traffic + +Even with Azure DDoS Protection enabled, these attacks can slip through because: +1. Individual source behavior appears "normal" +2. No single protocol shows anomalous volume +3. Rate limiting per-IP is ineffective when each IP is already low-rate + +## Detection Logic + +The alert triggers when ALL of the following conditions are met: + +| Condition | Default Value | Purpose | +|-----------|---------------|---------| +| Unique source IPs ≥ threshold | 50 | Ensures attack is distributed | +| Events per source ≤ threshold | 10 | Confirms "low rate" per source | +| Distinct protocols ≥ threshold | 2 | Detects protocol rotation | +| Max protocol share ≤ threshold | 80% | Ensures no single protocol dominates | +| Total events ≥ threshold | 500 | Confirms aggregate impact potential | +| Distinct countries ≥ threshold | 3 | Indicates geo-distributed botnet | + +## Alert Output + +When triggered, the alert provides: + +- **TargetDestination**: The IP/FQDN under attack +- **TotalEvents**: Aggregate traffic volume in the window +- **UniqueSourceIPs**: Count of distinct attacking IPs +- **MaxEventsFromSingleSource**: Confirms low-rate per source +- **ProtocolsObserved**: List of protocols used (TCP, UDP, ICMP, etc.) +- **MaxProtocolDominancePct**: Highest single protocol percentage +- **UniqueCountries**: Geographic diversity indicator +- **CountriesList**: Countries where traffic originated +- **SampleSourceIPs**: Sample of source IPs for investigation +- **AllowedTraffic / DeniedTraffic**: Traffic disposition breakdown +- **AlertSeverity**: Computed severity (High/Medium/Low) +- **AlertDescription**: Human-readable summary + +## Deployment + +### Prerequisites + +- Azure Firewall with diagnostics enabled +- Log Analytics workspace receiving firewall logs +- Choose the correct template based on your log configuration: + - **Classic (AzureDiagnostics)**: Most common, uses legacy diagnostic settings + - **Resource-Specific**: Newer tables (`AZFWNetworkRule`, `AZFWApplicationRule`) for better performance + +### Deploy via Azure Portal + +1. Navigate to **Custom deployment** in Azure Portal +2. Select **Build your own template in the editor** +3. Copy the contents of the appropriate JSON file +4. Click **Save**, then fill in parameters +5. Deploy to the same resource group as your Log Analytics workspace + +### Deploy via Azure CLI + +**Classic Schema:** +```bash +az deployment group create \ + --resource-group \ + --template-file DistributedLowRateDDoS_Classic.json \ + --parameters workspaceName= +``` + +**Resource-Specific Schema:** +```bash +az deployment group create \ + --resource-group \ + --template-file DistributedLowRateDDoS_ResourceSpecific.json \ + --parameters workspaceName= +``` + +### Deploy via PowerShell + +```powershell +New-AzResourceGroupDeployment ` + -ResourceGroupName "" ` + -TemplateFile "DistributedLowRateDDoS_Classic.json" ` + -workspaceName "" +``` + +## Tuning Guide + +### Conservative Tuning (Fewer Alerts, High Confidence) + +For environments with high legitimate traffic diversity: + +| Parameter | Conservative Value | +|-----------|-------------------| +| MinDistinctSources | 100 | +| MinTotalEvents | 1000 | +| MinDistinctCountries | 5 | +| MaxEventsPerSource | 5 | + +### Aggressive Tuning (More Coverage, More Alerts) + +For high-security environments or smaller deployments: + +| Parameter | Aggressive Value | +|-----------|-----------------| +| MinDistinctSources | 20 | +| MinTotalEvents | 200 | +| MinDistinctCountries | 2 | +| MaxEventsPerSource | 20 | + +### Recommended Baseline by Environment Type + +| Environment | MinDistinctSources | MinTotalEvents | TimeWindow | +|-------------|-------------------|----------------|------------| +| **Small/Medium business** | 30 | 300 | 5m | +| **Enterprise with diverse traffic** | 75 | 750 | 5m | +| **API/CDN backend** | 100 | 1500 | 10m | +| **High-security (accept more alerts)** | 20 | 200 | 3m | + +## Reducing False Positives + +Common false positive scenarios and mitigations: + +### 1. Legitimate API Traffic +**Symptom**: Many mobile clients accessing APIs +**Mitigation**: Increase `MinDistinctSources` and `MinTotalEvents`, or exclude known API endpoints + +### 2. CDN Traffic +**Symptom**: CDN edge servers appear as diverse sources +**Mitigation**: The protocol rotation filter should help (CDN traffic is typically HTTPS-dominant) + +### 3. Health Checks +**Symptom**: Load balancer or monitoring health checks +**Mitigation**: Low event counts per source typically exclude these, but increase `MinTotalEvents` if needed + +### 4. Marketing Campaign Spikes +**Symptom**: Legitimate traffic surge after campaign launch +**Mitigation**: The geo-diversity and protocol diversity requirements should filter these out + +## Known Limitations + +1. **Geo-IP accuracy**: The `geo_info_from_ip_address()` function may not resolve all IPs accurately, especially private ranges +2. **Log latency**: Azure Firewall logs may have ingestion delay; consider this when setting time windows +3. **Cost implications**: This query performs multiple joins and aggregations; monitor Log Analytics costs +4. **IPv6 support**: Detection works with IPv6 but geo-IP resolution may be less accurate + +## Testing the Detection + +To validate the detection without a real attack, you can use this test query (adjust thresholds temporarily): + +```kql +// Test query with lowered thresholds - DO NOT deploy in production +let TimeWindow = 60m; +let MinDistinctSources = 5; // Lowered for testing +let MaxEventsPerSource = 100; // Raised for testing +let MinProtocols = 1; // Lowered for testing +let MaxProtocolSharePct = 100.0; // Raised for testing +let MinTotalEvents = 50; // Lowered for testing +let MinDistinctCountries = 1; // Lowered for testing +// ... rest of query +``` + +## Integration with Response Workflows + +### Azure Sentinel / Microsoft Sentinel + +This alert can be promoted to a Sentinel Analytics Rule for enhanced investigation capabilities: + +1. Import the ARM template into Sentinel +2. Configure automated investigation playbooks +3. Correlate with other network security signals + +### Recommended Response Actions + +1. **Immediate**: Review sample source IPs for known bad actors +2. **Short-term**: Consider geographic blocking if sources are from unexpected regions +3. **Investigation**: Check if target destination is experiencing availability issues +4. **Long-term**: Feed source IPs into threat intelligence for future blocking + +## References + +- [Carpet Bombing DDoS Attacks - Vercara](https://www.vercara.com/) +- [Low-Rate DDoS Attack Techniques - NSFocus](https://www.nsfocusglobal.com/) +- [Azure Firewall Logging](https://docs.microsoft.com/azure/firewall/logs-and-metrics) +- [Azure DDoS Protection](https://docs.microsoft.com/azure/ddos-protection/ddos-protection-overview) + +## Version History + +| Version | Date | Changes | +|---------|------|---------| +| 1.0.0 | 2026-02-23 | Initial release | + +## Contributing + +This project welcomes contributions and suggestions. See the [CONTRIBUTING.md](../../../../CONTRIBUTING.md) file for details.