CVE-2024-37899 (Java, published 2024-06-20T23:15Z)
Description: value: XWiki Platform is a generic wiki platform offering runtime services for applications built on top of it. When an admin disables a user account, the user's profile is executed with the admin's rights. This allows a user to place malicious code in the user profile before getting an admin to disable the user account. To reproduce, as a user without script nor programming rights, edit the about section of your user profile and add `{{groovy}}services.logging.getLogger("attacker").error("Hello from Groovy!"){{/groovy}}`.
As an admin, go to the user profile and click the "Disable this account" button. Then, reload the page. If the logs show `attacker - Hello from Groovy!` then the instance is vulnerable. This has been patched in XWiki 14.10.21, 15.5.5, 15.10.6 and 16.0.0. Users are advised to upgrade. There are no known workarounds for this vulnerability.

### Workarounds
We're not aware of any workaround except upgrading.

### References
* https://jira.xwiki.org/browse/XWIKI-21611
* https://github.com/xwiki/xwiki-platform/commit/f89c8f47fad6e5cc7e68c69a7e0acde07f5eed5a

Severity: nan (CVSS v3 base score: )
Weakness: NVD-CWE-noinfo \u2014 Insufficient Information
There is insufficient information about the issue to classify it; details are unkown or unspecified.
Repository: https://github.com/xwiki/xwiki-platform
Files changed:
xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/user/api/XWikiUser.java
xwiki-platform-core/xwiki-platform-oldcore/src/test/java/com/xpn/xwiki/user/api/XWikiUserTest.java
xwiki-platform-core/xwiki-platform-web/xwiki-platform-web-templates/src/main/resources/templates/getusers.vm
Commit message: XWIKI-21611: Set right author when disabling/enabling an account

* Set only the original metadata author in XWikiUser.setDisabled
* Use the same disable/enable action everywhere
* Remove no longer needed request parameters


