用PowerShell和Microsoft Graph挖出M365僵尸账户与多余许可

本文深入解析如何使用Microsoft Graph与PowerShell批量挖掘M365/Entra用户信息,包括账户启用状态、最后登录时间和许可证分配。通过这些命令可快速发现僵尸账户与多余许可证,同时提醒安全团队警惕类似的Graph API侦察行为。

🔍 Low 其他

本文介绍安全管理员如何借助Microsoft Graph PowerShell模块批量拉取Entra/M365用户属性、登录活动和许可证分配,识别已禁用、长期未登录的账户及未使用的许可证。攻击者也可以利用相同API进行高效侦察,因此需要严格管控Graph API权限并开启审计日志。

来源:SANS ISC | 2026-08-20 | 原文链接

🔍 关键发现

  • Get-MgUser -All 可绕过默认100条对象限制,批量导出用户基础属性;SignInActivity 需要显式请求才能返回最后登录时间。
  • 许可证原始返回值是SKU GUID,必须通过 Get-MgUserLicenseDetail 映射为 SkuPartNumber(如SPE_E3、Microsoft_365_Copilot)才能准确识别订阅。
  • 将用户属性、最后登录时间与许可证信息输出到CSV后,可以快速定位禁用账户、非活跃账户和可回收许可证,属于典型的资产梳理与数据挖掘场景。

⚔️ 攻击链分析

无固定攻击链;但若攻击者获得高权限Graph令牌,可按以下步骤进行侦察:1. Connect-MgGraph申请User.Read.All范围;2. Get-MgUser -All批量拉取用户列表与SignInActivity;3. Get-MgUserLicenseDetail提取可读许可证名;4. 将结果整理成CSV用于后续钓鱼、横向移动或资源滥用。

🚩 失陷指标 (IOC)

  • 暂无特定IOC;重点关注非管理员身份频繁调用Get-MgUser/Get-MgUserLicenseDetail的行为
  • 可疑PowerShell命令:Connect-MgGraph -Scopes User.Read.All; Get-MgUser -All -Property SignInActivity

🛡️ 缓解建议

  • ✅ 遵循最小权限原则,为Graph API应用/用户仅分配必要范围,避免使用User.Read.All等高风险权限。
  • ✅ 启用Entra登录日志和Graph API审计日志,监控批量用户查询、许可证枚举及结果导出行为。
  • ✅ 定期审计已禁用账户、长期未登录账户和未分配许可证,及时停用旧账户并回收许可。

涉及漏洞:[]


⚠️ 本文仅供安全研究与学习,IOC 信息请勿用于非法目的。

🤖 常见问题解答(FAQ)

❓ 为什么Get-MgUser默认只返回100个对象?

因为Graph API分页默认每页100条,PowerShell SDK在无-All参数时只取第一页;使用-All可自动遍历所有分页,获取完整结果。

❓ 如何将许可证SKU ID变成可读名称?

使用 Get-MgUserLicenseDetail -UserId <用户> 并读取 SkuPartNumber 属性;在Select-Object中通过计算属性为每个用户执行一次即可得到人类可读的许可证名称。

❓ 攻击者使用该技术需要什么权限?

至少需要User.Read.All或Directory.Read.All才能批量枚举用户、登录活动及许可证;安全团队应通过条件访问和Privileged Identity Management严格限制此类权限授予。

[!] CONTACT_CHANNELS

如需商务合作、技术咨询或漏洞反馈,请通过以下离岸节点联系作者。

> PING_AUTHOR (@A1RedTeam)