XWiki users can be tricked to execute scripts as the create page action doesn't display the page's title
Description
XWiki Platform is a generic wiki platform offering runtime services for applications built on top of it. In org.xwiki.platform:xwiki-platform-web versions 7.2-milestone-2 until 14.10.12 and org.xwiki.platform:xwiki-platform-web-templates prior to versions 14.10.12 and 15.5-rc-1, it is possible to pass a title to the page creation action that isn't displayed at first but then executed in the second step. This can be used by an attacker to trick a victim to execute code, allowing script execution if the victim has script right or remote code execution including full access to the XWiki instance if the victim has programming right.
For the attack to work, the attacker needs to convince the victim to visit a link like <xwiki-host>/xwiki/bin/create/NonExistingSpace/WebHome?title=$services.logging.getLogger(%22foo%22).error(%22Script%20executed!%22) where <xwiki-host> is the URL of the Wiki installation and to then click on the "Create" button on that page. The page looks like a regular XWiki page that the victim would also see when clicking the button to create a page that doesn't exist yet, the malicious code is not displayed anywhere on that page. After clicking the "Create" button, the malicious title would be displayed but at this point, the code has already been executed and the attacker could use this code also to hide the attack, e.g., by redirecting the victim again to the same page with an innocent title. It thus seems plausible that this attack could work if the attacker can place a fake "create page" button on a page which is possible with edit right.
This has been patched in org.xwiki.platform:xwiki-platform-web version 14.10.12 and org.xwiki.platform:xwiki-platform-web-templates versions 14.10.12 and 15.5-rc-1 by displaying the title already in the first step such that the victim can notice the attack before continuing. It is possible to manually patch the modified files from the patch in an existing installation. For the JavaScript change, the minified JavaScript file would need to be obtained from a build of XWiki and replaced accordingly.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
org.xwiki.platform:xwiki-platform-web-templatesMaven | < 14.10.12 | 14.10.12 |
org.xwiki.platform:xwiki-platform-web-templatesMaven | >= 15.0-rc-1, < 15.5-rc-1 | 15.5-rc-1 |
org.xwiki.platform:xwiki-platform-webMaven | >= 7.2-milestone-2, < 14.10.12 | 14.10.12 |
Affected products
1- Range: >= 7.2-milestone-2, < 14.10.12
Patches
1199e27ce7016XWIKI-20869: Display more information in the create action
4 files changed · +74 −42
xwiki-platform-core/xwiki-platform-flamingo/xwiki-platform-flamingo-skin/xwiki-platform-flamingo-skin-resources/src/main/resources/flamingo/create.vm+1 −0 modified@@ -20,6 +20,7 @@ #template("startpage.vm") <div class="main layoutsubsection"> <div id="mainContentArea"> + #template('hierarchy.vm') ## --------------------------------------------------------------------------------------------------------- ## Since this template can be used for creating a Page or a Space, compute its title based on the passed ## "tocreate" parameter which can be either "page" or "space". If no "tocreate" parameter is passed then we
xwiki-platform-core/xwiki-platform-web/xwiki-platform-web-templates/src/main/resources/templates/createinline.vm+29 −14 modified@@ -265,8 +265,8 @@ ## --------------------------------------------------------------------------------------------------------- ## Display the form. ## --------------------------------------------------------------------------------------------------------- -## FIXME: Using the 'container' class when displayed in an ajax call by clicking on a Wanted Link so that it looks good (proper margins) in the popup. -<form action="$doc.getURL('create')" method="post" id="create" class="xform #if($request.ajax)container#end"> +#set ($isAjaxRequest = $request.getHeader('X-Requested-With') == 'XMLHttpRequest') +<form action="$doc.getURL('create')" method="post" id="create" class="xform"> <fieldset> ## Deprecated: Pass along any received page parent. @@ -279,16 +279,13 @@ <input type="hidden" name="form_token" value="$!{escapetool.xml($services.csrf.getToken())}"/> <div class='row'> - ## Hide the first column when displayed in an AJAX call by clicking on a Wanted Link (because we know the target - ## location from the link reference) or when the current document is new (because the create action uses the location - ## of the current document as target in this case). - #if (!$request.ajax && !$doc.isNew()) + ## Hide the first column when displayed in an AJAX call by clicking on a Wanted Link, because we know the target + ## location from the link reference. + #if (!$isAjaxRequest) ## Determine the parent reference for the new document. #set ($parentReference = $spaceReference) #if (!$parentReference) ## No parent reference specified. - ## We keep this code although we tested above that the current document is not new because in the future we may - ## want to support changing the target location even if the current document is new. #if ($doc.isNew()) ## Encourage the user to create the current document. #set ($parentReference = $doc.documentReference.parent) @@ -310,6 +307,9 @@ ## Display the location picker. <div class='col-xs-12 col-lg-6'> #template('locationPicker_macros.vm') + ## The create action doesn't support changing the location when the current document doesn't exist (i.e. it + ## forces you to create the current document that is missing). For this reason we make the name and parent fields + ## read-only when the current document is new. #locationPicker({ 'id': 'target', 'title': { @@ -328,14 +328,16 @@ 'hint': 'core.create.spaceReference.hint', 'name': 'spaceReference', 'reference': $parentReference, - 'placeholder': 'core.create.spaceReference.placeholder' + 'placeholder': 'core.create.spaceReference.placeholder', + 'readOnly': $doc.isNew() }, 'name': { 'label': 'core.create.name.label', 'hint': 'core.create.name.hint', 'name': 'name', 'value': $name, - 'placeholder': 'core.create.name.placeholder' + 'placeholder': 'core.create.name.placeholder', + 'readOnly': $doc.isNew() } }) </div> @@ -348,10 +350,20 @@ #set ($spaceReferenceLocalString = $services.model.serialize($spaceReference, 'local')) <input type='hidden' id='spaceReference' name='spaceReference' value="$!{escapetool.xml($spaceReferenceLocalString)}" /> #end - ## FIXME: When displayed in an ajax call by clicking on a Wanted Link, the responsive classes consider the calling document (large screen) - ## as the viewport and not the popup (small screen), so we can not use them since they create problems instead of fixing them. - <div class='#if(!$request.ajax)col-xs-12 col-lg-6#end'> + <div class='col-xs-12#if (!$isAjaxRequest) col-lg-6#end'> <dl> + #if ($isAjaxRequest) + ## The first column that shows the location preview is hidden when creating a new page from a Wanted Link so we + ## need to show this information elsewhere. Even if the target page reference can't be modified by the user, + ## they should still see where the page is going to be created. + <dt> + <label>$escapetool.xml($services.localization.render('core.create.pageTitle'))</label> + </dt> + <dl> + #set ($targetDocumentReference = $services.model.createDocumentReference($name, $spaceReference)) + #hierarchy($targetDocumentReference) + </dl> + #end ## --------------------------------------------------------------------------------------------------------- ## Page type ## --------------------------------------------------------------------------------------------------------- @@ -360,7 +372,10 @@ ## Terminal page - Advanced users ## --------------------------------------------------------------------------------------------------------- #set ($hidden = '') - #if (!($isAdvancedUser || $isSuperAdmin) || $deprecatedSpaceCreate) + ## Top level documents cannot be terminal. + #set ($isTopLevelDoc = $doc.documentReference.name == 'WebHome' && + $doc.documentReference.lastSpaceReference.parent.type == 'WIKI') + #if (!($isAdvancedUser || $isSuperAdmin) || $deprecatedSpaceCreate || ($doc.isNew() && $isTopLevelDoc)) #set ($hidden = 'hidden') #end <dt class="$hidden">
xwiki-platform-core/xwiki-platform-web/xwiki-platform-web-templates/src/main/resources/templates/locationPicker_macros.vm+32 −21 modified@@ -72,10 +72,7 @@ <div class="breadcrumb-container"> ## Note: We display only the parent reference here. The new document part will be added from JavaScript. #hierarchy($options.parent.reference $options) - #if ($isDocumentTreeAvailable) - #documentPickerModal($options) - #locationPickerActions() - #end + #locationPickerActions($options) </div> ## ## --------------------------------------------------------------------------------------------------------- @@ -87,13 +84,17 @@ </dl> #end -#macro (locationPickerActions) +#macro (locationPickerActions $options) <div class="location-actions"> ## Tree picker toggle - <a href="#" class="location-action location-action-pick" title="Select a new location"> + #if ($isDocumentTreeAvailable && $options.parent.label && !$options.parent.readOnly) + #documentPickerModal($options) + <a href="#" class="location-action location-action-pick" title="Select a new location"> $services.icon.renderHTML('chart-organisation')</a> + #end ## Location advanced edit button. - #if ($isAdvancedUser) + #if ($isAdvancedUser && ($options.name.label || $options.parent.label || + ($options.wiki.label && $displayWikiFields))) <a href="#" class="location-action location-action-edit"> $services.icon.renderHTML('pencil') </a> @@ -136,9 +137,9 @@ </dd> #elseif ($wikiField) <dt class="hidden"></dt> - <dt class="hidden"> + <dd class="hidden"> <input type="hidden" name="$wikiField.name" class="location-wiki-field" value="$!escapedValue" /> - </dt> + </dd> #end ## --------------------------------------------------------------------------------------------------------- ## Parent Reference field @@ -159,17 +160,26 @@ #set ($value = $defaultParentReferenceString) #end #set ($escapedValue = $escapetool.xml($value)) - <dt> - <label for="$escapetool.xml($!{options.id})ParentReference">## - $escapetool.xml($services.localization.render($parentField.label))## - </label> - <span class="xHint">$!escapetool.xml($services.localization.render($parentField.hint))</span> - </dt> - <dd> - <input type="text" id="$escapetool.xml($!{options.id})ParentReference" class="location-parent-field suggestSpaces" - name="$escapetool.xml($parentField.name)" value="$!escapedValue" - placeholder="$!escapetool.xml($services.localization.render($parentField.placeholder))" /> - </dd> + #if ($parentField.label) + <dt> + <label for="$escapetool.xml($!{options.id})ParentReference">## + $escapetool.xml($services.localization.render($parentField.label))## + </label> + <span class="xHint">$!escapetool.xml($services.localization.render($parentField.hint))</span> + </dt> + <dd> + <input type="text" id="$escapetool.xml($!{options.id})ParentReference" class="location-parent-field suggestSpaces" + name="$escapetool.xml($parentField.name)" value="$!escapedValue" + placeholder="$!escapetool.xml($services.localization.render($parentField.placeholder))" + #if ($parentField.readOnly)readonly#end /> + </dd> + #else + <dt class="hidden"></dt> + <dd class="hidden"> + <input type="hidden" name="$escapetool.xml($parentField.name)" value="$!escapedValue" + class="location-parent-field" /> + </dd> + #end ## ## --------------------------------------------------------------------------------------------------------- ## Name field @@ -191,7 +201,8 @@ <dd> <input type="text" id="$escapetool.xml($!{options.id})Name" name="$escapetool.xml($nameField.name)" class="location-name-field" value="$!escapedValue" - placeholder="$!escapetool.xml($services.localization.render($nameField.placeholder))" /> + placeholder="$!escapetool.xml($services.localization.render($nameField.placeholder))" + #if ($nameField.readOnly)readonly#end /> </dd> #elseif ($nameField) <dt class="hidden"></dt>
xwiki-platform-core/xwiki-platform-web/xwiki-platform-web-war/src/main/webapp/resources/uicomponents/widgets/locationPicker.js+12 −7 modified@@ -151,13 +151,18 @@ require(['jquery', 'xwiki-meta', 'xwiki-events-bridge', 'xwiki-form-validation-a * Compute a page name from a given title. **/ var getPageName = function(title) { - var url = XWiki.currentDocument.getURL("get"); - return Promise.resolve($.get(url, { - 'xpage': 'entitynamevalidation_json', - 'outputSyntax': 'plain', - 'name': title, - 'form_token': xm.form_token - })); + if (nameInput.prop('readonly')) { + // The page name is read-only so we shouldn't update it based on the title. + return Promise.resolve({transformedName: nameInput.val()}); + } else { + var url = XWiki.currentDocument.getURL("get"); + return Promise.resolve($.get(url, { + 'xpage': 'entitynamevalidation_json', + 'outputSyntax': 'plain', + 'name': title, + 'form_token': xm.form_token + })); + } }; /**
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-ghf6-2f42-mjh9ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2023-45135ghsaADVISORY
- github.com/xwiki/xwiki-platform/commit/199e27ce7016757e66fa7cea99e718044a1b639bghsax_refsource_MISCWEB
- github.com/xwiki/xwiki-platform/security/advisories/GHSA-ghf6-2f42-mjh9ghsax_refsource_CONFIRMWEB
- jira.xwiki.org/browse/XWIKI-20869ghsax_refsource_MISCWEB
News mentions
0No linked articles in our index yet.