Vulnerable code:
                userdoc.setAuthorReference(context.getUserReference());
    private DocumentReference userClassReference = new DocumentReference("xwiki", "XWiki", "XWikiUsers");
    private DocumentReference userReference = new DocumentReference("xwiki", "XWiki", "Foo");
        when(mockitoOldcore.getSpyXWiki().getDocument(userReference, mockitoOldcore.getXWikiContext()))
            .thenReturn(userDocument);
        when(userDocument.getDocumentReference()).thenReturn(userReference);
        when(userDocument.getDocumentReferenceWithLocale()).thenReturn(userReference);
        when(userDocument.clone()).thenReturn(userDocument);
        XWikiUser user = new XWikiUser(userReference);
        when(userDocument.getIntValue(userClassReference, XWikiUser.ACTIVE_PROPERTY, 1)).thenReturn(1);
        assertFalse(user.isDisabled(mockitoOldcore.getXWikiContext()));
        when(userDocument.getIntValue(userClassReference, XWikiUser.ACTIVE_PROPERTY, 1)).thenReturn(0);
        assertTrue(user.isDisabled(mockitoOldcore.getXWikiContext()));
        assertFalse(user.isDisabled(mockitoOldcore.getXWikiContext()));
        assertFalse(user.isDisabled(mockitoOldcore.getXWikiContext()));
        XWikiUser user = new XWikiUser(userReference);
        user.setDisabled(false, mockitoOldcore.getXWikiContext());
        verify(userDocument, times(1)).setIntValue(userClassReference, XWikiUser.ACTIVE_PROPERTY, 1);
        verify(mockitoOldcore.getSpyXWiki(), times(1))
            .saveDocument(same(userDocument), any(String.class), same(mockitoOldcore.getXWikiContext()));
        XWikiUser user = new XWikiUser(userReference);
        user.setDisabled(true, mockitoOldcore.getXWikiContext());
        verify(userDocument, times(1)).setIntValue(userClassReference, XWikiUser.ACTIVE_PROPERTY, 0);
        verify(mockitoOldcore.getSpyXWiki(), times(1))
            .saveDocument(same(userDocument), any(String.class), same(mockitoOldcore.getXWikiContext()));
        user.setDisabled(true, mockitoOldcore.getXWikiContext());
        verify(userDocument, never())
            .setIntValue(same(userClassReference), any(String.class), any(Integer.class));
        verify(mockitoOldcore.getSpyXWiki(), never())
            .saveDocument(any(XWikiDocument.class), any(String.class), same(mockitoOldcore.getXWikiContext()));

        user.setDisabled(false, mockitoOldcore.getXWikiContext());
        verify(userDocument, never())
            .setIntValue(same(userClassReference), any(String.class), any(Integer.class));
        verify(mockitoOldcore.getSpyXWiki(), never())
            .saveDocument(any(XWikiDocument.class), any(String.class), same(mockitoOldcore.getXWikiContext()));
        user.setDisabled(true, mockitoOldcore.getXWikiContext());
        verify(userDocument, never())
            .setIntValue(same(userClassReference), any(String.class), any(Integer.class));
        verify(mockitoOldcore.getSpyXWiki(), never())
            .saveDocument(any(XWikiDocument.class), any(String.class), same(mockitoOldcore.getXWikiContext()));

        user.setDisabled(false, mockitoOldcore.getXWikiContext());
        verify(userDocument, never())
            .setIntValue(same(userClassReference), any(String.class), any(Integer.class));
        verify(mockitoOldcore.getSpyXWiki(), never())
            .saveDocument(any(XWikiDocument.class), any(String.class), same(mockitoOldcore.getXWikiContext()));
        XWikiUser user = new XWikiUser(userReference);
        when(userDocument.getIntValue(userClassReference, XWikiUser.EMAIL_CHECKED_PROPERTY, 1)).thenReturn(1);
        assertTrue(user.isEmailChecked(mockitoOldcore.getXWikiContext()));
        when(userDocument.getIntValue(userClassReference, XWikiUser.EMAIL_CHECKED_PROPERTY, 1)).thenReturn(0);
        assertFalse(user.isEmailChecked(mockitoOldcore.getXWikiContext()));
        assertTrue(user.isEmailChecked(mockitoOldcore.getXWikiContext()));
        assertTrue(user.isEmailChecked(mockitoOldcore.getXWikiContext()));
        XWikiUser user = new XWikiUser(userReference);
        user.setEmailChecked(false, mockitoOldcore.getXWikiContext());
        verify(userDocument, times(1)).setIntValue(userClassReference, XWikiUser.EMAIL_CHECKED_PROPERTY, 0);
        verify(mockitoOldcore.getSpyXWiki(), times(1))
            .saveDocument(same(userDocument), any(String.class), same(mockitoOldcore.getXWikiContext()));
        XWikiUser user = new XWikiUser(userReference);
        user.setEmailChecked(true, mockitoOldcore.getXWikiContext());
        verify(userDocument, times(1)).setIntValue(userClassReference, XWikiUser.EMAIL_CHECKED_PROPERTY, 1);
        verify(mockitoOldcore.getSpyXWiki(), times(1))
            .saveDocument(same(userDocument), any(String.class), same(mockitoOldcore.getXWikiContext()));
        user.setEmailChecked(true, mockitoOldcore.getXWikiContext());
        verify(userDocument, never())
            .setIntValue(same(userClassReference), any(String.class), any(Integer.class));
        verify(mockitoOldcore.getSpyXWiki(), never())
            .saveDocument(any(XWikiDocument.class), any(String.class), same(mockitoOldcore.getXWikiContext()));

        user.setEmailChecked(false, mockitoOldcore.getXWikiContext());
        verify(userDocument, never())
            .setIntValue(same(userClassReference), any(String.class), any(Integer.class));
        verify(mockitoOldcore.getSpyXWiki(), never())
            .saveDocument(any(XWikiDocument.class), any(String.class), same(mockitoOldcore.getXWikiContext()));
        user.setEmailChecked(true, mockitoOldcore.getXWikiContext());
        verify(userDocument, never())
            .setIntValue(same(userClassReference), any(String.class), any(Integer.class));
        verify(mockitoOldcore.getSpyXWiki(), never())
            .saveDocument(any(XWikiDocument.class), any(String.class), same(mockitoOldcore.getXWikiContext()));

        user.setEmailChecked(false, mockitoOldcore.getXWikiContext());
        verify(userDocument, never())
            .setIntValue(same(userClassReference), any(String.class), any(Integer.class));
        verify(mockitoOldcore.getSpyXWiki(), never())
            .saveDocument(any(XWikiDocument.class), any(String.class), same(mockitoOldcore.getXWikiContext()));
      'doc_disable_url': $user.getURL('save', $escapetool.url({
        "${userClassName}_0_active": 0,
        'comment': $services.localization.render('core.users.disable.saveComment'),
        'form_token': $services.csrf.token
      'doc_enable_url': $user.getURL('save', $escapetool.url({
        "${userClassName}_0_active": 1,
        'comment': $services.localization.render('core.users.enable.saveComment'),
        'form_token': $services.csrf.token

Fixed code:
import org.xwiki.user.UserReference;
import org.xwiki.user.UserReferenceResolver;
    private UserReferenceResolver<DocumentReference> documentReferenceUserReferenceResolver;

    private UserReferenceResolver<DocumentReference> getDocumentReferenceUserReferenceResolver()
    {
        if (this.documentReferenceUserReferenceResolver == null) {
            this.documentReferenceUserReferenceResolver =
                Utils.getComponent(UserReferenceResolver.TYPE_DOCUMENT_REFERENCE, "document");
        }
        return this.documentReferenceUserReferenceResolver;
    }

                UserReference userReference =
                    getDocumentReferenceUserReferenceResolver().resolve(context.getUserReference());
                userdoc.getAuthors().setOriginalMetadataAuthor(userReference);
import javax.inject.Named;

import org.xwiki.model.document.DocumentAuthors;
import org.xwiki.test.junit5.mockito.MockComponent;
import org.xwiki.user.UserReferenceResolver;
    @MockComponent
    @Named("document")
    private UserReferenceResolver<DocumentReference> documentReferenceUserReferenceResolver;

    @Mock
    private DocumentAuthors authors;

    private final DocumentReference userClassReference = new DocumentReference("xwiki", "XWiki", "XWikiUsers");
    private final DocumentReference userReference = new DocumentReference("xwiki", "XWiki", "Foo");
        when(this.mockitoOldcore.getSpyXWiki().getDocument(this.userReference, this.mockitoOldcore.getXWikiContext()))
            .thenReturn(this.userDocument);
        when(this.userDocument.getDocumentReference()).thenReturn(this.userReference);
        when(this.userDocument.getDocumentReferenceWithLocale()).thenReturn(this.userReference);
        when(this.userDocument.getAuthors()).thenReturn(this.authors);
        when(this.userDocument.clone()).thenReturn(this.userDocument);
        XWikiUser user = new XWikiUser(this.userReference);
        when(this.userDocument.getIntValue(this.userClassReference, XWikiUser.ACTIVE_PROPERTY, 1)).thenReturn(1);
        assertFalse(user.isDisabled(this.mockitoOldcore.getXWikiContext()));
        when(this.userDocument.getIntValue(this.userClassReference, XWikiUser.ACTIVE_PROPERTY, 1)).thenReturn(0);
        assertTrue(user.isDisabled(this.mockitoOldcore.getXWikiContext()));
        assertFalse(user.isDisabled(this.mockitoOldcore.getXWikiContext()));
        assertFalse(user.isDisabled(this.mockitoOldcore.getXWikiContext()));
        XWikiUser user = new XWikiUser(this.userReference);
        user.setDisabled(false, this.mockitoOldcore.getXWikiContext());
        verify(this.userDocument, times(1)).setIntValue(this.userClassReference, XWikiUser.ACTIVE_PROPERTY, 1);
        verify(this.mockitoOldcore.getSpyXWiki(), times(1))
            .saveDocument(same(this.userDocument), any(String.class), same(this.mockitoOldcore.getXWikiContext()));
        verify(this.authors, times(1)).setOriginalMetadataAuthor(any());
        verify(this.authors, never()).setEffectiveMetadataAuthor(any());
        XWikiUser user = new XWikiUser(this.userReference);
        user.setDisabled(true, this.mockitoOldcore.getXWikiContext());
        verify(this.userDocument, times(1)).setIntValue(this.userClassReference, XWikiUser.ACTIVE_PROPERTY, 0);
        verify(this.mockitoOldcore.getSpyXWiki(), times(1))
            .saveDocument(same(this.userDocument), any(String.class), same(this.mockitoOldcore.getXWikiContext()));
        verify(this.authors, times(1)).setOriginalMetadataAuthor(any());
        verify(this.authors, never()).setEffectiveMetadataAuthor(any());
        user.setDisabled(true, this.mockitoOldcore.getXWikiContext());
        verify(this.userDocument, never())
            .setIntValue(same(this.userClassReference), any(String.class), any(Integer.class));
        verify(this.mockitoOldcore.getSpyXWiki(), never())
            .saveDocument(any(XWikiDocument.class), any(String.class), same(this.mockitoOldcore.getXWikiContext()));

        user.setDisabled(false, this.mockitoOldcore.getXWikiContext());
        verify(this.userDocument, never())
            .setIntValue(same(this.userClassReference), any(String.class), any(Integer.class));
        verify(this.mockitoOldcore.getSpyXWiki(), never())
            .saveDocument(any(XWikiDocument.class), any(String.class), same(this.mockitoOldcore.getXWikiContext()));
        user.setDisabled(true, this.mockitoOldcore.getXWikiContext());
        verify(this.userDocument, never())
            .setIntValue(same(this.userClassReference), any(String.class), any(Integer.class));
        verify(this.mockitoOldcore.getSpyXWiki(), never())
            .saveDocument(any(XWikiDocument.class), any(String.class), same(this.mockitoOldcore.getXWikiContext()));

        user.setDisabled(false, this.mockitoOldcore.getXWikiContext());
        verify(this.userDocument, never())
            .setIntValue(same(this.userClassReference), any(String.class), any(Integer.class));
        verify(this.mockitoOldcore.getSpyXWiki(), never())
            .saveDocument(any(XWikiDocument.class), any(String.class), same(this.mockitoOldcore.getXWikiContext()));
        XWikiUser user = new XWikiUser(this.userReference);
        when(this.userDocument.getIntValue(this.userClassReference, XWikiUser.EMAIL_CHECKED_PROPERTY, 1)).thenReturn(1);
        assertTrue(user.isEmailChecked(this.mockitoOldcore.getXWikiContext()));
        when(this.userDocument.getIntValue(this.userClassReference, XWikiUser.EMAIL_CHECKED_PROPERTY, 1)).thenReturn(0);
        assertFalse(user.isEmailChecked(this.mockitoOldcore.getXWikiContext()));
        assertTrue(user.isEmailChecked(this.mockitoOldcore.getXWikiContext()));
        assertTrue(user.isEmailChecked(this.mockitoOldcore.getXWikiContext()));
        XWikiUser user = new XWikiUser(this.userReference);
        user.setEmailChecked(false, this.mockitoOldcore.getXWikiContext());
        verify(this.userDocument, times(1)).setIntValue(this.userClassReference, XWikiUser.EMAIL_CHECKED_PROPERTY, 0);
        verify(this.mockitoOldcore.getSpyXWiki(), times(1))
            .saveDocument(same(this.userDocument), any(String.class), same(this.mockitoOldcore.getXWikiContext()));
        XWikiUser user = new XWikiUser(this.userReference);
        user.setEmailChecked(true, this.mockitoOldcore.getXWikiContext());
        verify(this.userDocument, times(1)).setIntValue(this.userClassReference, XWikiUser.EMAIL_CHECKED_PROPERTY, 1);
        verify(this.mockitoOldcore.getSpyXWiki(), times(1))
            .saveDocument(same(this.userDocument), any(String.class), same(this.mockitoOldcore.getXWikiContext()));
        user.setEmailChecked(true, this.mockitoOldcore.getXWikiContext());
        verify(this.userDocument, never())
            .setIntValue(same(this.userClassReference), any(String.class), any(Integer.class));
        verify(this.mockitoOldcore.getSpyXWiki(), never())
            .saveDocument(any(XWikiDocument.class), any(String.class), same(this.mockitoOldcore.getXWikiContext()));

        user.setEmailChecked(false, this.mockitoOldcore.getXWikiContext());
        verify(this.userDocument, never())
            .setIntValue(same(this.userClassReference), any(String.class), any(Integer.class));
        verify(this.mockitoOldcore.getSpyXWiki(), never())
            .saveDocument(any(XWikiDocument.class), any(String.class), same(this.mockitoOldcore.getXWikiContext()));
        user.setEmailChecked(true, this.mockitoOldcore.getXWikiContext());
        verify(this.userDocument, never())
            .setIntValue(same(this.userClassReference), any(String.class), any(Integer.class));
        verify(this.mockitoOldcore.getSpyXWiki(), never())
            .saveDocument(any(XWikiDocument.class), any(String.class), same(this.mockitoOldcore.getXWikiContext()));

        user.setEmailChecked(false, this.mockitoOldcore.getXWikiContext());
        verify(this.userDocument, never())
            .setIntValue(same(this.userClassReference), any(String.class), any(Integer.class));
        verify(this.mockitoOldcore.getSpyXWiki(), never())
            .saveDocument(any(XWikiDocument.class), any(String.class), same(this.mockitoOldcore.getXWikiContext()));
      'doc_disable_url': $xwiki.getURL('XWiki.XWikiUserProfileSheet', 'get', $escapetool.url({
        'outputSyntax': 'plain',
        'action': 'disable',
        'userId': $user.documentReference,
        'csrf': $services.csrf.token
      'doc_enable_url': $xwiki.getURL('XWiki.XWikiUserProfileSheet', 'get', $escapetool.url({
        'outputSyntax': 'plain',
        'action': 'enable',
        'userId': $user.documentReference,
        'csrf': $services.csrf.token