Adversaries may use Azure IP addresses to mask their origin, making it harder to trace their activity within Azure environments. SOC teams should proactively hunt for this behavior to identify potential lateral movement or command-and-control traffic disguised as legitimate Azure traffic.
KQL Query
let AzureSubnets = toscalar (
externaldata (xml:string)
[
@'https://download.microsoft.com/download/0/1/8/018E208D-54F8-44CD-AA26-CD7BC9524A8C/PublicIPs_20200824.xml'
]
with (format="txt")
| extend Subnet = tostring(parse_xml(xml).IpRange.['@Subnet'])
| where isnotempty(Subnet)
| summarize make_set(Subnet)
);
let IsItAzure = (SourceData:(RemoteIP:string)) {
SourceData
| extend AzureSubnet = AzureSubnets
| mv-expand AzureSubnet to typeof(string)
| extend IsAzure = ipv4_is_in_range(RemoteIP, AzureSubnet)
| summarize IsAzure = max(IsAzure) by RemoteIP
};
// BEGIN SAMPLE QUERY //
DeviceNetworkEvents
| take 10000
// END SAMPLE QUERY
| invoke IsItAzure()
id: a883cf6b-52dd-480a-8581-4e5774fc9002
name: Detect Azure RemoteIP
description: |
This query is a function that consumes the publicly available Azure IP address list and checks a list of remote IP addresses against it to see if they are Azure IP addresses or not.
To use this, replace the demo portion of the query (DeviceNetworkEvents | take 10000) with your query with the column name of the IP address to check named RemoteIP. The function will add a new column to the end called IsAzure denoting if the IP address range is in the published list or not.
Please note that over time the URL to the Azure IP address list may need to be updated.
requiredDataConnectors:
- connectorId: MicrosoftThreatProtection
dataTypes:
- DeviceNetworkEvents
query: |
let AzureSubnets = toscalar (
externaldata (xml:string)
[
@'https://download.microsoft.com/download/0/1/8/018E208D-54F8-44CD-AA26-CD7BC9524A8C/PublicIPs_20200824.xml'
]
with (format="txt")
| extend Subnet = tostring(parse_xml(xml).IpRange.['@Subnet'])
| where isnotempty(Subnet)
| summarize make_set(Subnet)
);
let IsItAzure = (SourceData:(RemoteIP:string)) {
SourceData
| extend AzureSubnet = AzureSubnets
| mv-expand AzureSubnet to typeof(string)
| extend IsAzure = ipv4_is_in_range(RemoteIP, AzureSubnet)
| summarize IsAzure = max(IsAzure) by RemoteIP
};
// BEGIN SAMPLE QUERY //
DeviceNetworkEvents
| take 10000
// END SAMPLE QUERY
| invoke IsItAzure()
| Sentinel Table | Notes |
|---|---|
DeviceNetworkEvents | Ensure this data connector is enabled |
Scenario: Azure VMs communicating with other Azure services
Description: Azure virtual machines (VMs) often communicate with other Azure services (e.g., Azure Storage, Azure SQL) using internal Azure IP ranges.
Filter/Exclusion: Exclude traffic originating from Azure VMs (e.g., using source_ip in the range 10.0.0.0/8, 172.16.0.0/12, or 192.168.0.0/16).
Example Filter: source_ip not in (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16)
Scenario: Azure DevOps pipeline execution
Description: Azure DevOps pipelines may use Azure IP addresses for internal communication during CI/CD processes.
Filter/Exclusion: Exclude traffic associated with Azure DevOps pipelines (e.g., using user_agent containing “Azure-DevOps” or checking for known pipeline IP ranges).
Example Filter: user_agent not contains "Azure-DevOps"
Scenario: Scheduled Azure Backup Jobs
Description: Azure Backup services may initiate connections from Azure IP addresses to back up on-premises or virtual machine data.
Filter/Exclusion: Exclude traffic related to Azure Backup (e.g., using destination_ip in the Azure Backup IP range or checking for backup-related service tags).
Example Filter: destination_ip not in (AzureBackupIPRange)
Scenario: Azure Active Directory (AAD) Sign-in Activity
Description: AAD sign-in activities may involve Azure IP addresses when users authenticate from within the Azure environment.
Filter/Exclusion: