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

Azure DevOps Agent Pool Created Then Deleted

Back
Idacfdee3f-b794-404a-aeba-ef6a1fa08ad1
RulenameAzure DevOps Agent Pool Created Then Deleted
DescriptionAs 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.
SeverityHigh
TacticsDefenseEvasion
TechniquesT1578.002
KindScheduled
Query frequency7d
Query period14d
Trigger threshold0
Trigger operatorgt
Source Urihttps://github.com/Azure/Azure-Sentinel/blob/master/Solutions/AzureDevOpsAuditing/Analytic Rules/ADOAgentPoolCreatedDeleted.yaml
Version1.0.1
Arm templateacfdee3f-b794-404a-aeba-ef6a1fa08ad1.json
Deploy To Azure
let lookback = 14d;
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
severity: High
name: Azure DevOps Agent Pool Created Then Deleted
requiredDataConnectors: []
id: acfdee3f-b794-404a-aeba-ef6a1fa08ad1
tactics:
- DefenseEvasion
queryFrequency: 7d
triggerOperator: gt
OriginalUri: https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/AzureDevOpsAuditing/Analytic Rules/ADOAgentPoolCreatedDeleted.yaml
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.'  
triggerThreshold: 0
kind: Scheduled
relevantTechniques:
- T1578.002
query: |
  let lookback = 14d;
  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:
  - columnName: AccountCustomEntity
    identifier: FullName
- entityType: IP
  fieldMappings:
  - columnName: IPCustomEntity
    identifier: Address
status: Available
version: 1.0.1
queryPeriod: 14d
{
  "$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/acfdee3f-b794-404a-aeba-ef6a1fa08ad1')]",
      "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/acfdee3f-b794-404a-aeba-ef6a1fa08ad1')]",
      "type": "Microsoft.OperationalInsights/workspaces/providers/alertRules",
      "kind": "Scheduled",
      "apiVersion": "2022-11-01-preview",
      "properties": {
        "displayName": "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.\nAzure 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 \ndetection 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), \nas an attacker is likely to remove a malicious pool once used in order to reduce/remove evidence of their activity.'\n",
        "severity": "High",
        "enabled": true,
        "query": "let lookback = 14d;\nlet timewindow = 7d;\nAzureDevOpsAuditing\n| where TimeGenerated > ago(lookback)\n| where OperationName =~ \"Library.AgentPoolCreated\"\n| extend AgentCloudId = tostring(Data.AgentCloudId)\n| extend PoolType = iif(isnotempty(AgentCloudId), \"Azure VMs\", \"Self Hosted\")\n// Comment this line out to include cloud pools as well\n| where PoolType == \"Self Hosted\"\n| extend AgentPoolName = tostring(Data.AgentPoolName)\n| extend AgentPoolId = tostring(Data.AgentPoolId)\n| extend IsHosted = tostring(Data.IsHosted)\n| extend IsLegacy = tostring(Data.IsLegacy)\n| extend timekey = bin(TimeGenerated, timewindow)\n// Join only with pools deleted in the same window\n| join (AzureDevOpsAuditing\n| where TimeGenerated > ago(lookback)\n| where OperationName =~ \"Library.AgentPoolDeleted\"\n| extend AgentPoolName = tostring(Data.AgentPoolName)\n| extend AgentPoolId = tostring(Data.AgentPoolId)\n| extend timekey = bin(TimeGenerated, timewindow)) on AgentPoolId, timekey\n| project-reorder TimeGenerated, ActorUPN, UserAgent, IpAddress, AuthenticationMechanism, OperationName, AgentPoolName, IsHosted, IsLegacy, Data\n| extend timestamp = TimeGenerated, AccountCustomEntity = ActorUPN, IPCustomEntity = IpAddress\n",
        "queryFrequency": "P7D",
        "queryPeriod": "P14D",
        "triggerOperator": "GreaterThan",
        "triggerThreshold": 0,
        "suppressionDuration": "PT1H",
        "suppressionEnabled": false,
        "tactics": [
          "DefenseEvasion"
        ],
        "techniques": [
          "T1578.002"
        ],
        "alertRuleTemplateName": "acfdee3f-b794-404a-aeba-ef6a1fa08ad1",
        "customDetails": null,
        "entityMappings": [
          {
            "fieldMappings": [
              {
                "identifier": "FullName",
                "columnName": "AccountCustomEntity"
              }
            ],
            "entityType": "Account"
          },
          {
            "fieldMappings": [
              {
                "identifier": "Address",
                "columnName": "IPCustomEntity"
              }
            ],
            "entityType": "IP"
          }
        ],
        "status": "Available",
        "templateVersion": "1.0.1",
        "OriginalUri": "https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/AzureDevOpsAuditing/Analytic Rules/ADOAgentPoolCreatedDeleted.yaml"
      }
    }
  ]
}