mcpField
I
investigating-m365-entra

investigating-m365-entra

github.com/trilwu安全17747 次安装12 周前更新
代码仓库

关于 investigating-m365-entra

面向事件响应人员的 M365 和 Entra ID 调查指南,适用于邮箱失陷、账户入侵及 OAuth 授权滥用等事件。它结合审计与登录日志分析、持久化检查和时间线构建,并强调日志留存与许可造成的可见性限制。

主要功能

  1. 01梳理审计、登录及身份日志
  2. 02分析异常登录与 MFA 活动
  3. 03调查 OAuth 授权和应用凭据
  4. 04检查邮箱规则及转发设置
  5. 05关联事件并构建时间线

适用场景

  1. 01调查商业邮件失陷和未授权邮箱访问
  2. 02排查可疑 OAuth 授权及持久化
  3. 03分析 Entra ID 登录异常与 MFA 篡改
  4. 04在遏制前保全邮箱证据

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/trilwu/secskills --skill investigating-m365-entra

安装后在 Claude Code 中输入/investigating-m365-entra确认已加载。

详细介绍

调查 M365 和 Entra ID

M365/Entra 调查与本地 DFIR 不同——没有磁盘映像、内存转储,也无法自行收集事件日志。所有数据都来自对 Microsoft 日志存储的 API 查询,其中有些日志需要 E5 许可证或高级审核才能保留所需数据。了解有哪些日志、缺少哪些日志,以及日志保留时长,是调查工作的一半。

适用场景

  • 商务电子邮件泄露(BEC)——未经授权访问邮箱、设置转发或冒充他人
  • Entra ID 帐户遭入侵——可疑登录、令牌重放、凭据填充
  • 可疑的 OAuth 同意授权——权限过大的第三方应用
  • 邮箱规则遭篡改——收件箱规则隐藏攻击者通信或转发邮件
  • Azure AD 登录异常——不可能的旅行、旧式身份验证、匿名网络
  • 条件访问或 MFA 遭篡改——策略变更、MFA 疲劳攻击

不适用场景

  • 更广泛的事件响应方法——使用 responding-to-incidents
  • 对 Entra ID 进行攻击测试——使用 attacking-entra-id
  • Azure 资源/订阅遭入侵——使用 investigating-azure-incidents
  • AWS 遭入侵——使用 investigating-aws-incidents
  • 其他云基础设施(GCP、攻击测试)——使用 exploiting-cloud-platforms
  • 根据调查发现构建检测规则——使用 engineering-detections

日志概况与保留期限

查询任何数据之前,先确认你有哪些日志,以及日志可追溯到多久以前。 缺少日志本身就是一项调查发现,而不是跳过问题的理由。

日志源保留期限(默认)许可证要求关键操作
统一审核日志(UAL)默认 180 天;E5 许可用户为 1 年MailItemsAccessed 需要 E5MailItemsAccessed、New-InboxRule、Set-Mailbox、同意授权
Entra ID 登录日志30 天导出到 Log Analytics 可延长保留期限登录事件、CA 评估、MFA 结果
Entra ID 审核日志30 天无角色分配、应用注册、凭据变更
Identity Protection30 天需要 P2风险登录、风险检测
Defender for Cloud Apps180 天需要 MDCA 许可证活动日志、OAuth 应用清单
邮箱审核日志90 天(默认启用)MailItemsAccessed 需要 E5邮件访问、代表发送、委派操作

在根据日志缺口得出任何结论前,先确认保留期限。 自 2023 年 10 月 17 日起,Audit (Standard) 的默认期限为 180 天,而不是许多较旧资料仍提到的 90 天——在该日期之前生成的记录仍按旧的 90 天期限保留。只有持有 E5(或 Purview Audit 附加许可证)的用户默认可保留一年,即便如此,也仅适用于 Exchange、SharePoint、OneDrive 和 Entra ID。因此,E3 租户的 UAL 保留期限为 180 天,且没有 MailItemsAccessed,也无法在产品内延长保留期限。

保留期限按用户许可证确定,而非按租户确定:在混合许可证的租户中,同一查询可能返回 E5 调查对象一整年的历史记录,以及其相邻 E3 帐户 180 天的历史记录。在将空结果解读为“没有活动”之前,请确认受调查帐户的许可证。 明确说明这一缺口。检查 SIEM 或 Log Analytics 中是否有延长保留的数据。

统一审核日志(UAL)

UAL 是数据最丰富的单一来源。每项调查都从这里开始。

