VYPR
Critical severityNVD Advisory· Published Aug 17, 2023· Updated Oct 8, 2024

Privilege escalation (PR)/RCE from account through Invitation subject/message

CVE-2023-37914

Description

XWiki Platform is a generic wiki platform offering runtime services for applications built on top of it. Any user who can view Invitation.WebHome can execute arbitrary script macros including Groovy and Python macros that allow remote code execution including unrestricted read and write access to all wiki contents. This vulnerability has been patched on XWiki 14.4.8, 15.2-rc-1, and 14.10.6. Users are advised to upgrade. Users unable to upgrade may manually apply the patch on Invitation.InvitationCommon and Invitation.InvitationConfig, but there are otherwise no known workarounds for this vulnerability.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
org.xwiki.platform:xwiki-platform-invitation-uiMaven
>= 2.5-m-1, < 14.4.814.4.8
org.xwiki.platform:xwiki-platform-invitation-uiMaven
>= 14.5, < 14.10.614.10.6
org.xwiki.platform:xwiki-platform-invitation-uiMaven
>= 15.0-rc-1, < 15.2-rc-115.2-rc-1

Affected products

1

Patches

1
ff1d8a1790c6

XWIKI-20421: Improved escaping of subjectLineTemplate

https://github.com/xwiki/xwiki-platformManuel LeducFeb 28, 2023via ghsa
4 files changed · +53 16
  • xwiki-platform-core/xwiki-platform-invitation/xwiki-platform-invitation-ui/src/main/resources/Invitation/InvitationCommon.xml+1 1 modified
    @@ -825,7 +825,7 @@ $services.localization.render('xe.invitation.emailContent.reportMessage', ["&lt;
           <smtp_server_username/>
         </property>
         <property>
    -      <subjectLineTemplate>{{velocity}}$services.localization.render('xe.invitation.emailContent.subjectLine', [$xcontext.getUser().replaceAll("^[^\.]*.", ""), $xwiki.getRequestURL().replaceAll("https?://([^/:]*).*$", "$1"), $!subjectLine]){{/velocity}}</subjectLineTemplate>
    +      <subjectLineTemplate>{{velocity wiki='false'}}$services.localization.render('xe.invitation.emailContent.subjectLine', [$xcontext.getUser().replaceAll("^[^\.]*.", ""), $xwiki.getRequestURL().replaceAll("https?://([^/:]*).*$", "$1"), $!subjectLine]){{/velocity}}</subjectLineTemplate>
         </property>
         <property>
           <usersMayPersonalizeMessage>1</usersMayPersonalizeMessage>
    
  • xwiki-platform-core/xwiki-platform-invitation/xwiki-platform-invitation-ui/src/main/resources/Invitation/InvitationConfig.xml+1 1 modified
    @@ -420,7 +420,7 @@ $services.localization.render('xe.invitation.emailContent.reportMessage', ["&lt;
           <smtp_server_username/>
         </property>
         <property>
    -      <subjectLineTemplate>{{velocity}}$services.localization.render('xe.invitation.emailContent.subjectLine', [$xcontext.getUser().replaceAll("^[^\.]*.", ""), $xwiki.getRequestURL().replaceAll("https?://([^/:]*).*$", "$1"), $!subjectLine]){{/velocity}}</subjectLineTemplate>
    +      <subjectLineTemplate>{{velocity wiki='false'}}$services.localization.render('xe.invitation.emailContent.subjectLine', [$xcontext.getUser().replaceAll("^[^\.]*.", ""), $xwiki.getRequestURL().replaceAll("https?://([^/:]*).*$", "$1"), $!subjectLine]){{/velocity}}</subjectLineTemplate>
         </property>
         <property>
           <usersMayPersonalizeMessage>1</usersMayPersonalizeMessage>
    
  • xwiki-platform-core/xwiki-platform-invitation/xwiki-platform-invitation-ui/src/test/java/org/xwiki/invitation/InvitationCommonPageTest.java+31 12 modified
    @@ -33,7 +33,6 @@
     import org.xwiki.rendering.internal.configuration.DefaultRenderingConfigurationComponentList;
     import org.xwiki.rendering.internal.macro.message.ErrorMessageMacro;
     import org.xwiki.rendering.internal.macro.message.InfoMessageMacro;
    -import org.xwiki.rendering.syntax.Syntax;
     import org.xwiki.script.ScriptContextManager;
     import org.xwiki.security.authorization.AuthorizationManager;
     import org.xwiki.test.annotation.ComponentList;
    @@ -51,6 +50,9 @@
     import static org.junit.jupiter.api.Assertions.assertEquals;
     import static org.mockito.ArgumentMatchers.any;
     import static org.mockito.Mockito.when;
    +import static org.xwiki.rendering.syntax.Syntax.PLAIN_1_0;
    +import static org.xwiki.rendering.syntax.Syntax.XWIKI_2_0;
    +import static org.xwiki.rendering.syntax.Syntax.XWIKI_2_1;
     
     /**
      * Test of {@code Invitation.InvitationCommon}.
    @@ -73,6 +75,9 @@
     })
     class InvitationCommonPageTest extends PageTest
     {
    +    public static final DocumentReference INVITATION_COMMON_REFERENCE =
    +        new DocumentReference("xwiki", "Invitation", "InvitationCommon");
    +
         private ScriptContext scriptContext;
     
         @BeforeEach
    @@ -91,8 +96,7 @@ void testEq0() throws Exception
             this.context.setDoc(this.xwiki.getDocument(
                 new DocumentReference("xwiki", "]]  {{noscript/}}", "InvitationCommon"), this.context));
     
    -        DocumentReference invitationCommonReference = new DocumentReference("xwiki", "Invitation", "InvitationCommon");
    -        Document document = Jsoup.parse(loadPage(invitationCommonReference).getRenderedContent(this.context));
    +        Document document = Jsoup.parse(loadPage(INVITATION_COMMON_REFERENCE).getRenderedContent(this.context));
             assertEquals("xe.invitation.internalDocument []] {{noscript/}}.WebHome]",
                 document.selectFirst(".infomessage").text());
         }
    @@ -101,7 +105,6 @@ void testEq0() throws Exception
         void testEq1ConfigClassIsNew() throws Exception
         {
             String spaceName = "<script>console.log</script>]]{{noscript/}}";
    -        DocumentReference invitationCommonReference = new DocumentReference("xwiki", "Invitation", "InvitationCommon");
     
             XWikiDocument doc =
                 this.xwiki.getDocument(new DocumentReference("xwiki", spaceName, "InvitationCommon"), this.context);
    @@ -110,7 +113,7 @@ void testEq1ConfigClassIsNew() throws Exception
     
             this.request.put("test", "1");
     
    -        Document document = Jsoup.parse(loadPage(invitationCommonReference).getRenderedContent(this.context));
    +        Document document = Jsoup.parse(loadPage(INVITATION_COMMON_REFERENCE).getRenderedContent(this.context));
     
             assertEquals("testLoadInvitationConfig", document.selectFirst(".infomessage").text());
             Element errorMessage = document.selectFirst(".errormessage");
    @@ -148,7 +151,7 @@ void testEq1ConfigClassExistsInvalidFromAddress() throws Exception
     
             this.request.put("test", "1");
     
    -        XWikiDocument invitationCommonDoc = loadPage(new DocumentReference("xwiki", "Invitation", "InvitationCommon"));
    +        XWikiDocument invitationCommonDoc = loadPage(INVITATION_COMMON_REFERENCE);
             Document document = Jsoup.parse(invitationCommonDoc.getRenderedContent(this.context));
     
             assertEquals("testLoadInvitationConfig", document.selectFirst(".infomessage").text());
    @@ -187,8 +190,7 @@ void testEq1ConfigClassExistsConfigMapTooSmall() throws Exception
     
             this.request.put("test", "1");
     
    -        DocumentReference reference = new DocumentReference("xwiki", "Invitation", "InvitationCommon");
    -        XWikiDocument invitationCommonDoc = loadPage(reference);
    +        XWikiDocument invitationCommonDoc = loadPage(INVITATION_COMMON_REFERENCE);
             Document document = Jsoup.parse(invitationCommonDoc.getRenderedContent(this.context));
     
             assertEquals("testLoadInvitationConfig", document.selectFirst(".infomessage").text());
    @@ -229,8 +231,7 @@ void testEq1ConfigClassExistsNewInvitationConfig() throws Exception
     
             this.request.put("test", "1");
     
    -        DocumentReference reference = new DocumentReference("xwiki", "Invitation", "InvitationCommon");
    -        XWikiDocument invitationCommonDoc = loadPage(reference);
    +        XWikiDocument invitationCommonDoc = loadPage(INVITATION_COMMON_REFERENCE);
             Document document = Jsoup.parse(invitationCommonDoc.getRenderedContent(this.context));
     
             assertEquals("testLoadInvitationConfig", document.selectFirst(".infomessage").text());
    @@ -241,15 +242,15 @@ void testEq1ConfigClassExistsNewInvitationConfig() throws Exception
         @Test
         void displayMessageVelocityMacro() throws Exception
         {
    -        loadPage(new DocumentReference("xwiki", "Invitation", "InvitationCommon"));
    +        loadPage(INVITATION_COMMON_REFERENCE);
             DocumentReference invitationMailClassDocumentReference =
                 new DocumentReference("xwiki", "Invitation", "InvitationMailClass");
             loadPage(invitationMailClassDocumentReference);
     
             XWikiDocument page = this.xwiki.getDocument(new DocumentReference("xwiki", "Space", "Page"), this.context);
             BaseObject invitationMailXObject = page.newXObject(invitationMailClassDocumentReference, this.context);
             invitationMailXObject.set("messageBody", "<strong>message body</strong>", this.context);
    -        page.setSyntax(Syntax.XWIKI_2_1);
    +        page.setSyntax(XWIKI_2_1);
             page.setContent("{{include reference=\"Invitation.InvitationCommon\"/}}\n"
                 + "\n"
                 + "{{velocity}}\n"
    @@ -262,4 +263,22 @@ void displayMessageVelocityMacro() throws Exception
             Document document = renderHTMLPage(page);
             assertEquals("<strong>message body</strong>", document.selectFirst("#preview-messagebody-field").html());
         }
    +
    +    /**
    +     * Check if the value of subjectLine correctly escape its parameters.
    +     */
    +    @Test
    +    void subjectLineTemplate() throws Exception
    +    {
    +        com.xpn.xwiki.api.Document invitationCommonDocument =
    +            new com.xpn.xwiki.api.Document(loadPage(INVITATION_COMMON_REFERENCE), this.context);
    +        String value = String.valueOf(
    +            invitationCommonDocument.getObject("xwiki:Invitation.WebHome").getProperty("subjectLineTemplate")
    +                .getValue());
    +        this.oldcore.getScriptContext().setAttribute("subjectLine", "{{noscript/}}", GLOBAL_SCOPE);
    +        com.xpn.xwiki.api.Document testDocument = new com.xpn.xwiki.api.Document(
    +            this.xwiki.getDocument(new DocumentReference("xwiki", "Space", "Test"), this.context), this.context);
    +        String renderedContent = testDocument.getRenderedContent(value, XWIKI_2_0.toIdString(), PLAIN_1_0.toIdString());
    +        assertEquals("xe.invitation.emailContent.subjectLine [XWikiGuest, null, {{noscript/}}]", renderedContent);
    +    }
     }
    
  • xwiki-platform-core/xwiki-platform-invitation/xwiki-platform-invitation-ui/src/test/java/org/xwiki/invitation/InvitationConfigPageTest.java+20 2 modified
    @@ -36,7 +36,10 @@
     
     import com.xpn.xwiki.doc.XWikiDocument;
     
    +import static javax.script.ScriptContext.GLOBAL_SCOPE;
     import static org.junit.jupiter.api.Assertions.assertEquals;
    +import static org.xwiki.rendering.syntax.Syntax.PLAIN_1_0;
    +import static org.xwiki.rendering.syntax.Syntax.XWIKI_2_0;
     
     /**
      * Test of {@code Invitation.InvitationConfig}.
    @@ -59,13 +62,13 @@
     })
     class InvitationConfigPageTest extends PageTest
     {
    -    private static final DocumentReference INVITATION_CONFIG_DOCUMENT_REFERENCE =
    +    private static final DocumentReference INVITATION_CONFIG_REFERENCE =
             new DocumentReference("xwiki", "Invitation", "InvitationConfig");
     
         @Test
         void escapeInfoMessageInternalDocumentParameter() throws Exception
         {
    -        XWikiDocument invitationGuestActionsDocument = loadPage(INVITATION_CONFIG_DOCUMENT_REFERENCE);
    +        XWikiDocument invitationGuestActionsDocument = loadPage(INVITATION_CONFIG_REFERENCE);
     
             // Set up the current doc in the context so that $doc is bound in scripts
             this.context.setDoc(
    @@ -75,4 +78,19 @@ void escapeInfoMessageInternalDocumentParameter() throws Exception
             Element infomessage = document.selectFirst(".infomessage");
             assertEquals("xe.invitation.internalDocument [Invitation.WebHome]", infomessage.text());
         }
    +
    +    @Test
    +    void subjectLineTemplate() throws Exception
    +    {
    +        com.xpn.xwiki.api.Document invitationConfigDocument =
    +            new com.xpn.xwiki.api.Document(loadPage(INVITATION_CONFIG_REFERENCE), this.context);
    +        String value = String.valueOf(
    +            invitationConfigDocument.getObject("xwiki:Invitation.WebHome").getProperty("subjectLineTemplate")
    +                .getValue());
    +        this.oldcore.getScriptContext().setAttribute("subjectLine", "{{noscript/}}", GLOBAL_SCOPE);
    +        com.xpn.xwiki.api.Document testDocument = new com.xpn.xwiki.api.Document(
    +            this.xwiki.getDocument(new DocumentReference("xwiki", "Space", "Test"), this.context), this.context);
    +        String renderedContent = testDocument.getRenderedContent(value, XWIKI_2_0.toIdString(), PLAIN_1_0.toIdString());
    +        assertEquals("xe.invitation.emailContent.subjectLine [XWikiGuest, null, {{noscript/}}]", renderedContent);
    +    }
     }
    

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

News mentions

0

No linked articles in our index yet.