VYPR
High severityNVD Advisory· Published Feb 9, 2022· Updated Apr 23, 2025

Remote code execution in xwiki-platform

CVE-2022-23616

Description

XWiki Platform is a generic wiki platform offering runtime services for applications built on top of it. In affected versions it's possible for an unprivileged user to perform a remote code execution by injecting a groovy script in her own profile and by calling the Reset password feature since the feature is performing a save of the user profile with programming rights in the impacted versions of XWiki. The issue has been patched in XWiki 13.1RC1. There are two different possible workarounds, each consisting of modifying the XWiki/ResetPassword page. 1. The Reset password feature can be entirely disabled by deleting the XWiki/ResetPassword page. 2. The script in XWiki/ResetPassword can also be modified or removed: an administrator can replace it with a simple email contact to ask an administrator to reset the password.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
org.xwiki.platform:xwiki-platform-administration-uiMaven
>= 3.1-milestone-1, < 13.1RC113.1RC1

Affected products

1

Patches

1
407caeba05c1

XAADMINISTRATION-196: Reset Password fails in private wikis

https://github.com/xwiki/xwiki-platformsdumitriuJan 13, 2011via ghsa
5 files changed · +130 141
  • xwiki-platform-administration/src/main/resources/XWiki/ForgotUsername.xml+27 23 modified
    @@ -5,7 +5,7 @@
     <language></language>
     <defaultLanguage>en</defaultLanguage>
     <translation>0</translation>
    -<parent>XWiki.WebHome</parent>
    +<parent>Main.WebHome</parent>
     <creator>XWiki.Admin</creator>
     <author>XWiki.Admin</author>
     <customClass></customClass>
    @@ -14,13 +14,13 @@
     <date>1219710939000</date>
     <contentUpdateDate>1219710939000</contentUpdateDate>
     <version>1.1</version>
    -<title>$msg.get("xe.admin.passwordreset.forgotusername")</title>
    +<title>$msg.get("xe.admin.forgotUsername.title")</title>
     <template></template>
     <defaultTemplate></defaultTemplate>
     <validationScript></validationScript>
     <comment></comment>
     <minorEdit>false</minorEdit>
    -<syntaxId>xwiki/2.0</syntaxId>
    +<syntaxId>xwiki/2.1</syntaxId>
     <hidden>false</hidden>
     <object>
     <class>
    @@ -89,13 +89,16 @@
     </users>
     </class>
     <name>XWiki.ForgotUsername</name>
    -<number>1</number>
    +<number>0</number>
     <className>XWiki.XWikiRights</className>
    -<guid>5f9081d2-87b6-430e-8bb9-edc66544fc05</guid>
    +<guid>5f9081d2-87b6-430e-8bb9-edc66544fc06</guid>
     <property>
     <allow>0</allow>
     </property>
     <property>
    +<groups>XWiki.XWikiAllGroup</groups>
    +</property>
    +<property>
     <levels>edit</levels>
     </property>
     <property>
    @@ -169,9 +172,9 @@
     </users>
     </class>
     <name>XWiki.ForgotUsername</name>
    -<number>3</number>
    +<number>1</number>
     <className>XWiki.XWikiRights</className>
    -<guid>54be8bc6-c81a-4149-b2b0-161b2b974856</guid>
    +<guid>54be8bc6-c81a-4149-b2b0-161b2b974857</guid>
     <property>
     <allow>0</allow>
     </property>
    @@ -249,49 +252,50 @@
     </users>
     </class>
     <name>XWiki.ForgotUsername</name>
    -<number>5</number>
    +<number>2</number>
     <className>XWiki.XWikiRights</className>
    -<guid>3b1dcd67-ca67-47a4-a5ab-66c26bbec642</guid>
    +<guid>06daec64-228b-4da1-b7e6-b59d9624c859</guid>
     <property>
    -<allow>0</allow>
    +<allow>1</allow>
     </property>
     <property>
     <groups>XWiki.XWikiAllGroup</groups>
     </property>
     <property>
    -<levels>edit</levels>
    +<levels>view</levels>
    +</property>
    +<property>
    +<users>XWiki.XWikiGuest</users>
     </property>
     </object>
     <content>{{velocity}}
     #set($email = "$!request.get('e')")
     #if($email == '')
    -= $msg.get('xe.admin.passwordreset.forgotusername') =
    -
    -$msg.get('xe.admin.passwordreset.enteremail')
    +$msg.get('xe.admin.forgotUsername.instructions')
     
     {{html}}
       &lt;form method="post" action=""&gt;
    -  &lt;div&gt;&lt;label for="e"&gt;$msg.get('xe.admin.passwordreset.email')&lt;/label&gt;&lt;input type="text" id="e" name="e"/&gt; &lt;span class="buttonwrapper"&gt;&lt;input type="submit" value="$msg.get('xe.admin.passwordreset.retrieve')" class="button"/&gt;&lt;/span&gt;&lt;/div&gt;
    +    &lt;div&gt;&lt;label for="e"&gt;$msg.get('xe.admin.forgotUsername.emailLabel')&lt;/label&gt; &lt;input type="text" id="e" name="e"/&gt; &lt;span class="buttonwrapper"&gt;&lt;input type="submit" value="$msg.get('xe.admin.forgotUsername.submit')" class="button"/&gt;&lt;/span&gt;&lt;/div&gt;
       &lt;/form&gt;
     {{/html}}
     
     #else
    -  #set($results = $xwiki.searchDocuments(", BaseObject obj, StringProperty prop where obj.name = doc.fullName and obj.className = 'XWiki.XWikiUsers' and prop.id.id = obj.id and prop.id.name = 'email' and prop.value = '$escapetool.sql($email)'"))
    +  #set($results = $xwiki.searchDocuments(", BaseObject obj, StringProperty prop where obj.name = doc.fullName and obj.className = 'XWiki.XWikiUsers' and prop.id.id = obj.id and prop.id.name = 'email' and LOWER(prop.value) = ?", [$email.toLowerCase()]))
       #if($results.size() == 0)
    -    $msg.get("xe.admin.passwordreset.noaccountregistered")
    +    $msg.get('xe.admin.forgotUsername.error.noAccount')
     
    -    [[&#171; $msg.get("xe.admin.passwordreset.differentaddress")&gt;&gt;$doc.fullName]] | {{html}}&lt;a href="$xwiki.getURL('XWiki.XWikiLogin', 'login', '')"&gt;$msg.get('xe.admin.passwordreset.login') &#187;&lt;/a&gt;{{/html}}
    +    [[$msg.get('xe.admin.forgotUsername.error.retry')&gt;&gt;$doc.fullName]] | [[$msg.get('xe.admin.forgotUsername.login')>>path:${xwiki.getURL('XWiki.XWikiLogin', 'login')}]]
       #elseif($results.size() == 1)
    -    $msg.get('xe.admin.passwordreset.usernameis') **${xwiki.getDocument($results.get(0)).name}**
    +    $msg.get('xe.admin.forgotUsername.result', ["**${results.get(0).substring($results.get(0).indexOf('.')).substring(1)}**"])
     
    -    {{html}}&lt;a href="$xwiki.getURL('XWiki.XWikiLogin', 'login', '')"&gt;$msg.get('xe.admin.passwordreset.login') &#187;&lt;/a&gt;{{/html}}
    +    [[$msg.get('xe.admin.forgotUsername.login')>>path:${xwiki.getURL('XWiki.XWikiLogin', 'login')}]]
       #else
    -    $msg.get('xe.admin.passwordreset.multipleusernames')
    +    $msg.get('xe.admin.forgotUsername.multipleResults')
         #foreach($item in $results)
    -      * **${xwiki.getDocument($item).name}**
    +      * **${item.substring($item.indexOf('.')).substring(1)}**
         #end
     
    -    {{html}}&lt;a href="$xwiki.getURL('XWiki.XWikiLogin', 'login', '')"&gt;$msg.get('xe.admin.passwordreset.login') &#187;&lt;/a&gt;{{/html}}
    +    [[$msg.get('xe.admin.forgotUsername.login')>>path:${xwiki.getURL('XWiki.XWikiLogin', 'login')}]]
       #end
     #end
     {{/velocity}}</content></xwikidoc>
    
  • xwiki-platform-administration/src/main/resources/XWiki/ResetPasswordComplete.xml+55 51 modified
    @@ -5,7 +5,7 @@
     <language></language>
     <defaultLanguage>en</defaultLanguage>
     <translation>0</translation>
    -<parent>XWiki.WebHome</parent>
    +<parent>XWiki.ResetPassword</parent>
     <creator>XWiki.Admin</creator>
     <author>XWiki.Admin</author>
     <customClass></customClass>
    @@ -14,13 +14,13 @@
     <date>1219710939000</date>
     <contentUpdateDate>1219710939000</contentUpdateDate>
     <version>1.1</version>
    -<title>Reset password (step 2)</title>
    +<title>$msg.get("xe.admin.passwordReset.step2.title")</title>
     <template></template>
     <defaultTemplate></defaultTemplate>
     <validationScript></validationScript>
     <comment></comment>
     <minorEdit>false</minorEdit>
    -<syntaxId>xwiki/2.0</syntaxId>
    +<syntaxId>xwiki/2.1</syntaxId>
     <hidden>false</hidden>
     <object>
     <class>
    @@ -89,18 +89,21 @@
     </users>
     </class>
     <name>XWiki.ResetPasswordComplete</name>
    -<number>1</number>
    +<number>0</number>
     <className>XWiki.XWikiRights</className>
     <guid>7fd88649-d66d-442a-bcee-bc4147e410a7</guid>
     <property>
     <allow>0</allow>
     </property>
     <property>
    -<groups>xwiki:XWiki.XWikiAllGroup</groups>
    +<groups>XWiki.XWikiAllGroup</groups>
     </property>
     <property>
     <levels>edit</levels>
     </property>
    +<property>
    +<users>XWiki.XWikiGuest</users>
    +</property>
     </object>
     <object>
     <class>
    @@ -169,14 +172,14 @@
     </users>
     </class>
     <name>XWiki.ResetPasswordComplete</name>
    -<number>3</number>
    +<number>1</number>
     <className>XWiki.XWikiRights</className>
    -<guid>e0264a66-4a10-4d7e-822e-6863e3caaa17</guid>
    +<guid>e0264a66-4a10-4d7e-822e-6863e3caaa18</guid>
     <property>
     <allow>0</allow>
     </property>
     <property>
    -<groups>XWiki.XWikiAllGroup</groups>
    +<groups>xwiki:XWiki.XWikiAllGroup</groups>
     </property>
     <property>
     <levels>edit</levels>
    @@ -249,14 +252,17 @@
     </users>
     </class>
     <name>XWiki.ResetPasswordComplete</name>
    -<number>5</number>
    +<number>2</number>
     <className>XWiki.XWikiRights</className>
    -<guid>bb790edf-7dcc-4646-9cf9-855b439cef7f</guid>
    +<guid>06daec64-228b-4da1-b7e6-b59d9624c856</guid>
     <property>
    -<allow>0</allow>
    +<allow>1</allow>
     </property>
     <property>
    -<levels>edit</levels>
    +<groups>XWiki.XWikiAllGroup</groups>
    +</property>
    +<property>
    +<levels>view</levels>
     </property>
     <property>
     <users>XWiki.XWikiGuest</users>
    @@ -286,7 +292,7 @@ it is secured against unprivileged editing.
     ##
     ##
     ## The name of the class used for storing password reset verification data.
    -#set($verifClass = 'XWiki.ResetPasswordRequestClass')
    +#set ($verifClass = 'XWiki.ResetPasswordRequestClass')
     ##
     ## START MACROS
     ##
    @@ -298,7 +304,7 @@ it is secured against unprivileged editing.
      * @param result The encrypted output.
      *#
     #macro(encrypt $value $result)
    -  #set($result = $xwiki.getDocument($verifClass).getxWikiClass().getXWikiClass().get('verification').getPasswordHash($value))
    +  #set ($result = $xwiki.getDocument($verifClass).getxWikiClass().getXWikiClass().get('verification').getPasswordHash($value))
     #end
     ##
     ##
    @@ -308,12 +314,13 @@ it is secured against unprivileged editing.
      * @param validationString The unencrypted key that is stored in the ResetPasswordRequestClass object.
      * @param result A boolean where the validation result is returned. True if the request is valid, false otherwise.
      *#
    -#macro(verifyRequest $userName $validationString $result)
    -  #set($result = false)
    -  #if($validationString != '' &amp;&amp; $userName != '')
    -    #encrypt($validationString $encryptedValidationString)
    -    #if("$!xwiki.getDocument($userName).getObject($verifClass).getProperty('verification').getValue()" == $encryptedValidationString)
    -      #set($result = true)
    +#macro(verifyRequest $userName $validationString $isValid)
    +  #set ($isValid = false)
    +  #if ($validationString != '' &amp;&amp; $userName != '')
    +    #encrypt($validationString $result)
    +    #set ($encryptedValidationString = $result)
    +    #if ("$!xwiki.getDocument($userName).getObject($verifClass).getProperty('verification').getValue()" == $encryptedValidationString)
    +      #set ($isValid = true)
         #end
       #end
     #end
    @@ -326,87 +333,84 @@ it is secured against unprivileged editing.
      * @param v The validation string, which will be checked again upon receiving the form.
      *###
     #macro(displayForm $message $userName $validationString)
    -  = $msg.get('xe.admin.passwordreset.resetfor', [${xwiki.getUserName($userName, false)}]) =
    -  #if($message != '')
    -
    +  #if ($message != '')
     {{warning}}$message{{/warning}}
       #end
     
     {{html}}
    -  &lt;form action="$doc.getURL()" method="post" onsubmit="if($('p').value == '') {alert('$msg.get("xe.admin.passwordreset.emptystring")'); return false;} else if($('p').value != $('p2').value) {alert('$msg.get("xe.admin.passwordreset.nomatch")'); return false; }"&gt;
    +  &lt;form action="$doc.getURL()" method="post" class="xform third" onsubmit="if($('p').value == '') {alert('$msg.get('xe.admin.passwordReset.step2.error.emptyPassword')'); return false;} else if($('p').value != $('p2').value) {alert('$msg.get('xe.admin.passwordReset.step2.error.verificationMismatch')'); return false; }"&gt;
         &lt;div class="hidden"&gt;
           &lt;input type="hidden" name="form_token" value="$!{services.csrf.getToken()}" /&gt;
           &lt;input type="hidden" name="u" value="$!escapetool.xml($userName)"/&gt;
           &lt;input type="hidden" name="v" value="$!escapetool.xml($validationString)"/&gt;
         &lt;/div&gt;
         &lt;dl&gt;
    -      &lt;dt&gt;&lt;label for="p"&gt;$msg.get('xe.admin.passwordreset.newpassword')&lt;/label&gt;&lt;/dt&gt;
    +      &lt;dt&gt;&lt;label for="p"&gt;$msg.get('xe.admin.passwordReset.step2.newPassword.label')&lt;/label&gt;&lt;/dt&gt;
           &lt;dd&gt;&lt;input id="p" type="password" name="p" value="" size="20"/&gt;&lt;/dd&gt;
    -      &lt;dt&gt;&lt;label for="p2"&gt;$msg.get('xe.admin.passwordreset.reenterpassword')&lt;/label&gt;&lt;/dt&gt;
    +      &lt;dt&gt;&lt;label for="p2"&gt;$msg.get('xe.admin.passwordReset.step2.newPasswordVerification.label')&lt;/label&gt;&lt;/dt&gt;
           &lt;dd&gt;&lt;input id="p2" type="password" value="" name="p2" size="20"/&gt;&lt;/dd&gt;
         &lt;/dl&gt;
    -    &lt;div&gt;
    -      &lt;span class="buttonwrapper"&gt;&lt;input type="submit" value="$msg.get('xe.admin.passwordreset.save')" class="button"/&gt;&lt;/span&gt;
    +    &lt;div class="buttons"&gt;
    +      &lt;span class="buttonwrapper"&gt;&lt;input type="submit" value="$msg.get('xe.admin.passwordReset.step2.submit')" class="button"/&gt;&lt;/span&gt;
         &lt;/div&gt;
       &lt;/form&gt;
     {{/html}}
     
     #end
     ##
     ## END MACROS
    -## 
     ##
    -#set($userName = "$!request.u")
    -#set($validationString = "$!request.v")
    -#set($password = "$!request.p")
    -#set($password2 = "$!request.p2")
    -#verifyRequest($userName $validationString $result)
    +##
    +#set ($userName = "$!request.u")
    +#set ($validationString = "$!request.v")
    +#set ($password = "$!request.p")
    +#set ($password2 = "$!request.p2")
    +#verifyRequest($userName $validationString $isValid)
     {{/velocity}}
     
     {{velocity}}
     ##
     ##
     ## First, check if the page has programming rights, as nothing works otherwise
    -#if($xwiki.hasProgrammingRights())
    -#if($result)
    -  #set($vuserDoc = $xwiki.getDocument($userName))
    -  #if($request.getParameterMap().containsKey('p'))## Second step, set the user password
    +#if ($xwiki.hasProgrammingRights())
    +#if ($isValid)
    +  #set ($vuserDoc = $xwiki.getDocumentAsAuthor($userName))
    +  #if ($request.getParameterMap().containsKey('p'))## Second step, set the user password
         #if($password == '')
    -      #displayForm($msg.get('xe.admin.passwordreset.notempty') $userName $validationString)
    +      #displayForm($msg.get('xe.admin.passwordReset.step2.error.emptyPassword') $userName $validationString)
         #elseif($password != $password2)
    -      #displayForm($msg.get('xe.admin.passwordreset.nomatch') $userName $validationString)
    +      #displayForm($msg.get('xe.admin.passwordReset.step2.error.verificationMismatch') $userName $validationString)
         #else
           $vuserDoc.getObject('XWiki.XWikiUsers').set('password', $password)
           #set($discard = $vuserDoc.removeObjects($verifClass))
    -      $vuserDoc.saveWithProgrammingRights()
    -{{info}}$msg.get('xe.admin.passwordreset.success') {{html}}&lt;a href='$xwiki.getURL('XWiki.XWikiLogin', 'login')'&gt;$msg.get('xe.admin.passwordreset.loginsmall')&lt;/a&gt;{{/html}} $msg.get('xe.admin.passwordreset.successend'){{/info}}
    +      #set ($discard = $vuserDoc.saveAsAuthor($msg.get('xe.admin.passwordReset.step2.versionComment.passwordReset'), true))
    +{{info}}$msg.get('xe.admin.passwordReset.step2.success') [[$msg.get('xe.admin.passwordReset.step2.login')>>path:$xwiki.getURL('XWiki.XWikiLogin', 'login')]]{{/info}}
     
         #end
       #else## First step, request the user password
         ## The user might not complete this step, and leave the URL in the (public) browser's
         ## history. Prevent reusing the URL by invalidating the initial verification URL and only
         ## post the new string in the hidden form data.
    -    #set($validationString = $util.generateRandomString(30))
    -    $vuserDoc.getObject($verifClass).set('verification', $validationString)
    -    $vuserDoc.saveWithProgrammingRights()
    +    #set ($validationString = $util.generateRandomString(30))
    +    #set ($discard = $vuserDoc.getObject($verifClass).set('verification', $validationString))
    +    #set ($discard = $vuserDoc.saveAsAuthor($msg.get('xe.admin.passwordReset.step2.versionComment.changeValidationKey'), true))
         #displayForm('' $userName $validationString)
       #end
     #else
    -  #set($backToResetLink = " [[$msg.get('xe.admin.passwordreset.backtoreset') »&gt;&gt;ResetPassword]]")
     
    -{{error}}$msg.get('xe.admin.passwordreset.wrongparameters') ${backToResetLink}{{/error}}
    +{{error}}$msg.get('xe.admin.passwordReset.step2.error.wrongParameters') [[$msg.get('xe.admin.passwordReset.step2.backToStep1')&gt;&gt;ResetPassword]]{{/error}}
     
     #end
     ##
     ## Clear private variables, so that they cannot be accessed from the rest of the page (comments, panels...)
    -#set($validationString = '')
    -#set($password = '')
    -#set($password2 = '')
    +#set ($validationString = '')
    +#set ($password = '')
    +#set ($password2 = '')
     ##
     ##
     #else## No programming rights, warn and exit
     
    -{{error}}$msg.get('xe.admin.passwordreset.noprogrammingrights'){{/error}}
    +{{error}}$msg.get('xe.admin.passwordReset.step2.error.noProgrammingRights'){{/error}}
     
     #end
     {{/velocity}}</content></xwikidoc>
    
  • xwiki-platform-administration/src/main/resources/XWiki/ResetPasswordMailContent.xml+1 1 modified
    @@ -5,7 +5,7 @@
     <language></language>
     <defaultLanguage></defaultLanguage>
     <translation>0</translation>
    -<parent>XWiki.WebHome</parent>
    +<parent>XWiki.ResetPassword</parent>
     <creator>XWiki.Admin</creator>
     <author>XWiki.Admin</author>
     <customClass></customClass>
    
  • xwiki-platform-administration/src/main/resources/XWiki/ResetPasswordRequestClass.xml+1 1 modified
    @@ -5,7 +5,7 @@
     <language></language>
     <defaultLanguage></defaultLanguage>
     <translation>0</translation>
    -<parent>XWiki.WebHome</parent>
    +<parent>XWiki.ResetPassword</parent>
     <creator>XWiki.Admin</creator>
     <author>XWiki.Admin</author>
     <customClass></customClass>
    
  • xwiki-platform-administration/src/main/resources/XWiki/ResetPassword.xml+46 65 modified
    @@ -5,7 +5,7 @@
     <language></language>
     <defaultLanguage>en</defaultLanguage>
     <translation>0</translation>
    -<parent>XWiki.WebHome</parent>
    +<parent>Main.WebHome</parent>
     <creator>XWiki.Admin</creator>
     <author>XWiki.Admin</author>
     <customClass></customClass>
    @@ -14,13 +14,13 @@
     <date>1228221475000</date>
     <contentUpdateDate>1228221475000</contentUpdateDate>
     <version>1.1</version>
    -<title>$msg.get("xe.admin.passwordreset.forgotpassword")</title>
    +<title>$msg.get("xe.admin.passwordReset.title")</title>
     <template></template>
     <defaultTemplate></defaultTemplate>
     <validationScript></validationScript>
     <comment></comment>
     <minorEdit>false</minorEdit>
    -<syntaxId>xwiki/2.0</syntaxId>
    +<syntaxId>xwiki/2.1</syntaxId>
     <hidden>false</hidden>
     <object>
     <class>
    @@ -101,6 +101,9 @@
     <property>
     <levels>edit</levels>
     </property>
    +<property>
    +<users>XWiki.XWikiGuest</users>
    +</property>
     </object>
     <object>
     <class>
    @@ -171,15 +174,15 @@
     <name>XWiki.ResetPassword</name>
     <number>1</number>
     <className>XWiki.XWikiRights</className>
    -<guid>8e791339-668e-4a3c-9d44-a28396c1d66f</guid>
    +<guid>bb00007c-00dd-4423-9b7b-b87896c947fb</guid>
     <property>
     <allow>0</allow>
     </property>
     <property>
    -<levels>edit</levels>
    +<groups>xwiki:XWiki.XWikiAllGroup</groups>
     </property>
     <property>
    -<users>XWiki.XWikiGuest</users>
    +<levels>edit</levels>
     </property>
     </object>
     <object>
    @@ -251,15 +254,18 @@
     <name>XWiki.ResetPassword</name>
     <number>2</number>
     <className>XWiki.XWikiRights</className>
    -<guid>bb00007c-00dd-4423-9b7b-b87896c947fb</guid>
    +<guid>06daec64-228b-4da1-b7e6-b59d9624c866</guid>
     <property>
    -<allow>0</allow>
    +<allow>1</allow>
     </property>
     <property>
    -<groups>xwiki:XWiki.XWikiAllGroup</groups>
    +<groups>XWiki.XWikiAllGroup</groups>
     </property>
     <property>
    -<levels>edit</levels>
    +<levels>view</levels>
    +</property>
    +<property>
    +<users>XWiki.XWikiGuest</users>
     </property>
     </object>
     <content>{{velocity}}
    @@ -272,99 +278,74 @@ This page starts the password reset procedure. It works according to the next al
     URL parameters:
     
     u = user account sent in the form
    -
    -!!!!! IMPORTANT !!!!!
    -
    -This document requires programming rights, so always make sure
    -it is saved by a user with programming rights, and that
    -it is secured against unprivileged editing.
    -
     *###
     ##
     ##
    -## First, check if the page has programming rights, as nothing works otherwise
    -#if($xwiki.hasProgrammingRights())
    -##
    -##
     ## The name of the class used for storing password reset verification data.
    -#set($verifClass = 'XWiki.ResetPasswordRequestClass')
    -#set($userName = "$!request.get('u')")
    -#if($userName == '')## First step, display the form requesting the username
    -  = $msg.get('xe.admin.passwordreset.forgotpassword') =
    -
    -  $msg.get('xe.admin.passwordreset.startprocess')
    +#set ($verifClass = 'XWiki.ResetPasswordRequestClass')
    +#set ($userName = "$!request.get('u')")
    +#if ($userName == '')## First step, display the form requesting the username
    +  $msg.get('xe.admin.passwordReset.instructions')
     
       {{html}}
    -  &lt;form method="post" action=""&gt;
    +  &lt;form method="post" action="" class="xformInline"&gt;
       &lt;div&gt;
       &lt;input type="hidden" name="form_token" value="$!{services.csrf.getToken()}" /&gt;
    -  &lt;label for="u"&gt;$msg.get('xe.admin.passwordreset.username')&lt;/label&gt; &lt;input type="text" id="u" name="u"/&gt; &lt;span class="buttonwrapper"&gt;&lt;input type="submit" value="$msg.get('xe.admin.passwordreset.resetpassword')" class="button"/&gt;&lt;/span&gt;
    +  &lt;label for="u"&gt;$msg.get('xe.admin.passwordReset.username.label')&lt;/label&gt; &lt;input type="text" id="u" name="u"/&gt; &lt;span class="buttonwrapper"&gt;&lt;input type="submit" value="$msg.get('xe.admin.passwordReset.submit')" class="button"/&gt;&lt;/span&gt;
       &lt;/div&gt;
       &lt;/form&gt;
       {{/html}}
     #else## Second step, generate the verification string, store it, and send the email
       ## TODO: Once the usernames are not bound to the XWiki space, revisit this code
    -  #if($userName.indexOf('.') != -1)
    -    #set($userDoc = $xwiki.getDocument(${userName}))
    +  #if ($userName.indexOf('.') != -1)
    +    #set ($userDoc = $xwiki.getDocumentAsAuthor(${userName}))
       #else
    -    #set($userDoc = $xwiki.getDocument("XWiki.${userName}"))
    +    #set ($userDoc = $xwiki.getDocumentAsAuthor("XWiki.${userName}"))
       #end
       ## Check if the user exists and has a valid email address configured in his profile
    -  #set($userObj = '')
    -  #set($userObj = $userDoc.getObject('XWiki.XWikiUsers'))
    -  #if("$!userObj" == '')
    +  #set ($userObj = '')
    +  #set ($userObj = $userDoc.getObject('XWiki.XWikiUsers'))
    +  #if (!$userObj)
     
    -    {{warning}}$msg.get('xe.admin.passwordreset.nouser', [$escapetool.xml($userName)]){{/warning}}
    +    {{warning}}$msg.get('xe.admin.passwordReset.error.noUser', ["//${escapetool.xml($userName)}//"]){{/warning}}
     
       #elseif ($userDoc.getObject('XWiki.LDAPProfileClass'))
     
    -    {{warning}}$msg.get('xe.admin.passwordreset.ldapuser', [$escapetool.xml($userName)]){{/warning}}
    +    {{warning}}$msg.get('xe.admin.passwordReset.error.ldapUser', ["//${escapetool.xml($userName)}//"]){{/warning}}
     
       #else
    -    #set($userEmail = $userObj.getProperty('email').value)
    -    #if("$!userEmail" == '')
    +    #set ($userEmail = $userObj.getProperty('email').value)
    +    #if ("$!userEmail" == '')
     
    -      {{error}}$msg.get('xe.admin.passwordreset.cannotreset'){{/error}}
    +      {{error}}$msg.get('xe.admin.passwordReset.error.noEmail'){{/error}}
     
         #else
           ## Find the object that will hold the verification string
    -      #set($verifObj = '')
    -      #set($verifObj = $userDoc.getObject($verifClass))
    -      #if("$!verifObj" == '')
    -        #set($verifObj = $userDoc.newObject($verifClass))
    -      #end
    +      #set ($verifObj = '')
    +      #set ($verifObj = $userDoc.getObject($verifClass, true))
           ## Generate a random string
    -      #set($verifStr = $xwiki.generateRandomString(30))
    +      #set ($verifStr = $xwiki.generateRandomString(30))
           ## If the class is correctly configured, the string should automatically be stored as a hash
    -      #set($discard = $verifObj.set('verification', $verifStr))
    -      #set($discard = $userDoc.saveWithProgrammingRights())
    +      #set ($discard = $verifObj.set('verification', $verifStr))
    +      #set ($discard = $userDoc.saveAsAuthor($msg.get('xe.admin.passwordReset.versionComment'), true))
           ## Compose the verification URL
    -      #set($passwordResetURL = $xwiki.getDocument('XWiki.ResetPasswordComplete').getExternalURL('view', "u=${userName}&amp;amp;v=${verifStr}"))
    +      #set ($passwordResetURL = $xwiki.getDocument('XWiki.ResetPasswordComplete').getExternalURL('view', "u=${userName}&amp;amp;v=${verifStr}"))
           ## Send an email; the variables will be retrieved from the velocity context
    -      #set($mailResult = $xwiki.mailsender.sendMessageFromTemplate($xwiki.getXWikiPreference('admin_email', 'no-reply@xwiki.org'), $userEmail, $util.null, $util.null, $xcontext.language, 'XWiki.ResetPasswordMailContent', $xcontext.vcontext))
    -      #if($mailResult == 0)
    +      #set ($mailResult = $xwiki.mailsender.sendMessageFromTemplate($xwiki.getXWikiPreference('admin_email', 'no-reply@xwiki.org'), $userEmail, $util.null, $util.null, $xcontext.language, 'XWiki.ResetPasswordMailContent', $util.null))
    +      #if ($mailResult == 0)
     
    -        {{info}}$msg.get('xe.admin.passwordreset.emailsent', [$escapetool.xml($userEmail)]){{/info}}
    +        {{info}}$msg.get('xe.admin.passwordReset.emailSent', ["${escapetool.h}#${escapetool.xml($userEmail)}${escapetool.h}#"]){{/info}}
     
           #else
     
    -        {{error}}$msg.get('xe.admin.passwordreset.reseterror'){{/error}}
    +        {{error}}$msg.get('xe.admin.passwordReset.error.emailFailed'){{/error}}
     
           #end
         #end
       #end
    -  {{html}}
    -  &lt;a href="$doc.getURL()"&gt;&#171; $msg.get('xe.admin.passwordreset.retry')&lt;/a&gt; |
    -  &lt;a href="$xwiki.getURL('XWiki.XWikiLogin', 'login', '')"&gt;$msg.get('xe.admin.passwordreset.login') &#187;&lt;/a&gt;
    -  {{/html}}
    +  [[$msg.get('xe.admin.passwordReset.error.retry')>>$doc.fullName]] | [[$msg.get('xe.admin.passwordReset.error.recoverUsername')>>ForgotUsername]] | [[$msg.get('xe.admin.passwordReset.login')>>path:$xwiki.getURL('XWiki.XWikiLogin', 'login')]]
     #end
     ## Clear private variables, so that they cannot be accessed from the rest of the page (comments, panels...)
    -#set($verifStr = '')
    -#set($passwordResetURL = '')
    -##
    -##
    -#else
    -  ## No programming rights, warn and exit
    -  {{error}}$msg.get('xe.admin.passwordreset.noprogrammingrights'){{/error}}
    -#end
    +#set ($verifStr = '')
    +#set ($passwordResetURL = '')
     {{/velocity}}</content></xwikidoc>
    

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.