Connect-ExchangeOnline -UserPrincipalName [email protected]

# Basic search -- always constrain by date and user
Search-UnifiedAuditLog -StartDate "2026-07-01" -EndDate "2026-07-20" `
  -UserIds [email protected] -ResultSize 5000

# Search specific operations
Search-UnifiedAuditLog -StartDate "2026-07-01" -EndDate "2026-07-20" `
  -Operations "New-InboxRule","Set-InboxRule","Set-Mailbox","Add-MailboxPermission" `
  -ResultSize 5000

# MailItemsAccessed (E5 only)
Search-UnifiedAuditLog -StartDate "2026-07-01" -EndDate "2026-07-20" `
  -Operations MailItemsAccessed -UserIds [email protected] -ResultSize 5000

# Export -- AuditData field is JSON, expand it
Search-UnifiedAuditLog -StartDate "2026-07-01" -EndDate "2026-07-20" `
  -UserIds [email protected] -ResultSize 5000 |
  Select-Object CreationDate, UserIds, Operations,
    @{N='AuditData';E={$_.AuditData | ConvertFrom-Json | ConvertTo-Json -Depth 10}} |
  Export-Csv -Path .\ual_export.csv -NoTypeInformation
关键 UAL 操作
操作重要性
MailItemsAccessedBind = 读取单个项目,Sync = 批量下载。批量同步是 BEC 数据外传的指标。
New-InboxRule / Set-InboxRule隐藏回复或转发邮件的规则。检查关键词目标:“invoice”“payment”“security”。
Set-MailboxForwardingSMTPAddress 或 ForwardingAddress 已更改——静默外部转发。
Add-MailboxPermissionFullAccess 或 SendAs 委派——持久化。
Consent to applicationOAuth 授权。AuditData 包含权限信息。
Add service principal credentials在应用注册中添加新的密钥/证书。
HardDelete / SoftDelete销毁证据——攻击者删除已发送邮件。

UAL 每次查询最多返回 50,000 条结果。如果达到上限,请缩小日期范围。

Entra ID 登录分析

查询登录日志并查找:

  • 不可能的旅行——在不可能的时间范围内出现在相距遥远的位置
  • 匿名 IP——Tor 出口节点、VPN 服务、已知匿名代理
  • 旧式身份验证——IMAP、POP3、SMTP AUTH 可绕过 MFA,除非 CA 阻止
  • 令牌重放——同一关联 ID 来自不同源 IP
  • 异常用户代理——Python requests、PowerShell 访问 OWA 或 Graph
// Sign-ins from the compromised account
SigninLogs
| where UserPrincipalName == "[email protected]"
| where TimeGenerated > ago(30d)
| project TimeGenerated, AppDisplayName, IPAddress, Location,
    ClientAppUsed, ResultType, AuthenticationRequirement, MfaDetail, RiskState
| order by TimeGenerated asc

// Legacy auth sign-ins that bypass MFA
SigninLogs
| where ClientAppUsed in ("IMAP4", "POP3", "SMTP", "Exchange ActiveSync",
    "MAPI Over HTTP", "Outlook Anywhere", "Exchange Web Services")
| where ResultType == 0
| summarize count() by UserPrincipalName, ClientAppUsed, IPAddress
MFA 分析
  • ResultType 50074——需要 MFA,但未完成
  • ResultType 50076——已完成 MFA(攻击者拥有验证因子或使用了 MFA 疲劳攻击)
  • AuthenticationRequirement = singleFactorAuthentication——不需要 MFA(CA 配置缺口)
  • 检查 MfaDetail——在反复提示后接受推送通知,说明可能存在 MFA 疲劳攻击

OAuth 和授权授予调查

非法授权授予是 M365 入侵中最容易被漏掉的持久化手段。

Connect-MgGraph -Scopes "Application.Read.All","Directory.Read.All"

# Delegated permission grants -- look for Mail.Read, Files.ReadWrite, etc.
Get-MgOauth2PermissionGrant -All | Where-Object {
    $_.Scope -match "Mail.Read|Mail.ReadWrite|Files.ReadWrite|User.Read.All"
} | Format-Table ClientId, ConsentType, Scope, PrincipalId

# Application permission assignments
Get-MgServicePrincipal -All | ForEach-Object {
    $sp = $_
    Get-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $sp.Id -ErrorAction SilentlyContinue |
    Select-Object @{N='AppName';E={$sp.DisplayName}}, @{N='AppId';E={$sp.AppId}},
                  PrincipalDisplayName, @{N='Created';E={$_.CreatedDateTime}}
}
权限风险
Mail.Read / Mail.ReadWrite邮件数据外传。对未知应用而言很可疑。
Files.ReadWrite.All完全访问 SharePoint/OneDrive。
Directory.ReadWrite.All可修改用户、组和角色。
full_access_as_appExchange 应用级邮箱访问——第三方几乎从不会正当使用。

区分委派授权(单个用户的数据)和管理员授权(整个租户)。 向恶意应用授予管理员授权会暴露所有用户的数据。

邮箱规则取证

密码重置后,收件箱规则仍会保留。务必检查。

# Inbox rules
Get-InboxRule -Mailbox [email protected] |
  Select-Object Name, Enabled, MoveToFolder, DeleteMessage, ForwardTo,
    RedirectTo, MarkAsRead | Format-List

# Mailbox-level forwarding
Get-Mailbox -Identity [email protected] |
  Select-Object ForwardingSMTPAddress, ForwardingAddress, DeliverToMailboxAndForward

# All mailboxes with external forwarding
Get-Mailbox -ResultSize Unlimited |
  Where-Object { $_.ForwardingSMTPAddress -ne $null } |
  Select-Object UserPrincipalName, ForwardingSMTPAddress

# Transport rules
Get-TransportRule | Where-Object { $_.RedirectMessageTo -or $_.BlindCopyTo } |
  Select-Object Name, State, RedirectMessageTo, BlindCopyTo

# Delegate access
Get-MailboxPermission -Identity [email protected] |
  Where-Object { $_.User -ne "NT AUTHORITY\SELF" -and -not $_.IsInherited }

危险信号:针对关键词(“security”“password”“MFA”)的规则、删除邮件或将邮件移至 RSS Feeds / Conversation History 的规则、名称为空的规则、转发至免费电子邮件服务提供商的规则,以及在首次可疑登录后创建的规则。

eDiscovery 和内容搜索

Connect-IPPSSession -UserPrincipalName [email protected]

# Preserve mailbox content -- do this early
Set-Mailbox -Identity [email protected] -LitigationHoldEnabled $true

# Search outbound mail from compromised account
New-ComplianceSearch -Name "IR-2026-042 Outbound" `
  -ExchangeLocation [email protected] `
  -ContentMatchQuery "sent>=2026-07-01 AND sent<=2026-07-20"
Start-ComplianceSearch -Identity "IR-2026-042 Outbound"

请在保留策略生效前设置诉讼保全,否则攻击者可能清除证据。

Azure AD 审计日志分析

// Role assignments
AuditLogs
| where OperationName == "Add member to role"
| extend TargetUser = tostring(TargetResources[0].userPrincipalName),
         RoleName = tostring(TargetResources[0].modifiedProperties[1].newValue)
| project TimeGenerated, InitiatedBy, TargetUser, RoleName

// Service principal credential changes (persistence)
AuditLogs
| where OperationName in ("Add service principal credentials",
    "Update application - Certificates and secrets management")
| project TimeGenerated, InitiatedBy, TargetResources

// Conditional Access policy changes
AuditLogs
| where OperationName has "conditional access"
| project TimeGenerated, OperationName, InitiatedBy, Result

// MFA method changes (attacker registering their own factor)
AuditLogs
| where OperationName in ("User registered security info",
    "Admin registered security info", "User deleted security info")
| project TimeGenerated, OperationName, InitiatedBy, TargetResources

拥有 Application Admin 或 Global Admin 权限的攻击者可以向现有应用注册添加凭据,以实现持久化且不受 MFA 影响的访问:

Get-MgApplication -All | ForEach-Object {
    $app = $_
    $app.PasswordCredentials + $app.KeyCredentials | Where-Object {
        $_.StartDateTime -gt (Get-Date).AddDays(-30)
    } | Select-Object @{N='App';E={$app.DisplayName}}, @{N='AppId';E={$app.AppId}},
                      StartDateTime, EndDateTime
}

构建时间线

关联三种主要日志来源:

Sign-in logs  -->  When and where the attacker authenticated
Audit logs    -->  Configuration changes they made
UAL           -->  Data they accessed or modified
  1. 以最早的可疑登录为锚点
  2. 提取该用户前后 7 天的登录、UAL 和审计事件
  3. 合并为单一 UTC 时间线
  4. 查找以下模式:登录、侦察、持久化(规则/转发/OAuth)、目标行动
UTC Timestamp        | Source  | Event                                    | Detail
2026-07-12 08:41:22  | SignIn  | Sign-in from 198.51.x.x                  | Nigeria, no MFA
2026-07-12 08:42:05  | UAL     | MailItemsAccessed (Sync)                 | 847 items via Graph
2026-07-12 08:43:18  | UAL     | New-InboxRule "."                        | Delete "security alert"
2026-07-12 08:44:01  | UAL     | Set-Mailbox                              | ForwardingSMTPAddress set
2026-07-12 08:45:33  | UAL     | Consent to application                   | Mail.Read, Mail.Send
2026-07-12 09:12:44  | UAL     | Send (SendAs)                            | Invoice redirect to vendor

遏制

范围界定完成后同时执行。部分遏制会向攻击者发出警报。

# Revoke sessions and reset credentials
Revoke-MgUserSignInSession -UserId [email protected]
Update-MgUser -UserId [email protected] -PasswordProfile @{
    Password = (New-Guid).Guid + "!Aa1"; ForceChangePasswordNextSignIn = $true }

# Disable account if active compromise is ongoing
Update-MgUser -UserId [email protected] -AccountEnabled:$false

# Remove attacker inbox rules
Get-InboxRule -Mailbox [email protected] |
  Where-Object { $_.Name -match "^\.$|^$" -or $_.DeleteMessage -eq $true } |
  Remove-InboxRule -Confirm:$false

# Remove forwarding
Set-Mailbox -Identity [email protected] `
  -ForwardingSMTPAddress $null -ForwardingAddress $null `
  -DeliverToMailboxAndForward $false

# Remove unauthorized delegate access
Get-MailboxPermission -Identity [email protected] |
  Where-Object { $_.User -ne "NT AUTHORITY\SELF" -and -not $_.IsInherited } |
  ForEach-Object { Remove-MailboxPermission -Identity [email protected] `
    -User $_.User -AccessRights $_.AccessRights -Confirm:$false }

