文章总结: 本文分享了SharePoint漏洞CVE-2025-53770/CVE-2025-49704的分析失败经验,作者首先修正了上篇文章中的错误,指出实际分析的是CVE-2025-49706而非CVE-2025-53771。通过错误堆栈信息定位到漏洞相关的WebPart解析方法,并详细描述了复现漏洞时遇到的失败经验,包括PoC可能存在的问题。文章最后提供了SharePoint武器化的一些实用笔记,如版本判断方法、鉴权绕过技巧和持久化方案,强调了漏洞利用手法的重要性。 综合评分: 88 文章分类: 漏洞分析,渗透测试,WEB安全,漏洞POC,应急响应
浅谈SharePoint漏洞之CVE-2025-53770分析失败篇
原创
MG
不吃猹的瓜
2025年7月27日 16:46 日本
在本篇的开头先修正上篇文章中一个先入为主的错误,笔者以为自己分析的是CVE-2025-53771,但在写完文章后回头看了补丁才发现分析的是CVE-2025-49706。这俩漏洞本质是一样的,另外据不完全统计,如今公开的PoC都是CVE-2025-49706的PoC。以上对普通读者来说可能没有太大的意义,做流量检测的读者们可以关注下。笔者这里就不再赘述了,因为卡巴出了篇比较详尽的分析[1],感兴趣的读者可以自行阅读。在这一篇中,笔者主要是想分享一下分析复现CVE-2025-49704/CVE-2025-53770时的失败经验。
漏洞点定位
继续上篇的请求分析,看到在SharePoint会在返回包的头中加入request-id用于标识请求,既然能定位到请求,那毫不犹豫地继续开启日志大法(偷懒大法),根据request-id在日志中很容易就找到了错误的堆栈信息:
以上错误引起了笔者的注意,笔者推断此方法或许与CVE-2025-49704/CVE-2025-53770有关系,该方法如下所示:
internal static MarkupProperties GetPartPreviewAndPropertiesFromMarkup(Uri pageUri, string webPartMarkup, bool clearConnections, SPWebPartManager manager, SPWeb web, MarkupOption markupOption, bool bConvertWebPartFormatBehavior, bool prependRegisterDirectivesToMarkup, ref WebPart frontPagePart, ref string markupStorageKey, ref string frontPageZoneId, ref WebPartImporter webPartImporter, ref List<RegisterDirectiveData> registerDirectiveDataList, ref IServerDocumentDesigner documentDesigner)
{
MarkupProperties markupProperties = new MarkupProperties();
frontPagePart = null;
markupStorageKey = null;
bool flag = false;
...
}
方法的整体逻辑太长了,暂时先跳过,简单来说此函数的作用就是解析和实例化一个WebPart。回到堆栈错误,发现引发错误的原因是因为使用了空引用,也就是说要想完成实例化必须得传入相关参数进行解析,十分合理!开始遍历CodeBase,发现了如下方法:
internal WebPart SelectedAspWebPart
{
get
{
if (!this._selectedWebPartSet)
{
this._selectedWebPartSet = true;
if (this.InCustomToolPane && this.SPWebPartManager.DisplayMode == WebPartManager.EditDisplayMode)
{
if (this.frontPageWebPart == null)
{
try
{
stringvalue = SPRequestParameterUtility.GetValue<string>(this.Page.Request, "MSOTlPn_Uri", SPRequestParameterSource.Form); --> 1
if (value != null && value.Length > 0)
{
this.frontPageUri = new Uri(value);
}
}
catch (Exception)
{
this._errorText = WebPartPageResource.GetString("CantLoadWebPartIntranet");
this._selectedWebPart = null;
returnthis._selectedWebPart;
}
MarkupProperties partPreviewAndPropertiesFromMarkup = ToolPane.GetPartPreviewAndPropertiesFromMarkup(this.frontPageUri, SPRequestParameterUtility.GetValue<string>(this.Page.Request, "MSOTlPn_DWP", SPRequestParameterSource.Form), false, this.SPWebPartManager, this.SPWebPartManager.Web, MarkupOption.None, false, false, refthis.frontPageWebPart, refthis.frontPageMarkupStorageKey, refthis.frontPageZoneId, refthis.frontPageWebPartImporter, refthis.frontPageRegisterDirectiveList, refthis.frontPageServerDocumentDesigner); --> 2
this._errorText = partPreviewAndPropertiesFromMarkup.Error;
if (this.frontPageWebPart != null)
{
this.ProcessFrontPagePart(this.frontPageWebPart);
}
}
IL_0111:
this._selectedWebPart = this.frontPageWebPart;
}
elseif (!this.InCustomToolPane)
{
if (this.SPWebPartManager.ForWebPartRender)
{
if (this.SPWebPartManager.WebParts.Count > 0)
{
this._selectedWebPart = this.SPWebPartManager.WebParts[0];
}
else
{
this._selectedWebPartSet = false;
}
}
else
{
this._selectedWebPart = this.SPWebPartManager.FindWebPartByEitherID(ToolPane.GetSelectedWebPartId(this.Page));
}
}
}
returnthis._selectedWebPart;
}
}
此方法用于获取并返回当前在 ToolPane 中被选中的 WebPart 对象实例,获取 WebPart 实例是通过 GetPartPreviewAndPropertiesFromMarkup。到此为止,就已经知道了公开PoC中MSOTlPn_Uri和MSOTlPn_DWP的出处了:对应着SelectedAspWebPart方法中的代码1和2了。分析至此,漏洞的原理已经很清楚了:先通过Referer绕过鉴权,然后构造恶意的MSOTlPn_DWP数据,实现反序列化RCE。笔者觉得自己又行了,ysoserial.net一把梭就完了,但是在后续漫长的复现过程中才发现自己的无知!
失败的漏洞复现
对于已经公开的漏洞,笔者的习惯都是直接先用PoC进行复现,复现成功后再通过PoC去看具体的细节。但当笔者随机选取了一个PoC进行复现时,竟然出现了如下吊诡的结果,竟然返回401:
笔者顿感大事不妙,开始梳理可能出现问题的地方,想到了以下四点:
- 认证绕过是否只对某些认证配置有效?这里涉及到
SharePoint的几种认证方式,感兴趣的可以看看Jang的文章[2] - 是不是
SharePoint偷偷摸摸的自动升级了或者自动下发了IIS Rewrite规则,导致漏洞无法利用成功? - 是不是与
Defender或者AMSI有关?毕竟微软的东西确实是太复杂了 - 是不是
PoC不对劲?
笔者按照上面的顺序,逐个排查,具体排查过程这不讲了,无非就是不停的尝试,最终定位到是PoC不对劲,这个时候其实已经没有什么好办法,只能硬着头皮单步调试了。但又因为一个请求利用了两个漏洞,增加了额外的噪音,所以笔者直接使用已登录的Cookie进行对反序列化的调试。在开始盲目的调试之前,先假设如果自己是一个研发,会怎么设计WebPart实例化过程,正常来说肯定会遵循下面的逻辑:
- 校验用户传入的数据是否是一个符合规范的
WebPart数据,如果不是则抛出异常;如果是则进行实例化 - 实例化过程就是对
WebPart数据进行反序列化的过程,如果在反序列化中发生错误,则抛出异常
由此,可以推断出PoC可能存在的问题点有两个:
MSOTlPn_DWP并不是一个正确的WebPart格式,这种情况下无法运行到反序列化处,直接抛出异常返回- 反序列化链构造错误,不符合
WebPart反序列化的流程,在这种情况下能运行到反序列化处,但会在反序列化时抛出异常,但抛出的异常与第一个相同
以上两种情况,笔者都遇到过,但由于篇幅原因(笔者太懒),这篇先简单提一下反序列化链构造错误这种情况。根据公开信息很容易定位到真正反序列化的触发点在Microsoft.PerformancePoint.Scorecards.Helper.GetObjectFromCompressedBase64String方法,直接上DnSpy:
抛出的异常显而易见,就是序列化数据的问题,先看GetObjectFromCompressedBase64String方法:
public static object GetObjectFromCompressedBase64String(string base64String)
{
if (base64String == null || base64String.Length == 0)
{
returnnull;
}
object obj = null;
byte[] array = Convert.FromBase64String(base64String);
using (MemoryStream memoryStream = new MemoryStream(array))
{
memoryStream.Position = 0L;
GZipStream gzipStream = new GZipStream(memoryStream, CompressionMode.Decompress);
BinaryFormatter binaryFormatter = new BinaryFormatter();
obj = binaryFormatter.Deserialize(gzipStream);
}
return obj;
}
序列化数据得经过三次处理:BinaryFormatter->gzip->base64encode,但具体的细节留到下一篇再讲,一篇文章如果字太多笔者懒得写,读者也不乐意看。
一些武器化小笔记
漏洞复现和分析枯燥无味,下面简单说一些大家都感兴趣的东西,关于SharePoint武器化的一些notes:
- 可以通过
SharePoint返回头中的MicrosoftSharePointTeamServices字段判断SharePoint版本,做初步版本匹配是否存在漏洞,写扫描器的读者可以关注下 - 除了可以使用上篇中提到的
/_layouts/SignOut.aspx,/_layouts/15/SignOut.aspx绕过鉴权,还可以使用/_layouts/./SignOut.aspx - 可通过写入
WebShell获取MachineKey,实现持久化 - 并不是所有利用成功的请求都会返回
200,有很大一部分但是利用成功的请求会返回401
写在后面
相比起复现漏洞,笔者认为这个漏洞链的利用手法更值得学习,希望下一篇能讲到!漏洞本身没有太多意义,好的利用才是漏洞中最令人心醉的部分。
Ref
- https://securelist.com/toolshell-explained/117045/
- https://testbnull.medium.com/sharepoint-not-so-0day-how-ive-failed-at-p2o-vancouver-2024-f6476745d397
查看原文:《浅谈SharePoint漏洞之CVE-2025-53770分析失败篇》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。










评论