| name | detecting-azure-service-principal-abuse |
| description | 在 Microsoft Entra ID 环境中检测和调查 Azure 服务主体滥用,包括权限提升、凭据入侵、管理员同意绕过和未授权枚举。 |
| domain | cybersecurity |
| subdomain | cloud-security |
| tags | ["azure","entra-id","service-principal","privilege-escalation","credential-abuse","detection","splunk","sentinel"] |
| version | 1.0 |
| author | mahipal |
| license | Apache-2.0 |
检测 Azure 服务主体滥用
概述
Azure 服务主体是应用程序、服务和自动化工具用于访问 Azure 资源的身份对象。攻击者利用服务主体进行权限提升、横向移动和持久访问。关键滥用模式包括:向现有主体添加凭据、分配特权角色、绕过管理员同意以及枚举服务主体以寻找攻击路径。应用程序所有权授予管理凭据和配置权限的能力,从而产生隐藏的权限提升路径。
前置条件
- 具有 Microsoft Entra ID P2 许可证的 Azure 订阅
- 访问 Azure AD 审计日志和登录日志的权限
- 用于基于 SIEM 检测的 Microsoft Sentinel 或 Splunk
- 用于调查的 Microsoft Graph API 权限
- 至少具有全局读取者或安全读取者角色
关键滥用模式
1. 向服务主体添加新凭据
攻击者添加新的客户端密钥或证书以获得持久访问:
检测查询(KQL - Sentinel):
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):
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. 向服务主体分配特权角色
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. 服务主体枚举检测
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. 管理员同意绕过
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 应用权限提升
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:识别受损的服务主体
# 列出最近添加了凭据的服务主体
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:查看服务主体角色分配
# 检查特定服务主体的角色分配
$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:检查应用程序所有权
# 列出所有应用程序的所有者(所有权 = 凭据控制权)
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:查看登录活动
AADServicePrincipalSignInLogs
| where ServicePrincipalId == "<target-sp-id>"
| project TimeGenerated, ServicePrincipalName, IPAddress, Location,
ResourceDisplayName, Status.errorCode
| sort by TimeGenerated desc
预防控制
限制应用程序注册
# 禁用用户注册应用程序的能力
Update-MgPolicyAuthorizationPolicy -DefaultUserRolePermissions @{
AllowedToCreateApps = $false
}
配置应用同意策略
# 要求对所有应用同意请求进行管理员审批
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