关于 investigating-azure-incidents
用于调查 Azure 资源与身份相关事件的安全技能。它将 Azure Activity Log 与 Entra 登录记录及资源诊断日志关联分析,并强调缺少数据平面日志不能证明数据未被访问。
主要功能
- 01核实日志来源与保留期限
- 02关联 Activity Log 与 Entra 身份
- 03使用 KQL 查询 AzureActivity
- 04搜寻角色与凭据变更
- 05调查托管身份令牌滥用
- 06遏制前保全证据
适用场景
- 01调查可疑的 Azure 部署
- 02追踪在资源外使用的托管身份令牌
- 03评估存储或 Key Vault 数据是否遭窃取
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ trilwu/ secskills --skill investigating-azure-incidents安装后在 Claude Code 中输入/investigating-azure-incidents确认已加载。
详细介绍
调查 Azure 事件
在 Azure 中,控制平面几乎会通过 Azure Resource Manager 记录所有操作,因此,事件调查要以发起调用的身份——用户、服务主体或托管身份——为锚点,结合活动日志和资源/数据平面日志进行还原。一个常见陷阱是,身份信息位于 Entra,而损害发生在订阅中:必须跨两个平面进行关联,因为活动日志告诉你对资源做了什么,而 Entra 登录日志告诉你谁持有令牌以及来自哪里。
适用场景
- 怀疑 Azure 资源遭到入侵——订阅、资源组或 VM 的表现仿佛由他人控制
- Azure 活动日志中出现异常条目——来自陌生调用方、IP 或区域的写入,或看起来像在进行枚举的
AccessDenied风暴 - Microsoft Defender for Cloud 发出警报——加密货币挖矿、异常资源部署、可疑登录或 IMDS 令牌窃取
- 托管身份或服务主体遭滥用——为应用或 VM 签发的令牌出现在本不该出现的位置
- VM 被用于加密货币挖矿——支出意外激增、使用 GPU/大型 SKU,或在你未运营的区域新建部署
- 存储帐户或 Key Vault 数据外泄——密钥重新生成、SAS 令牌签发、公共访问权限变更、磁盘快照共享或机密转储
不适用场景
- 遭到入侵的是 Entra 身份/M365 邮箱,而非 Azure 资源(不过通常两者都需要调查)——请使用
investigating-m365-entra - 事件发生在 AWS——请使用
investigating-aws-incidents - 事件发生在 Google Cloud——请使用
investigating-gcp-incidents - 你是针对租户的攻击者,而非响应人员——请使用
attacking-entra-id - 攻击跳板具体指向 AKS/Kubernetes——请使用
attacking-eks-gke-aks;若要防御集群,请使用defending-kubernetes - 一般性的 IR 流程和主机级响应——请使用
responding-to-incidents
日志来源及存放位置
查询之前,先确认你有哪些日志。缺少日志本身就是一项发现,而非跳过问题的理由。
| 来源 | 范围 | 保留期限(默认) | 记录内容 |
|---|---|---|---|
| Azure 活动日志 | 订阅控制平面 | 90 天,除非已导出 | 所有 ARM 写入/操作/删除:roleAssignments、runCommand、listKeys、部署 |
| 资源/诊断日志 | 每个资源的数据平面 | 启用之前不保留 | Blob 读取、Key Vault SecretGet、NSG 流量——仅当诊断设置将其发送到工作区时才会记录 |
| Log Analytics 工作区 | 日志发送到的任何位置 | 由工作区配置 | AzureActivity、AzureDiagnostics、StorageBlobLogs、AZKVAuditLogs 表 |
| Entra 登录/审核日志 | 租户身份平面 | 30 天(导出后可延长) | 谁对 SP/MI 进行了身份验证、来自哪里、CA/MFA 上下文、凭据添加记录 |
| Microsoft Sentinel | 所接收的所有数据 | 按表而异 | 对以上所有数据进行关联式狩猎、事件和监视列表 |
**陷阱:**活动日志是控制平面记录。数据平面操作——读取 Blob、获取 Key Vault 机密、查询 Cosmos DB——完全不会记录在活动日志中。只有在事件发生之前已为相应资源配置诊断设置,这些操作才会有记录。活动日志中没有记录,绝不能证明数据未遭触碰(参见“合理化借口”)。
在相信日志缺口之前,先确认实际记录了哪些内容:az monitor diagnostic-settings subscription list(活动日志是否导出到 90 天之外?)以及 az monitor diagnostic-settings list --resource <id>(此存储帐户/保管库是否将数据平面日志发送到任何位置?)。
首小时分诊
按顺序执行三项操作:确认调用者身份范围,拉取其近期活动, 先保留证据,再进行遏制。
确认调用者身份范围。 将报告——Defender 警报、
账单激增、可疑部署——对应到 Activity Log 中 caller /
identity 字段里的身份。该主体(UPN,或服务主体 / 托管身份对象 ID)是后续所有调查的锚点。
# Everything a specific caller did across the subscription control plane
az monitor activity-log list --caller [email protected] \
--start-time 2026-07-01T00:00:00Z -o json
# Who holds what right now -- role assignments are the escalation surface
az role assignment list --all --include-inherited \
--query "[?roleDefinitionName=='Owner' || roleDefinitionName=='User Access Administrator']" -o table
如果存在 Log Analytics 工作区,则从 KQL 拉取近期活动——完成初步查看后,它比 CLI 更快、信息也更丰富:
AzureActivity
| where TimeGenerated > ago(7d)
| where Caller == "[email protected]"
| project TimeGenerated, OperationNameValue, ActivityStatusValue,
CallerIpAddress, ResourceProviderValue, ResourceId, CorrelationId
| order by TimeGenerated asc
先保留证据,再进行遏制。 如果攻击者看到操作进行到一半时角色分配被撤销,就会销毁你尚未发现的持久化机制。除非损害正在持续发生,否则应先创建磁盘快照、导出相关日志、梳理持久化机制,然后一次性全部遏制。正在进行的挖矿或数据外泄是例外——先阻止损害,并接受相应取舍。
Activity Log 深入分析(KQL)
AzureActivity 表是权威的控制平面记录。请熟悉其字段:
OperationNameValue—— ARM 操作,例如Microsoft.Authorization/roleAssignments/write。这是你的搜寻目标。Caller——发起调用的 UPN 或对象 ID。CallerIpAddress——托管身份的外部 IP。如果该身份本应仅从 Azure 内部发起调用,这就是 IMDS 窃取的特征(见下文)。ResourceProviderValue——Microsoft.Compute、Microsoft.Storage、Microsoft.KeyVault、Microsoft.Authorization。ActivityStatusValue——连续出现Failure(通常为AuthorizationFailed)意味着正在枚举:攻击者在摸清被盗主体可以访问哪些资源。CorrelationId——关联同一逻辑操作的子操作;以此为线索展开单个可疑事件,查看完整操作序列。
// Enumeration storm -- authorization failures by operation
AzureActivity
| where TimeGenerated > ago(7d)
| where ActivityStatusValue == "Failure"
| summarize n = count() by Caller, OperationNameValue, CallerIpAddress
| order by n desc
// Expand one event's full correlated sequence
AzureActivity
| where CorrelationId == "<correlation-id>"
| project TimeGenerated, OperationNameValue, ActivityStatusValue, ResourceId
| order by TimeGenerated asc
需要搜寻的典型攻击者操作
在时间线中搜索以下 OperationNameValue 模式——几乎所有 Azure 入侵都会呈现这些特征。
- 通过角色分配提升权限——
Microsoft.Authorization/roleAssignments/write授予 Owner、 Contributor 或 User Access Administrator(UAA 可以为自己授予任意权限)。留意通过看似无害的名称隐藏*操作的自定义角色创建(Microsoft.Authorization/roleDefinitions/write)。 - 向服务主体或应用添加凭据——此操作在 Entra 中进行,而非 Activity Log:向应用注册添加新密钥或证书,会带来不依赖 MFA 的持久访问权限。请关联 Entra 审计日志(“Add service principal credentials” / “Update application - Certificates and secrets management”)。
- 滥用托管身份令牌——为受入侵 VM 的系统分配托管身份签发的令牌被用于调用 ARM。托管身份的对象 ID 会显示为来自异常
CallerIpAddress的Caller。 - 在 VM 上执行代码——
Microsoft.Compute/virtualMachines/runCommand/action和自定义脚本扩展(Microsoft.Compute/virtualMachines/extensions/write,安装CustomScript)可在无需 RDP/SSH 的情况下,以 SYSTEM/root 身份运行攻击者代码。 - 计算 / 无服务器持久化——
Microsoft.Web/sites(App Service / Functions)、Automation Runbooks 和 Logic Apps,可被用作定时后门,重新签发凭据或再次授予角色。 - 资源级 RBAC 后门——将角色分配限定在单个存储帐户或保管库,而非订阅级别;在顶层审查中很容易漏掉。
AzureActivity
| where TimeGenerated > ago(14d)
| where OperationNameValue has_any (
"roleAssignments/write", "roleDefinitions/write",
"runCommand/action", "virtualMachines/extensions/write")
| project TimeGenerated, Caller, CallerIpAddress, OperationNameValue, ResourceId
| order by TimeGenerated asc
CLI 等效命令可筛选相同的操作:
az monitor activity-log list --start-time <t> --query "[?contains(operationName.value,'roleAssignments/write')]"。
身份平面关联
活动日志会记录主体,但不会记录主体背后的人。将服务主体或托管标识的对象 ID映射回 Entra,以查看身份验证上下文——这是跨平面的步骤,通常意味着打开 investigating-m365-entra。
// Where did this service principal / managed identity actually sign in from?
AADServicePrincipalSignInLogs
| where ServicePrincipalId == "<sp-or-mi-object-id>"
| where TimeGenerated > ago(30d)
| project TimeGenerated, AppId, ServicePrincipalName, IPAddress,
ResourceDisplayName, ResultType
| order by TimeGenerated asc
// For an interactive user: sign-ins around the abusive Activity Log calls
SigninLogs
| where UserPrincipalName == "[email protected]"
| project TimeGenerated, IPAddress, Location, AppDisplayName,
ConditionalAccessStatus, AuthenticationRequirement, ResultType
检查条件访问状态,以及 MFA 是否确实通过验证——服务主体会完全绕过交互式 CA,这正是攻击者转而使用 SP/MI 凭据的原因。
托管标识与 SSRF 凭据窃取
这相当于 Azure 版的 AWS IMDS 窃取:攻击者通过 SSRF 或在 VM / App Service 上取得立足点,读取实例元数据服务以窃取托管标识的令牌,然后在其他地方使用。
http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://management.azure.com/
其特征十分明显:托管标识的调用会出现在 AzureActivity 中,而 CallerIpAddress 不是该资源自己的出站 IP。令牌是为该资源签发的,因此,任何来自无关或外部 IP 的调用都意味着令牌已离开该资源。
AzureActivity
| where TimeGenerated > ago(7d)
| where Caller == "<managed-identity-object-id>"
| summarize ops = count() by CallerIpAddress, OperationNameValue
| order by ops desc // flag IPs that are not the VM's egress
将窃取时段与 NSG 流日志(如已启用)关联,查找出站 SSRF 和令牌重用来源。在 App Service 上,令牌终结点使用带标头密钥的 IDENTITY_ENDPOINT,而非 169.254.169.254——同样适用资源外使用的判断逻辑。
Defender for Cloud 警报分诊
Defender for Cloud 是调查的起点,而非调查本身。每条警报都对应一个假设,需要通过活动日志和诊断日志加以确认。
| 警报(代表性示例) | 含义 |
|---|---|
加密货币挖矿 / Digital currency mining 行为 | VM 正在与矿池通信——一个拥有部署权限的主体已遭入侵。 |
异常资源部署 / 类似 RunInstances 的异常行为 | 攻击者正在启动计算资源,通常位于未使用的区域。 |
| 可疑登录 / 从 Tor 或已知恶意 IP 访问 | 被盗主体从攻击者基础设施发起了调用。 |
| 托管标识 / 元数据凭据外泄 | 上述 IMDS 窃取——确认是否存在资源外令牌使用。 |
| 从异常位置访问存储帐户 / Key Vault | 从意外地理位置进行的数据平面访问。 |
az security alert list -o table
az security alert show --location <loc> -n <alert-name> -g <rg>
即使警报时间已超出活动日志的 90 天保留窗口,其中仍会包含主体和 IP——即使原始事件已过期,也要据此进行关联调查。
数据窃取检测
- 存储帐户密钥重新生成——
Microsoft.Storage/storageAccounts/listKeys/action和regenerateKey会向攻击者提供一个完全访问密钥,可在 RBAC 和后续活动日志之外使用。 - SAS 令牌签发——
listAccountSas/listServiceSas会生成有时限的外泄 URL,不会留下逐对象的控制平面记录。 - Blob 外泄——仅在启用数据平面诊断日志时可见:
StorageBlobLogs会显示按调用方统计的GetBlob数量。 - 容器的公共访问权限变更——将容器或帐户设置为允许匿名 / Blob 公共访问,是外泄准备行为。
- 磁盘快照导出 / 共享——
Microsoft.Compute/snapshots/write,随后执行/beginGetAccess/action,会为完整磁盘映像生成 SAS 下载 URL。 - Key Vault 密钥转储——通过
Microsoft.KeyVault/vaults/read(VaultGet)枚举,然后读取数据平面的SecretGet(仅当启用日志记录时,记录在AzureDiagnostics/AZKVAuditLogs中)。
// Control-plane data-theft indicators
AzureActivity
| where TimeGenerated > ago(14d)
| where OperationNameValue has_any (
"storageAccounts/listKeys", "storageAccounts/regenerateKey",
"listAccountSas", "listServiceSas",
"snapshots/write", "snapshots/beginGetAccess")
| project TimeGenerated, Caller, CallerIpAddress, OperationNameValue, ResourceId
// Key Vault data-plane reads -- only present if diagnostics were on beforehand
AzureDiagnostics
| where ResourceProvider == "MICROSOFT.KEYVAULT"
| where OperationName in ("SecretGet", "KeyGet", "VaultGet")
| project TimeGenerated, CallerIPAddress, identity_claim_upn_s, OperationName, id_s
攻击者尝试的反取证手段
能力较强的攻击者会试图蒙蔽你。需要追查的关键操作及其应对方式如下:
- 删除诊断设置 —
Microsoft.Insights/diagnosticSettings/delete会阻止数据平面日志发送到工作区。 - 移除/缩小 Activity Log 导出范围 — 删除订阅级诊断设置,该设置负责将 Activity Log 发送到工作区或存储。
- 删除资源 — 删除 VM、快照或存储帐户,以销毁取证材料及其日志。
应对方式与 AWS 组织跟踪类似:将数据在租户级别导出到遭到入侵的主体无法访问的集中式锁定目标——导出到不可变存储(WORM/法律保留)Blob,或导入到具有删除保护和资源锁的隔离工作区中的 Sentinel。导出数据不可变时,攻击者自己的 diagnosticSettings/delete 调用会先被记录到那里,然后才会生效,因此清理行为会成为证据,而非造成盲区。如果你没有这项保护,请将盲区记录为范围界定的限制。
AzureActivity
| where TimeGenerated > ago(14d)
| where OperationNameValue has_any (
"diagnosticSettings/delete", "Microsoft.Insights/diagnosticSettings/write")
| project TimeGenerated, Caller, CallerIpAddress, OperationNameValue, ResourceId
证据保全
-
对涉及的 VM 磁盘创建快照,再进行任何操作,并锁定快照,防止其被删除。
az snapshot create -g <rg> -n IR-2026-042-osdisk \ --source <os-disk-id> --tags case=IR-2026-042 legal-hold=true az lock create --name IR-2026-042-hold --lock-type CanNotDelete \ --resource-group <rg> --resource-name IR-2026-042-osdisk \ --resource-type Microsoft.Compute/snapshots -
导出相关时间段的 Activity Log,趁其尚未超出 90 天的保留期——运行 CLI/KQL,并将 JSON 保存到受保护的锁定存储中。
-
对证据材料应用资源锁/法律保留;如果事件可能演变为监管调查或诉讼事项,请在收集证据前联系法务。
遏制
完成范围界定后,一次性全部处理。部分遏制会向攻击者发出警报。
-
禁用或轮换遭到入侵的主体。 如果是服务主体/应用,请将其禁用并轮换凭据:
az ad sp update --id <app-id> --set accountEnabled=false # remove attacker-added secrets/certs az ad app credential reset --id <app-id>如果是用户,请在 Entra 中禁用帐户并强制重置密码,然后撤销会话(见下文)。
-
撤销攻击者授予的角色分配:
az role assignment delete --assignee <object-id> \ --role Owner --scope /subscriptions/<sub-id> -
撤销会话/刷新令牌,使现有令牌失效(这是 Entra 操作,可使用
Revoke-MgUserSignInSession/az ad user ...),因为仅禁用帐户仍会让已签发的令牌在最长一小时内保持有效。 -
隔离 VM,将其 NSG 替换为拒绝所有出站流量的规则,而不是删除 VM,这样磁盘和内存数据仍可用于分析(
az network nsg rule create ... --access Deny --direction Outbound --protocol '*')。 -
轮换存储密钥并重新生成 SAS——更新两个密钥会立即使所有未过期的 SAS 和访问密钥失效:
az storage account keys renew --account-name <acct> -g <rg> --key primary az storage account keys renew --account-name <acct> -g <rg> --key secondary -
移除攻击者的持久化机制——删除后门 Functions/App Service、Automation runbook、自定义角色和资源范围角色分配,但必须先将其记录在案。
调查时可使用 Azure CLI 和 Log Analytics/Sentinel 中的 KQL;使用 MicroBurst 和 ROADtools 了解攻击者会采用的 TTP(以及每种 TTP 会留下什么痕迹);如果 Azure 事件延伸到 M365,则使用 Microsoft 的 Unified Audit 进行关联分析。
应当摒弃的错误辩解
- “活动日志中什么都没有,所以没有数据被访问。” 活动日志只记录控制平面操作。Blob 读取、机密提取和数据库查询属于数据平面操作,除非事先启用了诊断日志记录,否则这些操作不可见。日志中没有记录,并不能证明没有数据外泄。
- “Defender for Cloud 没有发出警报,所以没有发生入侵。” Defender 只覆盖部分行为,并且依赖正确的计划和覆盖范围。没有发现结果,并不能证明没有问题——活动日志时间线才是权威依据。
- “我们已禁用该用户,所以账号已被控制。” 已签发的访问令牌最长仍可有效一小时;攻击者创建的任何服务主体凭据或角色分配也会在禁用后继续存在。请撤销会话,并审查所有持久化机制。
- “这是托管身份,只能从 Azure 内部使用。” 这是设计目标,并非保证。SSRF/IMDS 窃取会将令牌从资源中带走;资源外的
CallerIpAddress正是需要搜寻的特征。 - “该服务主体只有 Contributor 权限,没有 Owner 权限。” Contributor 可以在 VM 上运行
runCommand、读取存储密钥并生成 SAS 令牌——无需触碰角色分配,也能执行代码并窃取数据。追踪该角色能够访问的范围,不要想当然。 - “这只是挖矿,优先级不高。” 挖矿意味着拥有部署权限的主体已遭入侵——同一访问权限也可能被用来外泄数据,或通过
roleAssignments/write提升权限。挖矿只是可见症状,并非事件的全部范围。 - “日志只保留 90 天,所以入侵是在最近 90 天内开始的。” 这是活动日志的默认保留期限,也是你能看到的范围,并非攻击者的时间线。请将此记录为范围界定缺口,并检查工作区 / 不可变导出中是否有更长的保留期。
参考资料
investigating-m365-entra-- 配套的身份平面技能;打开它,将 SP/MI 与 Entra 登录和审计记录关联起来investigating-aws-incidents-- 事件发生在 AWS 时使用的配套云端事件响应技能attacking-entra-id-- 攻击视角;了解这些租户/订阅 TTP 的执行方式attacking-eks-gke-aks-- 当攻击具体转向 AKS / Kubernetes 时使用responding-to-incidents-- 通用事件响应流程、证据处理和主机级响应reporting-security-findings-- 组织事件叙述和交付内容- Azure CLI(
az)-- 操作控制平面并提取活动日志 - Log Analytics / Microsoft Sentinel 中的 KQL -- 查询
AzureActivity、AzureDiagnostics和登录表 - Microsoft Defender for Cloud -- 警报来源和发现类型参考
- MicroBurst 和 ROADtools -- 模拟并了解 Azure/Entra 攻击 TTP,以及各类攻击在日志中留下的记录