Microsoft Sentinel Analytic Rules
cloudbrothers.infoAzure Sentinel RepoToggle Dark/Light/Auto modeToggle Dark/Light/Auto modeToggle Dark/Light/Auto modeBack to homepage

Azure DevOps Pipeline modified by a new user

Back
Id155e9134-d5ad-4a6f-88f3-99c220040b66
RulenameAzure DevOps Pipeline modified by a new user
DescriptionThere 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 Microsoft Entra ID Protection in order to show if the user conducting the action has any associated Microsoft Entra ID Protection alerts. You can also choose to filter this detection to only alert when the user also has Microsoft Entra ID Protection alerts associated with them.
SeverityMedium
TacticsExecution
DefenseEvasion
TechniquesT1578
T1569
KindScheduled
Query frequency1d
Query period14d
Trigger threshold0
Trigger operatorgt
Source Urihttps://github.com/Azure/Azure-Sentinel/blob/master/Solutions/AzureDevOpsAuditing/Analytic Rules/ADOPipelineModifiedbyNewUser.yaml
Version1.0.7
Arm template155e9134-d5ad-4a6f-88f3-99c220040b66.json
Deploy To Azure
// 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
| extend AccountName = tostring(split(ActorUPN, "@")[0]), AccountUPNSuffix = tostring(split(ActorUPN, "@")[1])
name: Azure DevOps Pipeline modified by a new user
version: 1.0.7
severity: Medium
queryFrequency: 1d
triggerOperator: gt
relevantTechniques:
- T1578
- T1569
status: Available
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 Microsoft Entra ID Protection in order to show if the user conducting the action has any associated Microsoft Entra ID Protection alerts. You can also choose to filter this detection to only alert when the user also has Microsoft Entra ID Protection alerts associated with them.'  
OriginalUri: https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/AzureDevOpsAuditing/Analytic Rules/ADOPipelineModifiedbyNewUser.yaml
requiredDataConnectors: []
entityMappings:
- fieldMappings:
  - identifier: FullName
    columnName: ActorUPN
  - identifier: Name
    columnName: AccountName
  - identifier: UPNSuffix
    columnName: AccountUPNSuffix
  entityType: Account
- fieldMappings:
  - identifier: Address
    columnName: IpAddress
  entityType: IP
tactics:
- Execution
- DefenseEvasion
queryPeriod: 14d
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
  | extend AccountName = tostring(split(ActorUPN, "@")[0]), AccountUPNSuffix = tostring(split(ActorUPN, "@")[1])  
kind: Scheduled
triggerThreshold: 0
id: 155e9134-d5ad-4a6f-88f3-99c220040b66
{
  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "workspace": {
      "type": "String"
    }
  },
  "resources": [
    {
      "apiVersion": "2024-01-01-preview",
      "id": "[concat(resourceId('Microsoft.OperationalInsights/workspaces/providers', parameters('workspace'), 'Microsoft.SecurityInsights'),'/alertRules/155e9134-d5ad-4a6f-88f3-99c220040b66')]",
      "kind": "Scheduled",
      "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/155e9134-d5ad-4a6f-88f3-99c220040b66')]",
      "properties": {
        "alertRuleTemplateName": "155e9134-d5ad-4a6f-88f3-99c220040b66",
        "customDetails": null,
        "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 Microsoft Entra ID Protection in order to show if the user conducting the action has any associated Microsoft Entra ID Protection alerts. You can also choose to filter this detection to only alert when the user also has Microsoft Entra ID Protection alerts associated with them.'\n",
        "displayName": "Azure DevOps Pipeline modified by a new user",
        "enabled": true,
        "entityMappings": [
          {
            "entityType": "Account",
            "fieldMappings": [
              {
                "columnName": "ActorUPN",
                "identifier": "FullName"
              },
              {
                "columnName": "AccountName",
                "identifier": "Name"
              },
              {
                "columnName": "AccountUPNSuffix",
                "identifier": "UPNSuffix"
              }
            ]
          },
          {
            "entityType": "IP",
            "fieldMappings": [
              {
                "columnName": "IpAddress",
                "identifier": "Address"
              }
            ]
          }
        ],
        "OriginalUri": "https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/AzureDevOpsAuditing/Analytic Rules/ADOPipelineModifiedbyNewUser.yaml",
        "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\n| extend AccountName = tostring(split(ActorUPN, \"@\")[0]), AccountUPNSuffix = tostring(split(ActorUPN, \"@\")[1])\n",
        "queryFrequency": "P1D",
        "queryPeriod": "P14D",
        "severity": "Medium",
        "status": "Available",
        "subTechniques": [],
        "suppressionDuration": "PT1H",
        "suppressionEnabled": false,
        "tactics": [
          "DefenseEvasion",
          "Execution"
        ],
        "techniques": [
          "T1569",
          "T1578"
        ],
        "templateVersion": "1.0.7",
        "triggerOperator": "GreaterThan",
        "triggerThreshold": 0
      },
      "type": "Microsoft.OperationalInsights/workspaces/providers/alertRules"
    }
  ]
}