CVE-2024-31987 (Java, published 2024-04-10T21:15Z)
Description: value: XWiki Platform is a generic wiki platform. Starting in version 6.4-milestone-1 and prior to versions 4.10.19, 15.5.4, and 15.10-rc-1, any user who can edit any page like their profile can create a custom skin with a template override that is executed with programming right, thus allowing remote code execution. This has been patched in XWiki 14.10.19, 15.5.4 and 15.10RC1. No known workarounds are available except for upgrading.
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/org/xwiki/internal/velocity/XWikiVelocityManager.java
xwiki-platform-core/xwiki-platform-oldcore/src/test/java/org/xwiki/internal/velocity/XWikiVelocityManagerTest.java
Commit message: XWIKI-21478: Improve rights check of template macros


Vulnerable code:
        Arrays.<Event>asList(new TemplateUpdatedEvent(), new TemplateDeletedEvent());
    private Map<String, VelocityEngine> velocityEngines = new ConcurrentHashMap<>();
                    velocityEngines.remove(templateEvent.getId());
     *         Velocity Engine for the current skin since each skin has its own Velocity Engine so that each skin can
     *         have global velocimacros defined
        // Inject skin macros
        if (skinMacrosTemplate != null) {
            VelocityTemplate skinMacros = compile("", new StringReader(skinMacrosTemplate.getContent().getContent()));

import org.xwiki.component.manager.ComponentLookupException;
 * 
@ComponentList(value = {DefaultScriptContextManager.class, XWikiScriptContextInitializer.class,
    DefaultVelocityConfiguration.class, DefaultLoggerConfiguration.class})
    public void beforeEach() throws ComponentLookupException
    public void getVelocityContext()
        VelocityContext context = velocityManager.getVelocityContext();

Fixed code:
import org.xwiki.security.authorization.AuthorExecutor;
import org.xwiki.security.authorization.AuthorizationManager;
import org.xwiki.security.authorization.Right;
        Arrays.asList(new TemplateUpdatedEvent(), new TemplateDeletedEvent());
    @Inject
    private AuthorExecutor authorExecutor;

    @Inject
    private AuthorizationManager authorizationManager;

    private final Map<String, VelocityEngine> velocityEngines = new ConcurrentHashMap<>();
                    XWikiVelocityManager.this.velocityEngines.remove(templateEvent.getId());
     *     Velocity Engine for the current skin since each skin has its own Velocity Engine so that each skin can have
     *     global velocimacros defined
        // Inject skin macros if their author has at least Script rights.
        if (skinMacrosTemplate != null
            && this.authorizationManager.hasAccess(Right.SCRIPT, skinMacrosTemplate.getContent().getAuthorReference(),
            skinMacrosTemplate.getContent().getDocumentReference()))
        {
            VelocityTemplate skinMacros =
                compile("", new StringReader(skinMacrosTemplate.getContent().getContent()));
import org.mockito.Mock;
import org.xwiki.properties.ConverterManager;
import org.xwiki.security.authorization.AuthorizationManager;
import org.xwiki.security.authorization.Right;
import org.xwiki.skin.Skin;
import org.xwiki.skin.SkinManager;
import org.xwiki.template.Template;
import org.xwiki.template.TemplateContent;
import org.xwiki.template.TemplateManager;
import org.xwiki.velocity.VelocityEngine;
import static org.mockito.ArgumentMatchers.anyMap;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
 *
@ComponentList({
    DefaultScriptContextManager.class,
    XWikiScriptContextInitializer.class,
    DefaultVelocityConfiguration.class,
    DefaultLoggerConfiguration.class
})
    private static final DocumentReference TEMPLATE_DOCUMENT = new DocumentReference("xwiki", "XWiki", "TestMacros");

    private static final DocumentReference SCRIPT_USER = new DocumentReference("xwiki", "XWiki", "Script");

    @MockComponent
    private SkinManager skinManager;

    @Mock
    private Skin skin;

    @MockComponent
    private TemplateManager templateManager;

    @Mock
    private Template template;

    @Mock
    private TemplateContent templateContent;

    @MockComponent
    private ConverterManager converterManager;

    void beforeEach() throws Exception

        when(this.skinManager.getCurrentSkin(true)).thenReturn(this.skin);
        when(this.templateManager.getTemplate("macros.vm")).thenReturn(this.template);
        when(this.template.getId()).thenReturn("testMacros");
        when(this.template.getContent()).thenReturn(this.templateContent);
        when(this.templateContent.getDocumentReference()).thenReturn(TEMPLATE_DOCUMENT);
        when(this.templateContent.getContent()).thenReturn("");

        AuthorizationManager authorizationManager = this.oldcore.getMockAuthorizationManager();
        when(authorizationManager.hasAccess(Right.SCRIPT, SCRIPT_USER, TEMPLATE_DOCUMENT)).thenReturn(true);
    void getVelocityContext()
        VelocityContext context = this.velocityManager.getVelocityContext();

    @Test
    void checkMacrosInjectionWithoutScriptRights() throws Exception
    {
        VelocityEngine engine = this.velocityManager.getVelocityEngine();
        verify(engine, never()).addGlobalMacros(anyMap());
    }

    @Test
    void checkMacrosInjectionWithScriptRights() throws Exception
    {
        when(this.templateContent.getAuthorReference()).thenReturn(SCRIPT_USER);
        VelocityEngine engine = this.velocityManager.getVelocityEngine();
        verify(engine).addGlobalMacros(anyMap());
    }