# Block malicious OAuth app
$sp = Get-MgServicePrincipal -Filter "appId eq '<malicious-app-id>'"
Update-MgServicePrincipal -ServicePrincipalId $sp.Id -AccountEnabled:$false
Get-MgOauth2PermissionGrant -Filter "clientId eq '$($sp.Id)'" |
  Remove-MgOauth2PermissionGrant

仅撤销会话并不足够——令牌可能仍然有效长达一小时。请禁用账户以立即锁定。遏制后,确认不存在转发、未知委派、未知 OAuth 授权、攻击者注册的 MFA 方法,以及在入侵时间段内创建的服务主体凭据。

需要纠正的错误认知

  • “我们重置了密码,所以账户安全了。” 刷新令牌、OAuth 授权、收件箱规则、邮件转发和委托访问权限都不会因密码重置而失效。请撤销会话,并审查所有持久化访问机制。
  • “我们只有 E3,所以无法调查邮件访问情况。” 虽然无法使用 MailItemsAccessed,但登录日志、UAL 操作、收件箱规则和邮件转发仍然可查。请说明这一限制,并利用现有信息开展调查。
  • “这次登录来自 VPN,所以可能是用户本人。” 请结合出口 IP、用户代理和时间,与已知模式进行比对。攻击者也会使用 VPN。
  • “Defender 没有触发任何警报,所以没有遭到入侵。” Defender 需要相应的许可证等级和策略配置。没有警报并不能证明没有入侵。
  • “这个 OAuth 应用只有委托权限。” 只要刷新令牌有效,委托权限就能提供持久访问,无需再次输入密码。
  • “我们封锁了该 IP,所以攻击者无法再登录。” 攻击者会更换 IP。请撤销令牌和凭据,而不只是封锁网络路径。
  • “日志只保留 30 天,所以入侵一定是在这段时间内开始的。” 这是你的可见范围限制,并不代表攻击者的活动时间线。请记录这一限制,并检查 SIEM 是否有更长的保留期限。

参考资料

  • responding-to-incidents -- 更全面的 IR 方法和证据处理
  • attacking-entra-id -- Entra ID 攻击技术,有助于了解攻击者的手段
  • exploiting-cloud-platforms -- M365 之外的云基础设施攻击
  • engineering-detections -- 根据调查结果构建检测规则
  • hunting-threats -- 主动搜寻 M365 和 Entra ID 遥测数据中的威胁

评论

支持 Markdown