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 Created and Deleted on the Same Day

Back
Id17f23fbe-bb73-4324-8ecf-a18545a5dc26
RulenameAzure DevOps Pipeline Created and Deleted on the Same Day
DescriptionAn 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.
SeverityMedium
TacticsExecution
TechniquesT1072
KindScheduled
Query frequency3d
Query period3d
Trigger threshold0
Trigger operatorgt
Source Urihttps://github.com/Azure/Azure-Sentinel/blob/master/Solutions/AzureDevOpsAuditing/Analytic Rules/AzDOPipelineCreatedDeletedOneDay.yaml
Version1.0.2
Arm template17f23fbe-bb73-4324-8ecf-a18545a5dc26.json
Deploy To Azure
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 = TimeCreated
| extend CreatingUserAccountName = tostring(split(CreatingUser, "@")[0]), CreatingUserAccountUPNSuffix = tostring(split(CreatingUser, "@")[1])
| extend DeletingUserAccountName = tostring(split(DeletingUser, "@")[0]), DeletingUserAccountUPNSuffix = tostring(split(DeletingUser, "@")[1])
queryFrequency: 3d
severity: Medium
id: 17f23fbe-bb73-4324-8ecf-a18545a5dc26
status: Available
requiredDataConnectors: []
kind: Scheduled
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.'  
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 = TimeCreated
  | extend CreatingUserAccountName = tostring(split(CreatingUser, "@")[0]), CreatingUserAccountUPNSuffix = tostring(split(CreatingUser, "@")[1])
  | extend DeletingUserAccountName = tostring(split(DeletingUser, "@")[0]), DeletingUserAccountUPNSuffix = tostring(split(DeletingUser, "@")[1])  
tactics:
- Execution
triggerThreshold: 0
triggerOperator: gt
OriginalUri: https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/AzureDevOpsAuditing/Analytic Rules/AzDOPipelineCreatedDeletedOneDay.yaml
relevantTechniques:
- T1072
entityMappings:
- entityType: Account
  fieldMappings:
  - identifier: FullName
    columnName: CreatingUser
  - identifier: Name
    columnName: CreatingUserAccountName
  - identifier: UPNSuffix
    columnName: CreatingUserAccountUPNSuffix
- entityType: Account
  fieldMappings:
  - identifier: FullName
    columnName: DeletingUser
  - identifier: Name
    columnName: DeletingUserAccountName
  - identifier: UPNSuffix
    columnName: DeletingUserAccountUPNSuffix
- entityType: IP
  fieldMappings:
  - identifier: Address
    columnName: CreatingIP
- entityType: IP
  fieldMappings:
  - identifier: Address
    columnName: DeletingIP
