Query Details

High Privilege Takeover Agent ID Administrator Role Abuse

Query

# *High-Privilege Takeover - Agent ID Administrator Role Abuse*

## Query Information

#### MITRE ATT&CK Technique(s)

| Technique ID | Title    | Link    |
| ---  | --- | --- |
| T1098 | Account Manipulation | https://attack.mitre.org/techniques/T1098/ |

#### Description
This detection identifies potential privilege escalation involving the Agent ID Administrator role. Historically, this role could be exploited to bypass intended restrictions by assigning the actor as an owner of sensitive Service Principals and subsequently adding unauthorized credentials.

#### Author <Optional>
- **Name: Benjamin Zulliger**
- **Github: https://github.com/benscha/KQLAdvancedHunting**
- **LinkedIn: https://www.linkedin.com/in/benjamin-zulliger/**

#### References
- https://cybersecuritynews.com/entra-agent-id-administrator-abused/

## Sentinel

```KQL
// ===========================================================================
// Detection: Agent ID Administrator → Service Principal Takeover
// Source: Silverfort Research / Scope Overreach (Privilege Escalation) https://cybersecuritynews.com/entra-agent-id-administrator-abused/
// ===========================================================================
let LookbackDays = 30d;
let AgentRoleAssignments =
    AuditLogs
    | where TimeGenerated > ago(LookbackDays)
    | where OperationName == "Add member to role"
    | where TargetResources has "Agent ID Administrator"
    | extend NewRoleMemberId = tostring(TargetResources[0].id)
    , NewRoleMemberName = tostring(TargetResources[0].userPrincipalName)
    , RoleAssignedAt = TimeGenerated
    | project NewRoleMemberId, NewRoleMemberName, RoleAssignedAt;
// Owner additions to Service Principals
let OwnerAdditions =
    AuditLogs
    | where TimeGenerated > ago(LookbackDays)
    | where OperationName == "Add owner to service principal"
    | where Result == "success"
    | extend ActorId   = tostring(InitiatedBy.user.id)
    , ActorUPN  = tostring(InitiatedBy.user.userPrincipalName)
    , ActorIP   = tostring(InitiatedBy.user.ipAddress)
    // Target: The Service Principal that received a new owner
    | mv-expand TargetResource = TargetResources
    | where tostring(TargetResource["@odata.type"]) == "#microsoft.graph.servicePrincipal"
        or tostring(TargetResource.type) == "ServicePrincipal"
    | extend TargetSPName = tostring(TargetResource.displayName)
    , TargetSPId   = tostring(TargetResource.id)
    | project TimeGenerated, ActorId, ActorUPN, ActorIP, TargetSPName, TargetSPId;
// Credential additions (The proof of actual takeover)
let CredentialAdditions =
    AuditLogs
    | where TimeGenerated > ago(LookbackDays)
    | where OperationName in ("Add service principal credentials", 
                               "Update application – Certificates and secrets management")
    | where Result == "success"
    | extend ActorId   = tostring(InitiatedBy.user.id)
    , ActorUPN = tostring(InitiatedBy.user.userPrincipalName)
    | mv-expand TargetResource = TargetResources
    | extend TargetSPId   = tostring(TargetResource.id)
    , TargetSPName = tostring(TargetResource.displayName)
    | project CredentialAddedAt = TimeGenerated, ActorId, ActorUPN, TargetSPId, TargetSPName;
OwnerAdditions
| join kind=inner AgentRoleAssignments
    on $left.ActorId == $right.NewRoleMemberId
// Temporal Consistency: Owner change must occur AFTER role assignment
| where TimeGenerated >= RoleAssignedAt
// Correlate Credential step (Optional but provides high-fidelity)
| join kind=leftouter CredentialAdditions
    on $left.ActorId == $right.ActorId
    and $left.TargetSPId == $right.TargetSPId
// Prioritize non-agent SPs (Noise reduction filter)
| extend IsLikelyAgentSP = TargetSPName has_any ("Connector", "Agent", "Proxy", "Bot")
| project
    TimeGenerated,
    DetectionPhase    = "1 - Owner Added",
    ActorUPN,
    ActorIP,
    TargetSPName,
    TargetSPId,
    RoleAssignedAt,
    CredentialAddedAt,
    IsLikelyAgentSP,
    HighRiskAlert     = iff(isnotempty(CredentialAddedAt) and not(IsLikelyAgentSP), true, false)
| order by HighRiskAlert desc, TimeGenerated desc
```

Explanation

This KQL query is designed to detect potential privilege escalation activities involving the "Agent ID Administrator" role in an organization's Azure environment. Here's a simplified breakdown of what the query does:

  1. Purpose: The query aims to identify instances where someone might have exploited the "Agent ID Administrator" role to gain unauthorized access to sensitive resources, specifically Service Principals, by adding themselves as owners and then adding unauthorized credentials.

  2. Lookback Period: The query examines audit logs from the past 30 days.

  3. Steps Involved:

    • Role Assignments: It first checks for any instances where a user was added to the "Agent ID Administrator" role.
    • Owner Additions: It then looks for cases where a new owner was added to a Service Principal, which could indicate a takeover attempt.
    • Credential Additions: Finally, it checks if any credentials were added to the Service Principals, which would confirm a successful takeover.
  4. Correlation: The query correlates these activities to ensure that the owner addition happened after the role assignment, indicating a potential misuse of privileges.

  5. Risk Assessment: It flags high-risk activities where credentials were added to Service Principals that are not likely to be legitimate agents (e.g., those not containing terms like "Connector", "Agent", "Proxy", or "Bot").

  6. Output: The results are sorted to prioritize high-risk alerts, showing details like the time of the event, the user involved, their IP address, the Service Principal affected, and whether the activity is considered high risk.

Overall, this query helps security teams identify and investigate suspicious activities that could indicate a compromise of high-privilege roles within their Azure environment.

Details

Benjamin Zulliger profile picture

Benjamin Zulliger

Released: April 25, 2026

Tables

AuditLogs

Keywords

AuditLogsAgentIDAdministratorServicePrincipalActorIdActorUPNActorIPTargetSPNameTargetSPIdCredentialAddedAtRoleAssignedAtHighRiskAlertDetectionPhase

Operators

letagowherehasextendtostringprojectmv-expandjoinon==>=inorder bydesciffisnotemptynothas_anytruefalse

Actions