# Detecting Azure Service Principal Abuse

> 在 Microsoft Entra ID 环境中检测和调查 Azure 服务主体滥用，包括权限提升、凭据入侵、管理员同意绕过和未授权枚举。

- Skill: `killvxk/detecting-azure-service-principal-abuse` (Agent Skill, multi-file: 8 files)
- Install (CLI): `npx skillmds@latest add killvxk/detecting-azure-service-principal-abuse`
- Raw SKILL.md: https://api.skillmd.com/api/skills/killvxk/detecting-azure-service-principal-abuse/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: DevOps & Infra
- License: Apache-2.0
- Author: killvxk (https://skillmd.com/u/killvxk)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/killvxk/detecting-azure-service-principal-abuse

---


# 检测 Azure 服务主体滥用

## 概述

Azure 服务主体是应用程序、服务和自动化工具用于访问 Azure 资源的身份对象。攻击者利用服务主体进行权限提升、横向移动和持久访问。关键滥用模式包括：向现有主体添加凭据、分配特权角色、绕过管理员同意以及枚举服务主体以寻找攻击路径。应用程序所有权授予管理凭据和配置权限的能力，从而产生隐藏的权限提升路径。

## 前置条件

- 具有 Microsoft Entra ID P2 许可证的 Azure 订阅
- 访问 Azure AD 审计日志和登录日志的权限
- 用于基于 SIEM 检测的 Microsoft Sentinel 或 Splunk
- 用于调查的 Microsoft Graph API 权限
- 至少具有全局读取者或安全读取者角色

## 关键滥用模式

### 1. 向服务主体添加新凭据

攻击者添加新的客户端密钥或证书以获得持久访问：

**检测查询（KQL - Sentinel）：**
```kql
AuditLogs
| where OperationName has "Add service principal credentials"
    or OperationName has "Update application - Certificates and secrets management"
| extend InitiatedBy = tostring(InitiatedBy.user.userPrincipalName)
| extend TargetSP = tostring(TargetResources[0].displayName)
| extend TargetSPId = tostring(TargetResources[0].id)
| project TimeGenerated, InitiatedBy, OperationName, TargetSP, TargetSPId
| sort by TimeGenerated desc
```

**检测查询（SPL - Splunk）：**
```spl
index=azure sourcetype="azure:aad:audit"
operationName="Add service principal credentials"
    OR operationName="Update application*Certificates and secrets*"
| stats count by initiatedBy.user.userPrincipalName, targetResources{}.displayName, _time
| sort -_time
```

### 2. 向服务主体分配特权角色

```kql
AuditLogs
| where OperationName == "Add member to role"
| extend RoleName = tostring(TargetResources[0].modifiedProperties[1].newValue)
| where RoleName has_any ("Global Administrator", "Application Administrator",
    "Privileged Role Administrator", "Cloud Application Administrator")
| extend TargetSP = tostring(TargetResources[0].displayName)
| extend InitiatedBy = tostring(InitiatedBy.user.userPrincipalName)
| project TimeGenerated, InitiatedBy, TargetSP, RoleName, OperationName
```

### 3. 服务主体枚举检测

```kql
MicrosoftGraphActivityLogs
| where RequestMethod == "GET"
| where RequestUri has "/servicePrincipals"
| summarize RequestCount = count() by UserAgent, IPAddress, bin(TimeGenerated, 1h)
| where RequestCount > 10
| sort by RequestCount desc
```

### 4. 管理员同意绕过

```kql
AuditLogs
| where OperationName == "Consent to application"
| extend ConsentType = tostring(TargetResources[0].modifiedProperties[4].newValue)
| where ConsentType has "AllPrincipals"
| extend AppName = tostring(TargetResources[0].displayName)
| extend InitiatedBy = tostring(InitiatedBy.user.userPrincipalName)
| project TimeGenerated, InitiatedBy, AppName, ConsentType
```

### 5. OAuth 应用权限提升

```kql
AuditLogs
| where OperationName == "Add app role assignment to service principal"
| extend AppRoleValue = tostring(TargetResources[0].modifiedProperties[1].newValue)
| where AppRoleValue has_any ("RoleManagement.ReadWrite.Directory",
    "Application.ReadWrite.All", "AppRoleAssignment.ReadWrite.All",
    "Directory.ReadWrite.All", "Mail.ReadWrite")
| extend TargetApp = tostring(TargetResources[0].displayName)
| project TimeGenerated, TargetApp, AppRoleValue, CorrelationId
```

## 调查程序

### 步骤 1：识别受损的服务主体

```powershell
# 列出最近添加了凭据的服务主体
Connect-MgGraph -Scopes "Application.Read.All"

$suspiciousSPs = Get-MgServicePrincipal -All | ForEach-Object {
    $sp = $_
    $creds = Get-MgServicePrincipalPasswordCredential -ServicePrincipalId $sp.Id
    $recentCreds = $creds | Where-Object { $_.StartDateTime -gt (Get-Date).AddDays(-7) }
    if ($recentCreds) {
        [PSCustomObject]@{
            DisplayName = $sp.DisplayName
            AppId = $sp.AppId
            ObjectId = $sp.Id
            NewCredsCount = $recentCreds.Count
            LatestCredAdded = ($recentCreds | Sort-Object StartDateTime -Descending | Select-Object -First 1).StartDateTime
        }
    }
}
$suspiciousSPs | Sort-Object LatestCredAdded -Descending
```

### 步骤 2：查看服务主体角色分配

```powershell
# 检查特定服务主体的角色分配
$spId = "<service-principal-object-id>"
Get-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $spId | ForEach-Object {
    $resource = Get-MgServicePrincipal -ServicePrincipalId $_.ResourceId
    [PSCustomObject]@{
        AppRoleId = $_.AppRoleId
        ResourceDisplayName = $resource.DisplayName
        CreatedDateTime = $_.CreatedDateTime
    }
}
```

### 步骤 3：检查应用程序所有权

```powershell
# 列出所有应用程序的所有者（所有权 = 凭据控制权）
Get-MgApplication -All | ForEach-Object {
    $app = $_
    $owners = Get-MgApplicationOwner -ApplicationId $app.Id
    foreach ($owner in $owners) {
        [PSCustomObject]@{
            AppName = $app.DisplayName
            AppId = $app.AppId
            OwnerUPN = $owner.AdditionalProperties.userPrincipalName
            OwnerType = $owner.AdditionalProperties.'@odata.type'
        }
    }
} | Where-Object { $_.OwnerUPN -ne $null }
```

### 步骤 4：查看登录活动

```kql
AADServicePrincipalSignInLogs
| where ServicePrincipalId == "<target-sp-id>"
| project TimeGenerated, ServicePrincipalName, IPAddress, Location,
    ResourceDisplayName, Status.errorCode
| sort by TimeGenerated desc
```

## 预防控制

### 限制应用程序注册

```powershell
# 禁用用户注册应用程序的能力
Update-MgPolicyAuthorizationPolicy -DefaultUserRolePermissions @{
    AllowedToCreateApps = $false
}
```

### 配置应用同意策略

```powershell
# 要求对所有应用同意请求进行管理员审批
New-MgPolicyPermissionGrantPolicy -Id "admin-only-consent" `
    -DisplayName "Admin Only Consent" `
    -Description "Only admins can consent to applications"
```

### 使用 Microsoft Sentinel 分析规则进行监控

创建以下分析规则：
- 新服务主体凭据添加
- 向服务主体分配特权角色
- 批量服务主体枚举
- 向未知应用程序授予管理员同意
- 来自异常位置的服务主体登录

## MITRE ATT&CK 映射

| 技术 | ID | 描述 |
|------|----|------|
| 账户操控：附加云凭据（Account Manipulation: Additional Cloud Credentials） | T1098.001 | 向服务主体添加凭据 |
| 有效账户：云账户（Valid Accounts: Cloud Accounts） | T1078.004 | 使用受损的服务主体 |
| 账户发现：云账户（Account Discovery: Cloud Account） | T1087.004 | 枚举服务主体 |
| 窃取应用程序访问令牌（Steal Application Access Token） | T1528 | 通过服务主体窃取 OAuth 令牌 |

## 参考资料

- Splunk Detection: Azure AD Service Principal Abuse
- Semperis: Service Principal Ownership Abuse in Entra ID
- MITRE ATT&CK Cloud Matrix
- Microsoft: Securing service principals in Entra ID