version: 1.0.2
name: Azure DevOps Pipeline Created and Deleted on the Same Day
queryPeriod: 3d
{
  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "workspace": {
      "type": "String"
    }
  },
  "resources": [
    {
      "apiVersion": "2023-02-01-preview",
      "id": "[concat(resourceId('Microsoft.OperationalInsights/workspaces/providers', parameters('workspace'), 'Microsoft.SecurityInsights'),'/alertRules/17f23fbe-bb73-4324-8ecf-a18545a5dc26')]",
      "kind": "Scheduled",
      "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/17f23fbe-bb73-4324-8ecf-a18545a5dc26')]",
      "properties": {
        "alertRuleTemplateName": "17f23fbe-bb73-4324-8ecf-a18545a5dc26",
        "customDetails": null,
        "description": "'An attacker with access to Azure DevOps could create a pipeline to inject artifacts used by other pipelines, \nor to create a malicious software build that looks legitimate by using a pipeline that incorporates legitimate elements. \nAn attacker would also likely want to cover their tracks once conducting such activity. This query looks for Pipelines \ncreated and deleted within the same day, this is unlikely to be legitimate user activity in the majority of cases.'\n",
        "displayName": "Azure DevOps Pipeline Created and Deleted on the Same Day",
        "enabled": true,
        "entityMappings": [
          {
            "entityType": "Account",
            "fieldMappings": [
              {
                "columnName": "CreatingUser",
                "identifier": "FullName"
              },
              {
                "columnName": "CreatingUserAccountName",
                "identifier": "Name"
              },
              {
                "columnName": "CreatingUserAccountUPNSuffix",
                "identifier": "UPNSuffix"
              }
            ]
          },
          {
            "entityType": "Account",
            "fieldMappings": [
              {
                "columnName": "DeletingUser",
                "identifier": "FullName"
              },
              {
                "columnName": "DeletingUserAccountName",
                "identifier": "Name"
              },
              {
                "columnName": "DeletingUserAccountUPNSuffix",
                "identifier": "UPNSuffix"
              }
            ]
          },
          {
            "entityType": "IP",
            "fieldMappings": [
              {
                "columnName": "CreatingIP",
                "identifier": "Address"
              }
            ]
          },
          {
            "entityType": "IP",
            "fieldMappings": [
              {
                "columnName": "DeletingIP",
                "identifier": "Address"
              }
            ]
          }
        ],
        "OriginalUri": "https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/AzureDevOpsAuditing/Analytic Rules/AzDOPipelineCreatedDeletedOneDay.yaml",
        "query": "let timeframe = 3d;\n// Get Release Pipeline Creation Events and group by day\nAzureDevOpsAuditing\n| where TimeGenerated > ago(timeframe)\n| where OperationName =~ \"Release.ReleasePipelineCreated\"\n// Group by day\n| extend timekey = bin(TimeGenerated, 1d)\n| extend PipelineId = tostring(Data.PipelineId)\n| extend PipelineName = tostring(Data.PipelineName)\n// Rename some columns to make output clearer\n| project-rename TimeCreated = TimeGenerated, CreatingUser = ActorUPN, CreatingUserAgent = UserAgent, CreatingIP = IpAddress\n// Join with Release Pipeline Deletions where Pipeline ID is the same and deletion occurred on same day as creation\n| join (AzureDevOpsAuditing\n| where TimeGenerated > ago(timeframe)\n| where OperationName =~ \"Release.ReleasePipelineDeleted\"\n// Group by day\n| extend timekey = bin(TimeGenerated, 1d)\n| extend PipelineId = tostring(Data.PipelineId)\n| extend PipelineName = tostring(Data.PipelineName)\n// Rename some things to make the output clearer\n| project-rename TimeDeleted = TimeGenerated,DeletingUser = ActorUPN, DeletingUserAgent = UserAgent, DeletingIP = IpAddress) on PipelineId, timekey\n| project TimeCreated, TimeDeleted, PipelineName, PipelineId, CreatingUser, CreatingIP, CreatingUserAgent, DeletingUser, DeletingIP, DeletingUserAgent, ScopeDisplayName, ProjectName, Data, OperationName, OperationName1\n| extend timestamp = TimeCreated\n| extend CreatingUserAccountName = tostring(split(CreatingUser, \"@\")[0]), CreatingUserAccountUPNSuffix = tostring(split(CreatingUser, \"@\")[1])\n| extend DeletingUserAccountName = tostring(split(DeletingUser, \"@\")[0]), DeletingUserAccountUPNSuffix = tostring(split(DeletingUser, \"@\")[1])\n",
        "queryFrequency": "P3D",
        "queryPeriod": "P3D",
        "severity": "Medium",
        "status": "Available",
        "suppressionDuration": "PT1H",
        "suppressionEnabled": false,
        "tactics": [
          "Execution"
        ],
        "techniques": [
          "T1072"
        ],
        "templateVersion": "1.0.2",
        "triggerOperator": "GreaterThan",
        "triggerThreshold": 0
      },
      "type": "Microsoft.OperationalInsights/workspaces/providers/alertRules"
    }
  ]
}