XWiki Platform privilege escalation (PR) from account through AWM content fields
Description
XWiki Platform is a generic wiki platform offering runtime services for applications built on top of it. Any registered user can use the content field of their user profile page to execute arbitrary scripts with programming rights, thus effectively performing rights escalation. This issue is present since version 4.3M2 when AppWithinMinutes Application added support for the Content field, allowing any wiki page (including the user profile page) to use its content as an AWM Content field, which has a custom displayer that executes the content with the rights of the `AppWithinMinutes.Content author, rather than the rights of the content author. The vulnerability has been fixed in XWiki 14.10.5 and 15.1RC1. The fix is in the content of the AppWithinMinutes.Content page that defines the custom displayer. By using the display` script service to render the content we make sure that the proper author is used for access rights checks.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
org.xwiki.platform:xwiki-platform-appwithinminutes-uiMaven | >= 4.3-milestone-2, < 14.10.5 | 14.10.5 |
Affected products
1- Range: >= 4.3-milestone-2, < 14.10.5
Patches
1dfb1cde173e3XWIKI-19906: Render the AppWithinMinutes Content field as its author
2 files changed · +41 −15
xwiki-platform-core/xwiki-platform-appwithinminutes/xwiki-platform-appwithinminutes-test/xwiki-platform-appwithinminutes-test-docker/src/test/it/org/xwiki/appwithinminutes/test/ui/DocumentFieldsIT.java+34 −11 modified@@ -23,6 +23,7 @@ import org.apache.commons.lang3.RandomStringUtils; import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; import org.xwiki.appwithinminutes.test.po.ApplicationClassEditPage; import org.xwiki.appwithinminutes.test.po.ApplicationCreatePage; @@ -51,14 +52,15 @@ * @since 13.10RC1 */ @UITest(properties = { - // Exclude the AppWithinMinutes.ClassEditSheet and AppWithinMinutes.DynamicMessageTool from the PR checker since + // Exclude the AppWithinMinutes.ClassEditSheet and AppWithinMinutes.DynamicMessageTool from the PR checker since // they use the groovy macro which requires PR rights. // TODO: Should be removed once XWIKI-20529 is closed. // Exclude AppWithinMinutes.LiveTableEditSheet because it calls com.xpn.xwiki.api.Document.saveWithProgrammingRights - "xwikiPropertiesAdditionalProperties=test.prchecker.excludePattern=.*:AppWithinMinutes\\.(ClassEditSheet|DynamicMessageTool|LiveTableEditSheet)" -}) + "xwikiPropertiesAdditionalProperties=test.prchecker.excludePattern=.*:AppWithinMinutes\\.(ClassEditSheet|DynamicMessageTool|LiveTableEditSheet)"}) class DocumentFieldsIT { + private String appName = RandomStringUtils.randomAlphabetic(6); + @BeforeAll static void beforeAll(TestUtils setup) { @@ -73,14 +75,12 @@ static void beforeAll(TestUtils setup) } @Test + @Order(1) void titleAndContent(TestUtils setup) { - setup.loginAsAdmin(); - // Create a new application. - String appName = RandomStringUtils.randomAlphabetic(6); ApplicationCreatePage appCreatePage = ApplicationCreatePage.gotoPage(); - appCreatePage.setApplicationName(appName); + appCreatePage.setApplicationName(this.appName); ApplicationClassEditPage classEditPage = appCreatePage.clickNextStep(); // Add a standard field. @@ -126,30 +126,53 @@ void titleAndContent(TestUtils setup) assertTrue(entryViewPage.getContent().contains("Bar")); // Verify that we can edit the document fields in-place. - String propertyReference = String.format("%s.Code.%1$sClass[0].title1", appName); + String propertyReference = String.format("%s.Code.%1$sClass[0].title1", this.appName); EditablePropertyPane<String> titleProperty = new EditablePropertyPane<>(propertyReference); assertEquals("Foo", titleProperty.clickEdit().getValue()); titleProperty.setValue("Book").clickSave(); assertEquals("Book", titleProperty.getDisplayValue()); // Check the entries live table. - entryViewPage.clickBreadcrumbLink(appName); + entryViewPage.clickBreadcrumbLink(this.appName); LiveTableElement liveTable = new ApplicationHomePage().getEntriesLiveTable(); liveTable.waitUntilReady(); assertEquals(1, liveTable.getRowCount()); assertTrue(liveTable.hasRow("My Title", "Book")); assertTrue(liveTable.hasRow("My Content", "Bar")); // Check that the title and the content of the class have not been changed. - setup.gotoPage(new LocalDocumentReference(Arrays.asList(appName, "Code"), appName + "Class"), "edit", + setup.gotoPage(new LocalDocumentReference(Arrays.asList(this.appName, "Code"), this.appName + "Class"), "edit", "editor=wiki"); WikiEditPage editPage = new WikiEditPage(); - assertEquals(appName + " Class", editPage.getTitle()); + assertEquals(this.appName + " Class", editPage.getTitle()); assertEquals("", editPage.getContent()); // Now edit the class and check if the default values for title and content are taken from the template. editPage.edit(); assertEquals(defaultTitle, new ClassFieldEditPane("title1").getDefaultValue()); assertEquals(defaultContent, new ClassFieldEditPane("content1").getDefaultValue()); } + + @Test + @Order(2) + void contentFromSimpleUser(TestUtils setup) + { + // Create an application entry with a simple user that doesn't have script rights. + setup.createUserAndLogin("Alice", "pass"); + + ApplicationHomePage appHomePage = ApplicationHomePage.gotoPage(this.appName); + appHomePage.getEntriesLiveTable().waitUntilReady(); + + EntryNamePane entryNamePage = appHomePage.clickAddNewEntry(); + entryNamePage.setName("ByAlice"); + + EntryEditPage entryEditPage = entryNamePage.clickAdd(); + entryEditPage.setTitle("Title by $services.localization.render('Alice')"); + entryEditPage.setContent("Content by {{velocity}}$services.localization.render('Alice'){{/velocity}}"); + + ViewPage entryViewPage = entryEditPage.clickSaveAndView(); + assertEquals("Title by $services.localization.render('Alice')", entryViewPage.getDocumentTitle()); + assertTrue(entryViewPage.getContent().contains("Content by")); + assertTrue(entryViewPage.getContent().contains("The execution of the [velocity] script macro is not allowed")); + } }
xwiki-platform-core/xwiki-platform-appwithinminutes/xwiki-platform-appwithinminutes-ui/src/main/resources/AppWithinMinutes/Content.xml+7 −4 modified@@ -20,7 +20,7 @@ * 02110-1301 USA, or see the FSF site: http://www.fsf.org. --> -<xwikidoc version="1.2" reference="AppWithinMinutes.Content" locale=""> +<xwikidoc version="1.5" reference="AppWithinMinutes.Content" locale=""> <web>AppWithinMinutes</web> <name>Content</name> <language/> @@ -76,9 +76,12 @@ #end {{/html}} #elseif ("$!type" != '') - ## Include the content of the current document. - ## Escape {{ in the rendered content to be sure that the HTML macro is not closed unintentionally. - {{html}}$tdoc.getRenderedContent($tdoc.content, $tdoc.syntax.toIdString()).replace('{{', '&amp;#123;&amp;#123;'){{/html}} + ## Display the content of the current document without using any sheet. We can't use the include macro here (with the + ## author parameter) because the content may have unsaved changes (e.g. on preview action). We make sure that the HTML + ## macro is not closed unintentionally, even though the XHTML printer protects us against this, just to be extra safe. + {{html}}$services.display.content($tdoc, { + 'displayerHint': 'default' + }).replace('{{/html}}', '&amp;#123;&amp;#123;/html&amp;#125;&amp;#125;'){{/html}} #else The display mode is not specified! #end
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-5mf8-v43w-mfxpghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2023-40177ghsaADVISORY
- github.com/xwiki/xwiki-platform/commit/dfb1cde173e363ca5c12eb3654869f9719820262ghsax_refsource_MISCWEB
- github.com/xwiki/xwiki-platform/security/advisories/GHSA-5mf8-v43w-mfxpghsax_refsource_CONFIRMWEB
- jira.xwiki.org/browse/XWIKI-7369ghsax_refsource_MISCWEB
News mentions
0No linked articles in our index yet.