initial set of queries
This commit is contained in:
Родитель
00714818da
Коммит
8a1afd222a
|
@ -0,0 +1,47 @@
|
|||
id: acfdee3f-b794-404a-aeba-ef6a1fa08ad1
|
||||
name: Azure DevOps Agent Pool Created Then Deleted
|
||||
description: |
|
||||
'As well as adding build agents to an existing pool to execute malicious activity within a pipeline an attacker could create a complete new agent pool and use this for execution. Azure DevOps allows for the creation of agent pools with Azure hosted infrastructure or self-hosted infrastructure. Given the additional customizability of self-hosted agents this detection focuses on the creation of new self-hosted pools. To further reduce false positive rates the detection looks for pools created and deleted relatively quickly (within 7 days by default), as an attacker is likely to remove a malicious pool once used in order to reduce/remove evidence of their activity.'
|
||||
severity: High
|
||||
requiredDataConnectors: []
|
||||
queryFrequency: 7d
|
||||
queryPeriod: 30d
|
||||
triggerOperator: gt
|
||||
triggerThreshold: 0
|
||||
tactics:
|
||||
- DefenseEvasion
|
||||
relevantTechniques:
|
||||
- T1578.002
|
||||
query: |
|
||||
let lookback = 30d;
|
||||
let timewindow = 7d;
|
||||
AzureDevOpsAuditing
|
||||
| where TimeGenerated > ago(lookback)
|
||||
| where OperationName =~ "Library.AgentPoolCreated"
|
||||
| extend AgentCloudId = tostring(Data.AgentCloudId)
|
||||
| extend PoolType = iif(isnotempty(AgentCloudId), "Azure VMs", "Self Hosted")
|
||||
// Comment this line out to include cloud pools as well
|
||||
| where PoolType == "Self Hosted"
|
||||
| extend AgentPoolName = tostring(Data.AgentPoolName)
|
||||
| extend AgentPoolId = tostring(Data.AgentPoolId)
|
||||
| extend IsHosted = tostring(Data.IsHosted)
|
||||
| extend IsLegacy = tostring(Data.IsLegacy)
|
||||
| extend timekey = bin(TimeGenerated, timewindow)
|
||||
// Join only with pools deleted in the same window
|
||||
| join (AzureDevOpsAuditing
|
||||
| where TimeGenerated > ago(lookback)
|
||||
| where OperationName =~ "Library.AgentPoolDeleted"
|
||||
| extend AgentPoolName = tostring(Data.AgentPoolName)
|
||||
| extend AgentPoolId = tostring(Data.AgentPoolId)
|
||||
| extend timekey = bin(TimeGenerated, timewindow)) on AgentPoolId, timekey
|
||||
| project-reorder TimeGenerated, ActorUPN, UserAgent, IpAddress, AuthenticationMechanism, OperationName, AgentPoolName, IsHosted, IsLegacy, Data
|
||||
| extend timestamp = TimeGenerated, AccountCustomEntity = ActorUPN, IPCustomEntity = IpAddress
|
||||
entityMappings:
|
||||
- entityType: Account
|
||||
fieldMappings:
|
||||
- identifier: FullName
|
||||
columnName: ActorUPN
|
||||
- entityType: IP
|
||||
fieldMappings:
|
||||
- identifier: Address
|
||||
columnName: IpAddress
|
|
@ -0,0 +1,29 @@
|
|||
id: 4e8238bd-ff4f-4126-a9f6-09b3b6801b3d
|
||||
name: Azure DevOps Audit Stream Disabled
|
||||
description: |
|
||||
'Azure DevOps allow for audit logs to streamed to external storage solutions such as SIEM solutions. An attacker looking to hide malicious Azure DevOps activity from defenders may look to disable data streams before conducting activity and them re-enabling them after (so as not to raise data threshold-based alarms). Looking for disabled audit streams can identify this activity, and due to the nature of the action its unlikely to have a high false positive rate.'
|
||||
severity: High
|
||||
requiredDataConnectors: []
|
||||
queryFrequency: 1d
|
||||
queryPeriod: 1d
|
||||
triggerOperator: gt
|
||||
triggerThreshold: 0
|
||||
tactics:
|
||||
- DefenseEvasion
|
||||
relevantTechniques:
|
||||
- T1562.008
|
||||
query: |
|
||||
AzureDevOpsAuditing
|
||||
| where OperationName =~ "AuditLog.StreamDisabledByUser"
|
||||
| extend StreamType = tostring(Data.ConsumerType)
|
||||
| project-reorder TimeGenerated, Details, ActorUPN, IpAddress, UserAgent, StreamType
|
||||
| extend timestamp = TimeGenerated, AccountCustomEntity = ActorUPN, IPCustomEntity = IpAddress
|
||||
entityMappings:
|
||||
- entityType: Account
|
||||
fieldMappings:
|
||||
- identifier: FullName
|
||||
columnName: ActorUPN
|
||||
- entityType: IP
|
||||
fieldMappings:
|
||||
- identifier: Address
|
||||
columnName: IpAddress
|
|
@ -0,0 +1,56 @@
|
|||
id: 155e9134-d5ad-4a6f-88f3-99c220040b66
|
||||
name: Azure DevOps Pipleine modified by a New User.
|
||||
description: |
|
||||
'There are several potential pipeline steps that could be modified by an attacker to inject malicious code into the build cycle. A likely attacker path is the modification to an existing pipeline that they have access to. This detection looks for users modifying a pipeline when they have not previously been observed modifying or creating that pipeline before. This query also joins events with data to Azure AD Identity Protection (AAD IdP) in order to show if the user conducting the action has any associated AAD IdP alerts, you can also chose to filter this detection to only alert when the user also has AAD IdP alerts associated with them.'
|
||||
severity: Medium
|
||||
requiredDataConnectors: []
|
||||
queryFrequency: 1d
|
||||
queryPeriod: 30d
|
||||
triggerOperator: gt
|
||||
triggerThreshold: 0
|
||||
tactics:
|
||||
- Execution
|
||||
- DefenseEvasion
|
||||
relevantTechniques:
|
||||
- T1584.006
|
||||
- T1578
|
||||
query: |
|
||||
// Set the lookback to determine if user has created pipelines before
|
||||
let timeback = 30d;
|
||||
// Set the period for detections
|
||||
let timeframe = 1d;
|
||||
// Get a list of previous Release Pipeline creators to exclude
|
||||
let releaseusers = AzureDevOpsAuditing
|
||||
| where TimeGenerated > ago(timeback) and TimeGenerated < ago(timeframe)
|
||||
| where OperationName in ("Release.ReleasePipelineCreated", "Release.ReleasePipelineModified")
|
||||
// We want to look for users performing actions in specic projects so we creat this userscope object to match on
|
||||
| extend UserScope = strcat(ActorUserId, "-", ProjectName)
|
||||
| summarize by UserScope;
|
||||
// Get Release Pipeline creations by new users
|
||||
AzureDevOpsAuditing
|
||||
| where TimeGenerated > ago(timeframe)
|
||||
| where OperationName =~ "Release.ReleasePipelineModified"
|
||||
| extend UserScope = strcat(ActorUserId, "-", ProjectName)
|
||||
| where UserScope !in (releaseusers)
|
||||
| extend ActorUPN = tolower(ActorUPN)
|
||||
| project-away Id, ActivityId, ActorCUID, ScopeId, ProjectId, TenantId, SourceSystem, UserScope
|
||||
// See if any of these users have Azure AD alerts associated with them in the same timeframe
|
||||
| join kind = leftouter (
|
||||
SecurityAlert
|
||||
| where TimeGenerated > ago(timeframe)
|
||||
| where ProviderName == "IPC"
|
||||
| extend AadUserId = tostring(parse_json(Entities)[0].AadUserId)
|
||||
| summarize Alerts=count() by AadUserId) on $left.ActorUserId == $right.AadUserId
|
||||
| extend Alerts = iif(isnotempty(Alerts), Alerts, 0)
|
||||
// Uncomment the line below to only show results where the user as AADIdP alerts
|
||||
//| where Alerts > 0
|
||||
| extend timestamp = TimeGenerated, AccountCustomEntity = ActorUPN, IPCustomEntity = IpAddress
|
||||
entityMappings:
|
||||
- entityType: Account
|
||||
fieldMappings:
|
||||
- identifier: FullName
|
||||
columnName: ActorUPN
|
||||
- entityType: IP
|
||||
fieldMappings:
|
||||
- identifier: Address
|
||||
columnName: IpAddress
|
|
@ -0,0 +1,44 @@
|
|||
id: 3b9a44d7-c651-45ed-816c-eae583a6f2f1
|
||||
name: ADO Build Variable Modified by New User.
|
||||
description: |
|
||||
'Variables can be configured and used at any stage of the build process in Azure DevOps to inject values. An attacker with the required permissions could modify or add to these variables to conduct malicious activity such as changing paths or remote endpoints called during the build. As variables are often changed by users just detecting these changes would have a high false positive rate. This detection looks for modifications to variable groups where that user has not been observed modifying them before.'
|
||||
severity: Medium
|
||||
requiredDataConnectors: []
|
||||
queryFrequency: 1d
|
||||
queryPeriod: 30d
|
||||
triggerOperator: gt
|
||||
triggerThreshold: 0
|
||||
tactics:
|
||||
- DefenseEvasion
|
||||
relevantTechniques:
|
||||
- T1578
|
||||
query: |
|
||||
let lookback = 30d;
|
||||
let timeframe = 1d;
|
||||
historical_data =
|
||||
AzureDevOpsAuditing
|
||||
| where TimeGenerated > ago(lookback) and TimeGenerated < ago(timeframe)
|
||||
| where OperationName =~ "Library.VariableGroupModified"
|
||||
| extend variables = Data.Variables
|
||||
| extend VariableGroupId = tostring(Data.VariableGroupId)
|
||||
| extend UserKey = strcat(VariableGroupId, "-", ActorUserId)
|
||||
| project UserKey;
|
||||
AzureDevOpsAuditing
|
||||
| where TimeGenerated > ago(timeframe)
|
||||
| where OperationName =~ "Library.VariableGroupModified"
|
||||
| extend VariableGroupName = tostring(Data.VariableGroupName)
|
||||
| extend VariableGroupId = tostring(Data.VariableGroupId)
|
||||
| extend UserKey = strcat(VariableGroupId, "-", ActorUserId)
|
||||
| where UserKey !in (historical_data)
|
||||
| project-away UserKey
|
||||
| project-reorder TimeGenerated, VariableGroupName, ActorUPN, IpAddress, UserAgent
|
||||
| extend timestamp = TimeGenerated, AccountCustomEntity = ActorUPN, IPCustomEntity = IpAddress
|
||||
entityMappings:
|
||||
- entityType: Account
|
||||
fieldMappings:
|
||||
- identifier: FullName
|
||||
columnName: ActorUPN
|
||||
- entityType: IP
|
||||
fieldMappings:
|
||||
- identifier: Address
|
||||
columnName: IpAddress
|
|
@ -0,0 +1,58 @@
|
|||
id: 17f23fbe-bb73-4324-8ecf-a18545a5dc26
|
||||
name: Azure DevOps Pipeline Created and Deleted on the Same Day
|
||||
description: |
|
||||
'An attacker with access to Azure DevOps could create a pipeline to inject artifacts used by other pipelines,
|
||||
or to create a malicious software build that looks legitimate by using a pipeline that incorporates legitimate elements.
|
||||
An attacker would also likely want to cover their tracks once conducting such activity. This query looks for Pipelines
|
||||
created and deleted within the same day, this is unlikely to be legitimate user activity in the majority of cases.'
|
||||
severity: Medium
|
||||
requiredDataConnectors: []
|
||||
queryFrequency: 3d
|
||||
queryPeriod: 3d
|
||||
triggerOperator: gt
|
||||
triggerThreshold: 0
|
||||
tactics:
|
||||
- Execution
|
||||
relevantTechniques:
|
||||
- T1072
|
||||
query: |
|
||||
let timeframe = 3d;
|
||||
// Get Release Pipeline Creation Events and group by day
|
||||
AzureDevOpsAuditing
|
||||
| where TimeGenerated > ago(timeframe)
|
||||
| where OperationName =~ "Release.ReleasePipelineCreated"
|
||||
// Group by day
|
||||
| extend timekey = bin(TimeGenerated, 1d)
|
||||
| extend PipelineId = tostring(Data.PipelineId)
|
||||
| extend PipelineName = tostring(Data.PipelineName)
|
||||
// Rename some columns to make output clearer
|
||||
| project-rename TimeCreated = TimeGenerated, CreatingUser = ActorUPN, CreatingUserAgent = UserAgent, CreatingIP = IpAddress
|
||||
// Join with Release Pipeline Deletions where Pipeline ID is the same and deletion occurred on same day as creation
|
||||
| join (AzureDevOpsAuditing
|
||||
| where TimeGenerated > ago(timeframe)
|
||||
| where OperationName =~ "Release.ReleasePipelineDeleted"
|
||||
// Group by day
|
||||
| extend timekey = bin(TimeGenerated, 1d)
|
||||
| extend PipelineId = tostring(Data.PipelineId)
|
||||
| extend PipelineName = tostring(Data.PipelineName)
|
||||
// Rename some things to make the output clearer
|
||||
| project-rename TimeDeleted = TimeGenerated, DeletingUser = ActorUPN, DeletingUserAgent = UserAgent, DeletingIP = IpAddress) on PipelineId, timekey
|
||||
| project TimeCreated, TimeDeleted, PipelineName, PipelineId, CreatingUser, CreatingIP, CreatingUserAgent, DeletingUser, DeletingIP, DeletingUserAgent, ScopeDisplayName, ProjectName, Data, OperationName, OperationName1
|
||||
| extend timestamp = TimeGenerated, AccountCustomEntity = CreatingUser, IPCustomEntity = CreatingIP
|
||||
entityMappings:
|
||||
- entityType: Account
|
||||
fieldMappings:
|
||||
- identifier: FullName
|
||||
columnName: CreatingUser
|
||||
- entityType: Account
|
||||
fieldMappings:
|
||||
- identifier: FullName
|
||||
columnName: DeletingUser
|
||||
- entityType: IP
|
||||
fieldMappings:
|
||||
- identifier: Address
|
||||
columnName: CreatingIP
|
||||
- entityType: IP
|
||||
fieldMappings:
|
||||
- identifier: Address
|
||||
columnName: DeletingIP
|
|
@ -0,0 +1,47 @@
|
|||
id: adc32a33-1cd6-46f5-8801-e3ed8337885f
|
||||
name: External Upstream Source Added to Azure DevOps Feed
|
||||
description: |
|
||||
'The detection looks for new external sources added to an Azure DevOps feed. An allow list can be customized to explicitly allow known good sources.
|
||||
An attacker could look to add a malicious feed in order to inject malicious packages into a build pipeline.'
|
||||
severity: Medium
|
||||
requiredDataConnectors: []
|
||||
queryFrequency: 1d
|
||||
queryPeriod: 1d
|
||||
triggerOperator: gt
|
||||
triggerThreshold: 0
|
||||
tactics:
|
||||
- InitialAccess
|
||||
relevantTechniques:
|
||||
- T1199
|
||||
query: |
|
||||
// Add any known allowed sources and source locations to the filter below (the NuGet Gallery has been added here as an example).
|
||||
let allowed_sources = dynamic(["NuGet Gallery"]);
|
||||
let allowed_locations = dynamic(["https://api.nuget.org/v3/index.json"]);
|
||||
AzureDevOpsAuditing
|
||||
// Look for feeds created or modified at either the organization or project level
|
||||
| where OperationName matches regex "Artifacts.Feed.(Org|Project).Modify"
|
||||
| where Details has "UpstreamSources, added"
|
||||
| extend FeedName = tostring(Data.FeedName)
|
||||
| extend FeedId = tostring(Data.FeedId)
|
||||
| extend UpstreamsAdded = Data.UpstreamsAdded
|
||||
// As multiple feeds may be added expand these out
|
||||
| mv-expand UpstreamsAdded
|
||||
// Only focus on external feeds
|
||||
| where UpstreamsAdded.UpstreamSourceType !~ "internal"
|
||||
| extend SourceLocation = tostring(UpstreamsAdded.Location)
|
||||
| extend SourceName = tostring(UpstreamsAdded.Name)
|
||||
// Exclude sources and locations in the allow list
|
||||
| where SourceLocation !in (allowed_locations) and SourceName !in (allowed_sources)
|
||||
| extend SourceProtocol = tostring(UpstreamsAdded.Protocol)
|
||||
| extend SourceStatus = tostring(UpstreamsAdded.Status)
|
||||
| project-reorder TimeGenerated, OperationName, ScopeDisplayName, ProjectName, FeedName, SourceName, SourceLocation, SourceProtocol, ActorUPN, UserAgent, IpAddress
|
||||
| extend timestamp = TimeGenerated, AccountCustomEntity = ActorUPN, IPCustomEntity = IpAddress
|
||||
entityMappings:
|
||||
- entityType: Account
|
||||
fieldMappings:
|
||||
- identifier: FullName
|
||||
columnName: ActorUPN
|
||||
- entityType: IP
|
||||
fieldMappings:
|
||||
- identifier: Address
|
||||
columnName: IpAddress
|
|
@ -0,0 +1,63 @@
|
|||
id: 4ce177b3-56b1-4f0e-b83e-27eed4cb0b16
|
||||
name: New Agent Added to Pool by New User or of a New OS Type.
|
||||
description: |
|
||||
'As seen in attacks such as SolarWinds attackers can look to subvert a build process by controlling build servers. Azure DevOps uses agent pools to execute pipeline tasks. An attacker could insert compromised agents that they control into the pools in order to execute malicious code. This query looks for users adding agents to pools they have not added agents to before, or adding agents to a pool of an OS that has not been added to that pool before. This detection has potential for false positives so has a configurable allow list to allow for certain users to be excluded from the logic.'
|
||||
severity: Medium
|
||||
requiredDataConnectors: []
|
||||
queryFrequency: 1d
|
||||
queryPeriod: 30d
|
||||
triggerOperator: gt
|
||||
triggerThreshold: 0
|
||||
tactics:
|
||||
- Execution
|
||||
relevantTechniques:
|
||||
- T1053
|
||||
query: |
|
||||
let lookback = 30d;
|
||||
let timeframe = 1d;
|
||||
// exclude allowed users from query such as the ADO service
|
||||
let allowed_users = dynamic(["Azure DevOps Service"]);
|
||||
union
|
||||
// Look for agents being added to a pool of a OS type not seen with that pool before
|
||||
(AzureDevOpsAuditing
|
||||
| where TimeGenerated > ago(lookback) and TimeGenerated < ago(timeframe)
|
||||
| where OperationName =~ "Library.AgentAdded"
|
||||
| where ActorUPN !in (allowed_users)
|
||||
| extend AgentPoolName = tostring(Data.AgentPoolName)
|
||||
| extend OsDescription = tostring(Data.OsDescription)
|
||||
| where isnotempty(OsDescription)
|
||||
| extend OsDescription = tostring(split(OsDescription, "#", 0)[0])
|
||||
| project AgentPoolName, OsDescription
|
||||
| join kind=rightanti (AzureDevOpsAuditing
|
||||
| where TimeGenerated > ago(timeframe)
|
||||
| where OperationName == "Library.AgentAdded"
|
||||
| extend AgentPoolName = tostring(Data.AgentPoolName)
|
||||
| extend OsDescription = tostring(Data.OsDescription)
|
||||
| where isnotempty(OsDescription)
|
||||
| extend OsDescription = tostring(split(OsDescription, "#", 0)[0])) on AgentPoolName, OsDescription),
|
||||
// Look for users addeing agents to a pool that they have not added agents to before.
|
||||
(AzureDevOpsAuditing
|
||||
| where TimeGenerated > ago(lookback) and TimeGenerated < ago(timeframe)
|
||||
| extend AgentPoolName = tostring(Data.AgentPoolName)
|
||||
| where ActorUPN !in (allowed_users)
|
||||
| project AgentPoolName, ActorUPN
|
||||
| join kind=rightanti (AzureDevOpsAuditing
|
||||
| where TimeGenerated > ago(timeframe)
|
||||
| where OperationName == "Library.AgentAdded"
|
||||
| where ActorUPN !in (allowed_users)
|
||||
| extend AgentPoolName = tostring(Data.AgentPoolName)
|
||||
) on AgentPoolName, ActorUPN)
|
||||
| extend AgentName = tostring(Data.AgentName)
|
||||
| extend OsDescription = tostring(Data.OsDescription)
|
||||
| extend SystemDetails = Data.SystemCapabilities
|
||||
| project-reorder TimeGenerated, OperationName, ScopeDisplayName, AgentPoolName, AgentName, ActorUPN, IpAddress, UserAgent, OsDescription, SystemDetails, Data
|
||||
| extend timestamp = TimeGenerated, AccountCustomEntity = ActorUPN, IPCustomEntity = IpAddress
|
||||
entityMappings:
|
||||
- entityType: Account
|
||||
fieldMappings:
|
||||
- identifier: FullName
|
||||
columnName: ActorUPN
|
||||
- entityType: IP
|
||||
fieldMappings:
|
||||
- identifier: Address
|
||||
columnName: IpAddress
|
|
@ -0,0 +1,40 @@
|
|||
id: 35ce9aff-1708-45b8-a295-5e9a307f5f17
|
||||
name: New PA, PCA, or PCAS added to Azure DevOps
|
||||
description: |
|
||||
'In order for an attacker to be able to conduct many potential attacks against Azure DevOps they will need to gain elevated permissions. This detection looks for users being granted key administrative permissions. If the principal of least privilege is applied the number of users granted these permissions should be small. Note that permissions can also be granted via Azure AD groups and monitoring of these should also be conducted.'
|
||||
severity: Medium
|
||||
requiredDataConnectors: []
|
||||
queryFrequency: 1d
|
||||
queryPeriod: 1d
|
||||
triggerOperator: gt
|
||||
triggerThreshold: 0
|
||||
tactics:
|
||||
- InitialAccess
|
||||
relevantTechniques:
|
||||
- T1078.004
|
||||
query: |
|
||||
AzureDevOpsAuditing
|
||||
| where OperationName =~ "Group.UpdateGroupMembership.Add"
|
||||
| where Details has_any ("Project Administrators", "Project Collection Administrators", "Project Collection Service Accounts", "Build Administrator")
|
||||
| project-reorder TimeGenerated, Details, ActorUPN, IpAddress, UserAgent, AuthenticationMechanism, ScopeDisplayName
|
||||
| extend timekey = bin(TimeGenerated, 1h)
|
||||
| extend ActorUserId = tostring(Data.MemberId)
|
||||
| project timekey, ActorUserId, AddingUser=ActorUPN, TimeAdded=TimeGenerated, PermissionGrantDetails = Details
|
||||
// Get details of operations conducted by user soon after elevation of permissions
|
||||
| join (AzureDevOpsAuditing
|
||||
| extend timekey = bin(TimeGenerated, 1h)) on timekey, ActorUserId
|
||||
| summarize ActionsWhenAdded = make_set(OperationName) by ActorUPN, AddingUser, TimeAdded, PermissionGrantDetails, IpAddress, UserAgent
|
||||
| extend timestamp = TimeGenerated, AccountCustomEntity = ActorUPN, IPCustomEntity = IpAddress
|
||||
entityMappings:
|
||||
- entityType: Account
|
||||
fieldMappings:
|
||||
- identifier: FullName
|
||||
columnName: ActorUPN
|
||||
- entityType: Account
|
||||
fieldMappings:
|
||||
- identifier: FullName
|
||||
columnName: AddingUser
|
||||
- entityType: IP
|
||||
fieldMappings:
|
||||
- identifier: Address
|
||||
columnName: IpAddress
|
Загрузка…
Ссылка в новой задаче