Full Disclosure, GraphGhost: Are You Afraid of Failed Logins?
作者:@nyxgeek
发布日期:2025年6月5日
分类:漏洞评估
背景
Entra ID(前身为Azure AD)是Azure的身份认证核心组件,负责管理用户、应用程序和权限。用户和应用程序连接Azure服务时会生成登录日志,这些日志是Azure管理员的唯一信息来源。
虽然云服务简化了IT管理,但也带来了依赖性问题:管理员完全依赖云服务商提供的日志。当云服务商的逻辑出现错误时,管理员可能无法获取日志或获得错误日志——不知道真相和被提供错误信息,哪个更糟?
此类日志逻辑问题并非首次出现。GraphNinja(一种完整的日志绕过漏洞)曾在Azure中存在多年(详见博客文章)。该漏洞的原理是:通过Graph认证时,若将认证指向其他租户而非目标租户或通用端点,认证尝试不会记录在任何租户的登录日志中。GraphNinja已被修复,失败认证现在会显示在目标租户的日志中。
今天我将分享GraphGhost漏洞,与GraphNinja类似,但攻击者可在日志显示失败的同时确认密码是否有效。
工作原理
向Graph提交登录尝试时,需提供多个字段(取决于认证方法),例如:
- 用户名
- 密码
- 资源ID
- 客户端ID
提交正确值会获得令牌;提交错误值会收到错误信息——微软冗长的错误信息再次给防御者带来困难,却帮助了攻击者。
微软验证登录的有效性遵循以下顺序:
- 目标租户是否存在?
- 目标域是否存在?
- 用户(UPN)是否存在?
- 密码是否有效?
这是日常遇到的主要步骤。登录要么成功,要么因用户名或密码错误而失败。
但密码验证之后的步骤呢?其他提交的字段(如客户端ID、UPN是否在正确租户、请求资源是否有效)会在密码验证通过后检查。下图展示了已映射的检查流程:
图3 - Graph认证操作顺序
问题
任何密码验证后的检查失败都会导致整个登录被视为失败。但由于我们了解操作顺序,可以知道哪些错误代码仅在密码验证成功后出现。
下图再次展示了流程,并标明了密码验证后的检查:
图4 - 此处有龙!
红线以下的任何错误代码都表示已找到有效密码。
发起请求很简单,以下是两个执行登录尝试的curl请求示例:
图5 - 使用Curl演示
日志中的失败记录如下:
图6 - GraphGhost登录日志
图7 - GraphGhost - 失败登录列表
或另一个示例:
图8 - GraphGhost - 失败登录详情
当认证通过后发生任何失败时,登录状态会显示为“失败”。即使查看认证详情标签,也没有任何指示:
图9 - GraphGhost - 认证详情
注意密码认证的“成功”列显示为false。管理员审查此日志时,无法发现凭据已泄露。
需要明确的是:这仅让攻击者知道密码有效,但未获得有效令牌。此类攻击的目的是,一旦获知账户密码,攻击者可更精准地使用该凭据——例如在目标区域通过VPN登录,或在正常工作时间登录;攻击者还可使用受害者企业凭据 targeting Wi-Fi,完全绕过MFA。无论如何,管理员对密码泄露一无所知绝非好事。
修复
我曾希望微软借此机会反思其冗长错误代码的使用——它们帮助了谁?管理员本就可以查看实际日志和生成的错误代码;唯一受益者是攻击者。
但遗憾的是,微软未采取此路线:
图10 - 经典的微软
截至2025年4月11日,此问题已修复。微软最终更新了认证详情,以指示密码已成功验证——虽是小改动,但至关重要:
图11 - GraphGhost - 旧版vs新版
时间线
- 2024年12月17日 - 向微软报告
- 2024年12月17日 - 微软开案
- 2025年2月21日 - 确认并授予奖金
- 2025年4月11日 - 微软修复
思考
如前所述,我曾希望微软借此推动整体安全提升。此问题并不难发现,我困惑于GraphGhost及早期的GraphNinja日志问题为何未被微软自身安全团队识别。与GraphNinja一样,微软未公开提及此漏洞。作为自身的CVE编号机构(CNA),微软可决定什么是漏洞,以及公众应知悉什么。