Azure-Sentinel/Hunting Queries/MultipleDataSources/AzureResourceCreationWithNe...

105 строки
7.1 KiB
YAML
Исходник Обычный вид История

2019-10-29 20:40:17 +03:00
id: ac25d05d-362d-4a8d-b4e7-58c0edd2379c
name: Anomalous Resource Creation and related Network Activity
description: |
'Indicates when an anomalous number of resources are created successfully in Azure via the AzureActivity log.
This is then joined with the AzureNetworkAnalytics_CL data to identify any network related activity for the created resource.
The anomaly detection identifies activities that have occured both since the start of the day 1 day ago and the start of the day 7 days ago.
The start of the day is considered 12am UTC time.
2021-08-05 01:49:57 +03:00
Resource creation could indicated malicious or spurious use of your Azure Resource allocation. Resources can be abused in relation to digital
2019-10-29 20:40:17 +03:00
currency mining, command and control, exfiltration, distributed attacks and propagation of malware, among others. Verify that this resource creation
is expected.
2021-08-05 01:49:57 +03:00
Resources:
https://docs.microsoft.com/azure/azure-monitor/insights/azure-networking-analytics
https://docs.microsoft.com/azure/network-watcher/traffic-analytics-schema'
2019-10-29 20:40:17 +03:00
requiredDataConnectors:
- connectorId: AzureActivity
dataTypes:
- AzureActivity
- connectorId: AzureNetworkWatcher
dataTypes:
- AzureNetworkAnalytics_CL
tactics:
- Impact
relevantTechniques:
- T1496
query: |
2021-08-05 01:49:57 +03:00
let starttime = todatetime('{{StartTimeISO}}');
let endtime = todatetime('{{EndTimeISO}}');
let lookback = totimespan((endtime-starttime)*7);
2021-08-07 03:25:16 +03:00
let activity = AzureActivity
2021-08-05 01:49:57 +03:00
| where TimeGenerated >= startofday(ago(lookback))
2021-05-12 14:13:46 +03:00
// We look for any Operation that created and then succeeded where ActivitySubstatusValue has a value so that we can provide context
2021-09-18 04:08:34 +03:00
| where OperationNameValue endswith "write"
2021-05-12 14:13:46 +03:00
| where ActivityStatusValue has "Succeeded"
2021-09-18 04:08:34 +03:00
| make-series dResourceCount=dcount(ResourceId) default=0 on EventSubmissionTimestamp in range(startofday(ago(7d)), now(), 1d) by Caller, Resource, OperationNameValue
2019-10-29 20:40:17 +03:00
| extend (RSquare,Slope,Variance,RVariance,Interception,LineFit)=series_fit_line(dResourceCount)
// Comment slope reference below to see all returns
| where Slope > 0.2
| join kind=leftsemi (
// Last day's activity is anomalous
AzureActivity
2021-08-05 01:49:57 +03:00
| where TimeGenerated between(starttime..endtime)
2021-05-12 14:13:46 +03:00
// We look for any Operation that created and then succeeded where ActivitySubstatusValue has a value so that we can provide context
2021-09-18 04:08:34 +03:00
| where OperationNameValue endswith "write"
2021-05-12 14:13:46 +03:00
| where ActivityStatusValue has "Succeeded"
2021-09-18 04:08:34 +03:00
| make-series dResourceCount=dcount(ResourceId) default=0 on EventSubmissionTimestamp in range(startofday(ago(1d)), now(), 1d) by Caller, Resource, OperationNameValue
2019-10-29 20:40:17 +03:00
| extend (RSquare,Slope,Variance,RVariance,Interception,LineFit)=series_fit_line(dResourceCount)
// Comment slope reference below to see all returns
| where Slope > 0.2
2021-09-18 04:08:34 +03:00
) on Caller, Resource, OperationNameValue
2019-10-29 20:40:17 +03:00
// Expanding the fields that were grouped so we can match on a time window when we join the details later
| mvexpand EventSubmissionTimestamp, dResourceCount
// Making sure the fields are the right type or the join fails
| extend todatetime(EventSubmissionTimestamp), tostring(dResourceCount)
| join kind= inner (
AzureActivity
2021-08-05 01:49:57 +03:00
| where TimeGenerated between(starttime..endtime)
2021-05-12 14:13:46 +03:00
// We look for any Operation that created and then succeeded where ActivitySubstatusValue has a value so that we can provide context
2021-09-18 04:08:34 +03:00
| where OperationNameValue endswith "write"
2021-08-05 01:49:57 +03:00
| where ActivityStatusValue has "Succeeded" and isnotempty(ActivitySubstatusValue)
2021-09-18 04:08:34 +03:00
| summarize by EventSubmissionTimestamp = bin(EventSubmissionTimestamp, 1d), Caller, CallerIpAddress, OperationNameValue, ActivityStatusValue, Resource, ResourceGroup, ResourceId, SubscriptionId
) on EventSubmissionTimestamp, Caller, Resource, OperationNameValue;
2021-08-05 01:49:57 +03:00
let NetworkAnalytics =
2019-10-29 20:40:17 +03:00
union isfuzzy=true
(AzureNetworkAnalytics_CL
2021-08-05 01:49:57 +03:00
| where TimeGenerated between(starttime..endtime)
2019-10-29 20:40:17 +03:00
// Controlling for Schema Version and later parsing - This is Version 2 and Public IPs only
| where (isnotempty(FASchemaVersion_s) and isnotempty(DestPublicIPs_s))
| extend SchemaVersion = FASchemaVersion_s
| extend PublicIPs = tostring(split(DestPublicIPs_s,"|")[0])
2021-08-05 01:49:57 +03:00
| summarize StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated), FirstProcessedTimeUTC = min(FlowStartTime_t), LastProcessedTimeUtc = max(FlowEndTime_t),
Regions = makeset(Region_s), AzureRegions = makeset(AzureRegion_s), VMs = makeset(VM_s), MACAddresses = makeset(MACAddress_s), PublicIPs = makeset(PublicIPs), DestPort = makeset(DestPort_d), SrcIP = makeset(SrcIP_s),
2019-10-29 20:40:17 +03:00
ActivityCount = count() by NSGRule_s, NSGList_s, SubNet = Subnet1_s, FlowDirection_s, Subscription = Subscription1_g, Tags_s, SchemaVersion
//NSGList_s contains the subscription ID, remove that as we already have a field for this and now it will match what we get for SchemaVersion 1
| extend NSG = case(isnotempty(NSGList_s), strcat(split(NSGList_s, "/")[-2],"/",split(NSGList_s, "/")[-1]), "NotAvailable")
// Depending on the SchemaVersion, we will need to provide the NSG_Name for matching against the resource identified in AzureActivity
| extend NSG_Name = tostring(split(NSG, "/")[-1])
),
(
AzureNetworkAnalytics_CL
2021-08-05 01:49:57 +03:00
| where TimeGenerated between(starttime..endtime)
2019-10-29 20:40:17 +03:00
// Controlling for Schema Version and later parsing - This is Version 1
| where isempty(FASchemaVersion_s)
// Controlling for public IPs only
| where isnotempty(PublicFrontendIPs_s) or isnotempty(PublicIPAddresses_s)
| where PublicFrontendIPs_s != "null" or PublicIPAddresses_s != "null"
| extend SchemaVersion = SchemaVersion_s
// The Public IP can be indicated in one of 2 locations, assigning here for easy union results
| extend PublicIPs = case(isnotempty(PublicFrontendIPs_s), PublicFrontendIPs_s,
2021-08-05 01:49:57 +03:00
PublicIPAddresses_s)
| summarize StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated), FirstProcessedTimeUTC = min(TimeProcessed_t), LastProcessedTimeUtc = max(TimeProcessed_t),
Regions = makeset(Region_s), AzureRegions = makeset(DiscoveryRegion_s), VMs = makeset(VirtualMachine_s), MACAddresses = makeset(MACAddress_s), PublicIPs = makeset(PublicIPs),
2019-10-29 20:40:17 +03:00
SrcIP = makeset(PrivateIPAddresses_s), Name = makeset(Name_s), DestPort = makeset(DestinationPortRange_s),
ActivityCount = count() by NSG = NSG_s, SubNet = Subnetwork_s, Subscription = Subscription_g, Tags_s, SchemaVersion
// Some events don't have an NSG listed, populating so it is clear it is not available in th datatype
| extend NSG = case(isnotempty(NSG), NSG, "NotAvailable")
// Depending on the SchemaVersion, we will need to provide the NSG_Name for matching against the resource identified in AzureActivity
| extend NSG_Name = tostring(split(NSG, "/")[-1])
)
| project StartTimeUtc, EndTimeUtc, FirstProcessedTimeUTC, LastProcessedTimeUtc, PublicIPs, NSG, NSG_Name, SrcIP, DestPort, SubNet, Name, VMs, MACAddresses, ActivityCount, Regions, AzureRegions, Subscription, Tags_s, SchemaVersion
;
2021-08-07 03:25:16 +03:00
activity | join kind= leftouter (NetworkAnalytics
2019-10-29 20:40:17 +03:00
) on $left.Resource == $right.NSG_Name
2021-09-18 04:08:34 +03:00
| extend timestamp = StartTimeUtc, AccountCustomEntity = Caller, IPCustomEntity = CallerIpAddress