Azure DevOps Pipeline modified by a new user.
Id | 155e9134-d5ad-4a6f-88f3-99c220040b66 |
Rulename | Azure DevOps Pipeline 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 choose to filter this detection to only alert when the user also has AAD IdP alerts associated with them. |
Severity | Medium |
Tactics | Execution DefenseEvasion |
Techniques | T1578 T1569 |
Kind | Scheduled |
Query frequency | 1d |
Query period | 14d |
Trigger threshold | 0 |
Trigger operator | gt |
Source Uri | https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/AzureDevOpsAuditing/Analytic Rules/ADOPipelineModifiedbyNewUser.yaml |
Version | 1.0.2 |
Arm template | 155e9134-d5ad-4a6f-88f3-99c220040b66.json |
// Set the lookback to determine if user has created pipelines before
let timeback = 14d;
// 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 specific projects so we create 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
version: 1.0.2
status: Available
queryFrequency: 1d
requiredDataConnectors: []
entityMappings:
- fieldMappings:
- columnName: AccountCustomEntity
identifier: FullName
entityType: Account
- fieldMappings:
- columnName: IPCustomEntity
identifier: Address
entityType: IP
kind: Scheduled
queryPeriod: 14d
severity: Medium
query: |
// Set the lookback to determine if user has created pipelines before
let timeback = 14d;
// 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 specific projects so we create 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
triggerOperator: gt
id: 155e9134-d5ad-4a6f-88f3-99c220040b66
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 choose to filter this detection to only alert when the user also has AAD IdP alerts associated with them.'
triggerThreshold: 0
name: Azure DevOps Pipeline modified by a new user.
relevantTechniques:
- T1578
- T1569
tactics:
- Execution
- DefenseEvasion
OriginalUri: https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/AzureDevOpsAuditing/Analytic Rules/ADOPipelineModifiedbyNewUser.yaml
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"workspace": {
"type": "String"
}
},
"resources": [
{
"id": "[concat(resourceId('Microsoft.OperationalInsights/workspaces/providers', parameters('workspace'), 'Microsoft.SecurityInsights'),'/alertRules/155e9134-d5ad-4a6f-88f3-99c220040b66')]",
"name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/155e9134-d5ad-4a6f-88f3-99c220040b66')]",
"type": "Microsoft.OperationalInsights/workspaces/providers/alertRules",
"kind": "Scheduled",
"apiVersion": "2022-11-01",
"properties": {
"displayName": "Azure DevOps Pipeline 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. \nThis 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) \nin order to show if the user conducting the action has any associated AAD IdP alerts. You can also choose to filter this detection to only alert when the user also has AAD IdP alerts associated with them.'\n",
"severity": "Medium",
"enabled": true,
"query": "// Set the lookback to determine if user has created pipelines before\nlet timeback = 14d;\n// Set the period for detections\nlet timeframe = 1d;\n// Get a list of previous Release Pipeline creators to exclude\nlet releaseusers = AzureDevOpsAuditing\n| where TimeGenerated > ago(timeback) and TimeGenerated < ago(timeframe)\n| where OperationName in (\"Release.ReleasePipelineCreated\", \"Release.ReleasePipelineModified\")\n// We want to look for users performing actions in specific projects so we create this userscope object to match on\n| extend UserScope = strcat(ActorUserId, \"-\", ProjectName)\n| summarize by UserScope;\n// Get Release Pipeline creations by new users\nAzureDevOpsAuditing\n| where TimeGenerated > ago(timeframe)\n| where OperationName =~ \"Release.ReleasePipelineModified\"\n| extend UserScope = strcat(ActorUserId, \"-\", ProjectName)\n| where UserScope !in (releaseusers)\n| extend ActorUPN = tolower(ActorUPN)\n| project-away Id, ActivityId, ActorCUID, ScopeId, ProjectId, TenantId, SourceSystem, UserScope\n// See if any of these users have Azure AD alerts associated with them in the same timeframe\n| join kind = leftouter (\nSecurityAlert\n| where TimeGenerated > ago(timeframe)\n| where ProviderName == \"IPC\"\n| extend AadUserId = tostring(parse_json(Entities)[0].AadUserId)\n| summarize Alerts=count() by AadUserId) on $left.ActorUserId == $right.AadUserId\n| extend Alerts = iif(isnotempty(Alerts), Alerts, 0)\n// Uncomment the line below to only show results where the user as AADIdP alerts\n//| where Alerts > 0\n| extend timestamp = TimeGenerated, AccountCustomEntity = ActorUPN, IPCustomEntity = IpAddress\n",
"queryFrequency": "P1D",
"queryPeriod": "P14D",
"triggerOperator": "GreaterThan",
"triggerThreshold": 0,
"suppressionDuration": "PT1H",
"suppressionEnabled": false,
"tactics": [
"Execution",
"DefenseEvasion"
],
"techniques": [
"T1578",
"T1569"
],
"alertRuleTemplateName": "155e9134-d5ad-4a6f-88f3-99c220040b66",
"customDetails": null,
"entityMappings": [
{
"entityType": "Account",
"fieldMappings": [
{
"identifier": "FullName",
"columnName": "AccountCustomEntity"
}
]
},
{
"entityType": "IP",
"fieldMappings": [
{
"identifier": "Address",
"columnName": "IPCustomEntity"
}
]
}
],
"status": "Available",
"OriginalUri": "https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/AzureDevOpsAuditing/Analytic Rules/ADOPipelineModifiedbyNewUser.yaml",
"templateVersion": "1.0.2"
}
}
]
}