XWiki allows remote code execution from account through macro descriptions and XWiki.XWikiSyntaxMacrosList
Description
XWiki Platform is a generic wiki platform. Starting in version 9.7-rc-1 and prior to versions 15.10.11, 16.4.1, and 16.5.0, any user with an account can perform arbitrary remote code execution by adding instances of XWiki.WikiMacroClass to any page. This compromises the confidentiality, integrity and availability of the whole XWiki installation. This vulnerability has been fixed in XWiki 15.10.11, 16.4.1 and 16.5.0. It is possible to manually apply the patch to the page XWiki.XWikiSyntaxMacrosList as a workaround.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
org.xwiki.platform:xwiki-platform-help-uiMaven | >= 9.7-rc-1, < 15.10.11 | 15.10.11 |
org.xwiki.platform:xwiki-platform-help-uiMaven | >= 16.0.0-rc-1, < 16.4.1 | 16.4.1 |
org.xwiki.platform:xwiki-platform-help-uiMaven | >= 16.5.0-rc-1, < 16.5.0 | 16.5.0 |
Affected products
1- Range: >= 9.7-rc-1, < 15.10.11
Patches
140e1afe001d6XWIKI-22030: Improve escaping in macros list
2 files changed · +56 −18
xwiki-platform-core/xwiki-platform-help/xwiki-platform-help-ui/src/main/resources/XWiki/XWikiSyntaxMacrosList.xml+2 −2 modified@@ -39,10 +39,10 @@ <content>{{velocity}} ## If the translation key exists, use its value, otherwise use to the provided fallback value. #macro (translateOrElse $translationKey $fallback) - #if($services.localization.get($translationKey)) + #if($services.localization.get($translationKey)) $services.rendering.escape($services.localization.render($translationKey), 'xwiki/2.1')## #else - $fallback## + $services.rendering.escape($fallback, 'xwiki/2.1')## #end #end
xwiki-platform-core/xwiki-platform-help/xwiki-platform-help-ui/src/test/java/org/xwiki/help/XWikiSyntaxMacrosListPageTest.java+54 −16 modified@@ -27,6 +27,7 @@ import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import org.jsoup.select.Elements; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.xwiki.context.internal.concurrent.DefaultContextStoreManager; import org.xwiki.localization.macro.internal.TranslationMacro; @@ -49,13 +50,15 @@ import org.xwiki.test.annotation.ComponentList; import org.xwiki.test.page.HTML50ComponentList; import org.xwiki.test.page.PageTest; +import org.xwiki.test.page.TestNoScriptMacro; import org.xwiki.test.page.XWikiSyntax21ComponentList; import com.xpn.xwiki.DefaultSkinAccessBridge; import com.xpn.xwiki.doc.XWikiDocument; import com.xpn.xwiki.objects.BaseObject; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -88,19 +91,30 @@ // End of XWikiWikiModel DocumentXHTMLLinkTypeRenderer.class, DocumentResourceReferenceEntityReferenceResolver.class, + TestNoScriptMacro.class, TranslationMacro.class }) class XWikiSyntaxMacrosListPageTest extends PageTest { public static final DocumentReference DOCUMENT_REFERENCE = new DocumentReference("xwiki", "XWiki", "XWikiSyntaxMacrosList"); - @Test - void renderTable() throws Exception + private DefaultWikiMacro myMacro; + + @BeforeEach + void setUp() throws Exception { // Initialize "WikiMacroClass" this.xwiki.initializeMandatoryDocuments(this.context); + // Mock the database. + Query query = mock(Query.class); + QueryManagerScriptService queryManagerScriptService = + this.componentManager.registerMockComponent(ScriptService.class, "query", QueryManagerScriptService.class, + false); + when(queryManagerScriptService.xwql(any())).thenReturn(query); + when(query.execute()).thenReturn(List.of("xwiki:XWiki.MyMacro")); + // Create a wiki macro. XWikiDocument myMacroDocument = this.xwiki.getDocument(new DocumentReference("xwiki", "XWiki", "MyMacro"), this.context); @@ -111,20 +125,17 @@ void renderTable() throws Exception this.xwiki.saveDocument(myMacroDocument, this.context); // Register the wiki macro component. - DefaultWikiMacro myMacro = + this.myMacro = this.componentManager.registerMockComponent(Macro.class, "mymacro", DefaultWikiMacro.class, false); - DefaultMacroDescriptor macroDescriptor = - new DefaultMacroDescriptor(new MacroId("mymacro"), "My Macro", "My Macro Description"); - macroDescriptor.setDefaultCategories(Set.of("Category1", "Category2")); - when(myMacro.getDescriptor()).thenReturn(macroDescriptor); + } - // Mock the database. - Query query = mock(Query.class); - QueryManagerScriptService queryManagerScriptService = - this.componentManager.registerMockComponent(ScriptService.class, "query", QueryManagerScriptService.class, - false); - when(queryManagerScriptService.xwql(any())).thenReturn(query); - when(query.execute()).thenReturn(List.of("xwiki:XWiki.MyMacro")); + @Test + void renderTable() throws Exception + { + DefaultMacroDescriptor macroDescriptor = new DefaultMacroDescriptor(new MacroId("mymacro"), "My Macro", + "My Macro Description"); + macroDescriptor.setDefaultCategories(Set.of("Category1", "Category2")); + when(this.myMacro.getDescriptor()).thenReturn(macroDescriptor); // Render the page. Document document = renderHTMLPage(DOCUMENT_REFERENCE); @@ -144,12 +155,39 @@ void renderTable() throws Exception "XWiki.WikiMacroClass_visibility_Global"); assertWikiMacro(trs.get(3), "mymacro", "/xwiki/bin/view/XWiki/MyMacro", "My Macro", Set.of("Category1", "Category2"), "My Macro Description", "XWiki.WikiMacroClass_visibility_WIKI"); - assertJavaMacro(trs.get(4), "translation", "Translation", "Content", + assertJavaMacro(trs.get(4), "noscript", "NoScript", "", "No Script!", "XWiki.WikiMacroClass_visibility_Global"); + assertJavaMacro(trs.get(5), "translation", "Translation", "Content", "Display a translation message.", "XWiki.WikiMacroClass_visibility_Global"); - assertJavaMacro(trs.get(5), "velocity", "Velocity", "Development", "Executes a Velocity script.", + assertJavaMacro(trs.get(6), "velocity", "Velocity", "Development", "Executes a Velocity script.", "XWiki.WikiMacroClass_visibility_Global"); } + @Test + void checkTableEscaping() throws Exception + { + String unescapedString = "{{noscript /}}"; + + DefaultMacroDescriptor macroDescriptor = new DefaultMacroDescriptor(new MacroId("mymacro"), unescapedString, + unescapedString); + macroDescriptor.setDefaultCategories(Set.of(unescapedString)); + when(this.myMacro.getDescriptor()).thenReturn(macroDescriptor); + + Document document = renderHTMLPage(DOCUMENT_REFERENCE); + + Elements trs = document.select("tr"); + Element myMacroTr = null; + for (Element tr : trs) { + Element th = tr.selectFirst("td"); + if (th != null && th.text().equals("mymacro")) { + myMacroTr = tr; + } + } + + assertNotNull(myMacroTr); + assertWikiMacro(myMacroTr, "mymacro", "/xwiki/bin/view/XWiki/MyMacro", unescapedString, Set.of(unescapedString), + unescapedString, "XWiki.WikiMacroClass_visibility_WIKI"); + } + private void assertWikiMacro(Element rowElement, String id, String link, String name, Set<String> categories, String description, String visibility) {
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
5- github.com/advisories/GHSA-2r87-74cx-2p7cghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2024-55877ghsaADVISORY
- github.com/xwiki/xwiki-platform/commit/40e1afe001d61eafdf13f3621b4b597a0e58a3e3ghsax_refsource_MISCWEB
- github.com/xwiki/xwiki-platform/security/advisories/GHSA-2r87-74cx-2p7cghsax_refsource_CONFIRMWEB
- jira.xwiki.org/browse/XWIKI-22030ghsax_refsource_MISCWEB
News mentions
0No linked articles in our index yet.