原文链接:Phân tích CVE-2023-29357 – Microsoft SharePoint ValidateTokenIssuer Authentication Bypass Vulnerability
译者:知道创宇404实验室翻译组
CVE 简要说明
CVE-2023-29357它允许攻击者绕过身份验证并升级权限,是一个影响SharePoint使用的漏洞。由于未正确验证 JWT 令牌的签名,该漏洞存在于 ValidateTokenIssuer 方法内部。
受影响的版本为:小于16.0.10399.20005。
相关公告:
- https://msrc.microsoft.com/update-guide/vulnerability/CVE-2023-29357
- https://www.zerodayinitiative.com/advisories/ZDI-23-882/
解决办法
- 升级到最新版本的SharePoint。
- 启用AMSI集成功能并使用Windows Defender。
具体分析
在SPJsonWebSecurityBaseTokenHandler
类中添加MissingAlgorithm
枚举,以覆盖传入的令牌缺少算法的情况。
重写ReadToken
方法,并调用SPAuthenticationAlgorithmValidator.ValidateAlgorithm
。
ValidateAlgorithm
继续调用HasValidAlgorithm
方法来检查JWT中的alg
的值。
ZDI的公告中也描述了该漏洞存在于ValidateTokenIssuer
方法中。补丁代码添加了调用SPClaimsUtility.IsEnableOldHashedProofTokenFormat
方法的代码段。
调用堆栈到ValidateTokenIssuer
:
基于以上信息,可以用该算法制作 JWT 令牌并执行与 JWT 发行者( JWT 令牌 none 值)相关的操作。
为了进行身份验证,SharePoint会从以下两个位置获取令牌: GET param prooftoken/headerX-PROOF_TOKEN和 header Authorization。
该令牌需要具有以下值:
-
ver
: giá trị phải làhashedprooftoken
. -
nbf
和exp
: JWT 令牌的当前时间值和过期时间 -
aud
和iss
: 是目标受众和发行者的值,这里必须是00000003-0000-0ff1-ce00-000000000000
。该值00000003-0000-0ff1-ce00-000000000000
代表Office 365 SharePoint Online的应用程序ID,其也可以在本地部署的SharePoint版本中使用。
realm
: 这个值通过向API发送Authorization中的令牌值的请求,其会泄露该值。
在尝试使用上述格式发送JWT后,SharePoint识别出当前用户是“SharePoint App”。
虽然用户已经成功认证SharePoint App
,但该漏洞的目的是我们需要冒充farmadmin账户。使用/_api/web/siteusers
端点列出当前站点上具有权限的用户。
为了模拟其他账户,我们需要修改proof token中的nameid
字段.此时,我们的proof token如下所示:
在使用新令牌进行请求后,进程报错Specified method is not supported
。
进行调试找出原因,异常在SPApplicationPrincipalName.CreateFromString
方法中显现。
主要原因是我们当前的值applicationPrincipleName
没有@
字符,因此无法拆分成长度为2的数组。
SPApplicationPrincipalName.CreateFromString
方法从堆栈跟踪向上追溯一点,SharePoint会检查SPAppRequestContext.Current
是否有效,如果有效,则调用SPAppRequestContext.Current.ClientId
⇒ SPApplicationPrincipalName.CreateFromString
,如上面的堆栈跟踪截图所示。
因此,只需要将SPAppRequestContext.Current
的值设置为null
,就不会再调用ClientId
了。
经过一段时间的调试,我发现SPAppRequestContext.InitCurrent
方法负责给SPAppRequestContext.Current
赋值。其中调用了SPApplicationRequestHelper.IsApplicationRequest
方法来检查发送的请求是否是“应用程序请求”。
该方法如下:
在这里,我们只需要关注spincomingOAuthIdentityType
两个常量SPIncomingOAuthIdentityType.UserAndApplication
和SPIncomingOAuthIdentityType.ApplicationOnly
进行比较。只要我们的请求不符合这两个条件,SPAppRequestContext.Current
就是null
。
为了使spincomingOAuthIdentityType
不等于上述两个值,我们需要关注SPIncomingTokenContext.SetIdentityType
方法。该方法通过我们发送的proof token来确定我们的token是用户还是应用程序。
经过一段时间的调试,我们发现当我的proof token中的isuser
字段的值为true
时,可以将claimsIdentity.Actor
的值设置为null
,从而不进入if语句的内部代码块。
然后,程序继续执行到下面的SPIncomingTokenContext.IsProofToken
方法,该方法检查proof token中的tt
字段,如果该字段不存在,则跳过并返回false
。
因此,我们成功将IdentityType
设置为UserOnly
而不是UserAndApplication
或ApplicationOnly
。
此时,SPApplicationRequestHelper.IsApplicationRequest
的返回值为false
,app请求上下文设置为null
。
至此认证已经成功。
目前的proof token是:
在使用新的令牌重新尝试后,结果如下:
成功模拟farmadmin账户后,我们可以继续利用CVE-2023-24955来RCE,这个漏洞我会在下一篇博文中写到。
本文由 Seebug Paper 发布,如需转载请注明来源。本文地址:https://paper.seebug.org/3021/