Netwrix 1Secure 提供跨数据和身份的统一可见性——免费试用14天,享有完全访问权限。开始免费试用

资源中心博客

错误代码是线索

错误代码是线索

Oct 2, 2026

Proofpoint记录了今年两次针对Entra ID验证被盗凭据的活动,但从未产生成功登录。该技术足够简单,可以一句话解释:向令牌端点发送一个密码和一个不对应任何注册应用的client ID,然后读取返回的错误。这些错误代码很重要,但更大的泄露是每个代码在身份验证序列中的位置。

三个代码,三种不同的含义

请求发送到OAuth 2.0 Resource Owner Password Credentials(ROPC)端点:一个包含用户名、密码和 client_id 的POST请求。此技术明确限定于ROPC,限制其使用是缓解措施之一。

三个反应很重要:

  • AADSTS50034 (UserAccountNotFound): 该帐户不在目录中。
  • AADSTS50126 (InvalidUserNameOrPassword): 记录为涵盖各种失败的通用错误。
  • AADSTS700016:在目录中未找到应用程序标识符。

AADSTS50126 不应区分错误的用户名和错误的密码,但观察到的行为将它们区分开。对不存在的用户名的失败尝试根本不会写入登录日志,因为 Entra 只有在确认用户名有效后才会写入日志行。Entra 对不存在的账户返回 50034,对存在但密码错误的账户返回 50126,这是微软文档中模糊的行为区别。范围也很重要。这些观察适用于令牌端点的托管租户。联合租户通过 Pass-through Authentication (PTA) 或 ADFS 在本地验证密码,因此错误路径不同。

日志证明了处理方式的差异:

Request

Log row written?

What the attacker learns

Account not in directory (50034)

No

This username doesn't exist

Account exists, wrong password (50126)

Yes, failed sign-in

This username exists

Account exists, correct password (700016)

Yes, failed sign-in

This credential pair works

AADSTS700016 表示应用注册出现故障。在一连串使用不熟悉的客户端ID的身份验证尝试后出现,确认攻击者持有有效密码。

这两个活动

UNK_pyreq2323在2026年1月14日至3月初期间耗尽了AWS基础设施。它生成了超过700,000个伪造的客户端ID,通过随机化合法的第一方Exchange Online应用程序标识符的最后六位数字(例如,00000002-0000-0ff1-ce00-000000XXXXXX)。它针对约4,000个租户中的一百万多个账户,每个伪造ID最多用于12个账户后即被丢弃。第一方Microsoft应用程序ID在每个租户中均已预先同意,因此保留该前缀的ID看起来像是合法的,而非噪声。

UNK_OutFlareAZ于2025年12月出现,并于2026年2月和3月再次出现,通过Cloudflare基础设施针对超过200万用户,使用了370万伪造的应用程序ID。

两者使用了不同的生成方法,这种差异对检测很重要。pyreq2323保留了真实的前缀并最多重复使用每个ID 12次,而OutFlareAZ则为每个请求生成一个新的随机UUIDv4,每个ID对应一个用户,因此任何基于重复使用的启发式方法都无法检测到它。

(Proofpoint 将两个活动归入其UNK_ 指定,标记未归属的集群而非命名的行为者。)

检测

防御者看到了失败和锁定,但他们无法判断 哪些失败是凭证确认。信号就在那儿,和那天早上每个输错密码的用户堆在一起,但如果不知道要分离哪个错误代码,就无法读取。真正看不见的一半是枚举。不存在的用户名根本不会产生日志行,因此所有其他操作之前的侦察在任何租户中都不会留下任何痕迹。

这些请求中的每一个都会记录在交互式登录日志中。这符合微软自己的定义:IsInteractive 涵盖用户提供身份验证因素的登录,定义中明确提到密码,ROPC 提交了密码。

签名是一个空白的应用程序名称:

Client ID in request

AppId

AppDisplayName

Real registered app

populated

populated

Well-formed UUID, unregistered

populated

blank

Malformed client ID

blank

blank

第二行是你要找的。将 isnotempty(AppId) 与 isempty(AppDisplayName) 配对,这样你就能隔离格式正确但未注册的情况,而不会捕获格式错误的垃圾。

关键查询 返回已确认的凭据泄露:

      SigninLogs
| where TimeGenerated > ago(30d)
| where ResultType == "700016"
| where isnotempty(AppId) and isempty(AppDisplayName)
| project TimeGenerated, UserPrincipalName, AppId, IPAddress,
          UserAgent, AuthenticationProtocol, ResultDescription
| order by TimeGenerated asc
      

每一行都是由 Entra 验证的凭据对,因此请将输出视为密码重置和会话撤销列表。这不是应用注册票据,这里最严重的错误是将700016次访问路由到拥有应用注册的人。

用于活动检测,以来源为中心,而非客户ID:

      SigninLogs
| where TimeGenerated > ago(7d)
| where isnotempty(AppId) and isempty(AppDisplayName)
| summarize
    DistinctAppIds = dcount(AppId),
    TargetedUsers  = dcount(UserPrincipalName),
    ConfirmedCreds = countif(ResultType == "700016"),
    WrongPassword  = countif(ResultType == "50126"),
    Lockouts       = countif(ResultType == "50053"),
    UserAgents     = make_set(UserAgent, 10)
  by IPAddress, bin(TimeGenerated, 1h)
| where DistinctAppIds > 10
| order by DistinctAppIds desc
      

以 IPAddress 为中心是刻意为之。任何基于12账户重用上限的启发式方法都会识别pyreq2323,却完全漏掉OutFlareAZ,因为后者从不重用ID。两次攻击从源头看起来相同:一个来源呈现大量无法解析的客户端ID。将 ConfirmedCreds 和 Lockouts 保持在同一行,显示了真实入侵旁的噪声。

修复内容的覆盖范围及不覆盖内容

通过Conditional Access限制ROPC是正确的做法,Proofpoint也推荐这样做。但我想明确这带来了什么。

它不会影响枚举。 当Entra解析用户名时,会返回AADSTS50034,此时在任何凭据验证之前,且在策略评估前两步。没有任何Conditional Access策略可以拦截在策略被查询之前发出的响应,无论租户配置如何,都不会写入日志行。针对您的目录的用户名枚举会绕过所有租户端控制,包括严格的传统身份验证阻断。

阻止传统身份验证的策略返回 AADSTS53003,BlockedByConditionalAccess。根据相同的顺序逻辑,53003 只有在密码验证后才可达,因此攻击者的错误消息变为 50126 而非 53003。不同代码,相同信息。


参考资料

分享到

了解更多

关于作者

Darryl baker

Darryl Baker

高级安全研究员

Darryl G. Baker 是 Netwrix 的高级安全研究员,并在 Identity 与 Active Directory 安全领域享有广泛认可的权威地位。凭借十余年的身份系统经验,他领导并开展了面向 Active Directory、Entra ID 和 Azure 环境的企业安全评估、身份安全培训以及以威胁仿真(threat emulations)为重点的工作。Darryl 曾在 BlueTeamCon、BSidesCT、The Experts Conference 以及 Wild Wild West Hackin’ Fest 上提供备受好评的培训与演示。他是众多“动手”攻击仿真实验室背后的设计者——借助当前的红队与蓝队工具,帮助防御方从攻击路径分析到威胁狩猎(threat hunting)全面掌握所需技能。在他的课程中,Darryl 将深厚的技术洞察与真实世界的案例研究相结合,赋能蓝队专业人士强化其 Identity 安全态势,并抵御不断演进的对手技术。