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

New Agent Added to Pool by New User or Added to a New OS Type

Back
Id4ce177b3-56b1-4f0e-b83e-27eed4cb0b16
RulenameNew Agent Added to Pool by New User or Added to a New OS Type
DescriptionAs 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.
SeverityMedium
TacticsExecution
TechniquesT1053
KindScheduled
Query frequency1d
Query period14d
Trigger threshold0
Trigger operatorgt
Source Urihttps://github.com/Azure/Azure-Sentinel/blob/master/Solutions/AzureDevOpsAuditing/Analytic Rules/NewAgentAddedToPoolbyNewUserorofNewOS.yaml
Version1.0.4
Arm template4ce177b3-56b1-4f0e-b83e-27eed4cb0b16.json
Deploy To Azure
let lookback = 14d;
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
| extend AccountName = tostring(split(ActorUPN, "@")[0]), AccountUPNSuffix = tostring(split(ActorUPN, "@")[1])
triggerOperator: gt
requiredDataConnectors: []
queryPeriod: 14d
status: Available
kind: Scheduled
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.'  
query: |
  let lookback = 14d;
  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
  | extend AccountName = tostring(split(ActorUPN, "@")[0]), AccountUPNSuffix = tostring(split(ActorUPN, "@")[1])  
relevantTechniques:
- T1053
OriginalUri: https://github.com/Azure/Azure-Sentinel/blob/master/Solutions/AzureDevOpsAuditing/Analytic Rules/NewAgentAddedToPoolbyNewUserorofNewOS.yaml
severity: Medium
triggerThreshold: 0
name: New Agent Added to Pool by New User or Added to a New OS Type
tactics:
- Execution
version: 1.0.4
entityMappings:
- entityType: Account
  fieldMappings:
  - identifier: FullName
    columnName: ActorUPN
  - identifier: Name
    columnName: AccountName
  - identifier: UPNSuffix
    columnName: AccountUPNSuffix
- entityType: IP
  fieldMappings:
  - identifier: Address
    columnName: IpAddress
id: 4ce177b3-56b1-4f0e-b83e-27eed4cb0b16
queryFrequency: 1d
{
  "$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/4ce177b3-56b1-4f0e-b83e-27eed4cb0b16')]",
      "kind": "Scheduled",
      "name": "[concat(parameters('workspace'),'/Microsoft.SecurityInsights/4ce177b3-56b1-4f0e-b83e-27eed4cb0b16')]",
      "properties": {
        "alertRuleTemplateName": "4ce177b3-56b1-4f0e-b83e-27eed4cb0b16",
        "customDetails": null,
        "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. \nAn 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 \nnot 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 \nconfigurable allow list to allow for certain users to be excluded from the logic.'\n",
        "displayName": "New Agent Added to Pool by New User or Added to a New OS Type",
        "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/NewAgentAddedToPoolbyNewUserorofNewOS.yaml",
        "query": "let lookback = 14d;\nlet timeframe = 1d;\n// exclude allowed users from query such as the ADO service\nlet allowed_users = dynamic([\"Azure DevOps Service\"]);\nunion\n// Look for agents being added to a pool of a OS type not seen with that pool before\n(AzureDevOpsAuditing\n| where TimeGenerated > ago(lookback) and TimeGenerated < ago(timeframe)\n| where OperationName =~ \"Library.AgentAdded\"\n| where ActorUPN !in (allowed_users)\n| extend AgentPoolName = tostring(Data.AgentPoolName)\n| extend OsDescription = tostring(Data.OsDescription)\n| where isnotempty(OsDescription)\n| extend OsDescription = tostring(split(OsDescription, \"#\", 0)[0])\n| project AgentPoolName, OsDescription\n| join kind=rightanti (AzureDevOpsAuditing\n| where TimeGenerated > ago(timeframe)\n| where OperationName == \"Library.AgentAdded\"\n| extend AgentPoolName = tostring(Data.AgentPoolName)\n| extend OsDescription = tostring(Data.OsDescription)\n| where isnotempty(OsDescription)\n| extend OsDescription = tostring(split(OsDescription, \"#\", 0)[0])) on AgentPoolName, OsDescription),\n// Look for users addeing agents to a pool that they have not added agents to before.\n(AzureDevOpsAuditing\n| where TimeGenerated > ago(lookback) and TimeGenerated < ago(timeframe)\n| extend AgentPoolName = tostring(Data.AgentPoolName)\n| where ActorUPN !in (allowed_users)\n| project AgentPoolName, ActorUPN\n| join kind=rightanti (AzureDevOpsAuditing\n| where TimeGenerated > ago(timeframe)\n| where OperationName == \"Library.AgentAdded\"\n| where ActorUPN !in (allowed_users)\n| extend AgentPoolName = tostring(Data.AgentPoolName)\n) on AgentPoolName, ActorUPN)\n| extend AgentName = tostring(Data.AgentName)\n| extend OsDescription = tostring(Data.OsDescription)\n| extend SystemDetails = Data.SystemCapabilities\n| project-reorder TimeGenerated, OperationName, ScopeDisplayName, AgentPoolName, AgentName, ActorUPN, IpAddress, UserAgent, OsDescription, SystemDetails, Data\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",
        "suppressionDuration": "PT1H",
        "suppressionEnabled": false,
        "tactics": [
          "Execution"
        ],
        "techniques": [
          "T1053"
        ],
        "templateVersion": "1.0.4",
        "triggerOperator": "GreaterThan",
        "triggerThreshold": 0
      },
      "type": "Microsoft.OperationalInsights/workspaces/providers/alertRules"
    }
  ]
}