CVE-2023-40177 (published 2023-08-23T21:15Z)

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.

CVSS v3 base score: 8.8, HIGH (CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H)

Weakness information:
cwe_id: CWE-94
cwe_name: Improper Control of Generation of Code ('Code Injection')
description: The product constructs all or part of a code segment using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the syntax or behavior of the intended code segment.
extended_description: 
            When a product allows a user's input to contain code syntax, it might be possible for an attacker to craft the code in such a way that it will alter the intended control flow of the product. Such an alteration could lead to arbitrary code execution.
            Injection problems encompass a wide variety of issues -- all mitigated in very different ways. For this reason, the most effective way to discuss these weaknesses is to note the distinct features which classify them as injection weaknesses. The most important issue to note is that all injection problems share one thing in common -- i.e., they allow for the injection of control plane data into the user-controlled data plane. This means that the execution of the process may be altered by sending code in through legitimate data channels, using no other mechanism. While buffer overflows, and many other flaws, involve the use of some further issue to gain execution, injection problems need only for the data to be parsed. The most classic instantiations of this category of weakness are SQL injection and format string vulnerabilities.
         
         

Fix information:
repo_url: https://github.com/xwiki/xwiki-platform
commit_details: repo_url: https://github.com/xwiki/xwiki-platform
author: Marius Dumitru Florea
author_date: 2023-02-15 12:44:00+02:00
msg: XWIKI-19906: Render the AppWithinMinutes Content field as its author
repository: repo_url: https://github.com/xwiki/xwiki-platform
repo_name: xwiki/xwiki-platform
description: The XWiki platform
repo_language: Java
owner: xwiki
file_changes: filename: DocumentFieldsIT.java
change_type: ModificationType.MODIFY
diff: @@ -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,14 +126,14 @@ 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());
@@ -141,10 +141,10 @@ void titleAndContent(TestUtils setup)
         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.
@@ -152,4 +152,27 @@ void titleAndContent(TestUtils setup)
         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"));
+    }
 }

programming_language: Java
filename: Content.xml
change_type: ModificationType.MODIFY
diff: @@ -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;amp;#123;&amp;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;amp;#123;&amp;amp;#123;/html&amp;amp;#125;&amp;amp;#125;'){{/html}}
 #else
   The display mode is not specified!
 #end

programming_language: HTML