XWiki allows remote code execution through default value of wiki macro wiki-type parameters
Description
XWiki is a generic wiki platform. Any user with edit right on a page (could be the user's profile) can execute code (Groovy, Python, Velocity) with programming right by defining a wiki macro. This allows full access to the whole XWiki installation. The main problem is that if a wiki macro parameter allows wiki syntax, its default value is executed with the rights of the author of the document where it is used. This can be exploited by overriding a macro like the children macro that is used in a page that has programming right like the page XWiki.ChildrenMacro and thus allows arbitrary script macros. This vulnerability has been patched in XWiki 16.4.7, 16.10.3 and 17.0.0 by executing wiki parameters with the rights of the wiki macro's author when the parameter's value is the default value.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
org.xwiki.platform:xwiki-platform-rendering-wikimacro-storeMaven | >= 11.10.11 | — |
org.xwiki.platform:xwiki-platform-rendering-wikimacro-storeMaven | >= 12.6.3 | — |
org.xwiki.platform:xwiki-platform-rendering-wikimacro-storeMaven | >= 12.8-rc-1, < 16.4.7 | 16.4.7 |
org.xwiki.platform:xwiki-platform-rendering-wikimacro-storeMaven | >= 16.5.0-rc-1, < 16.10.3 | 16.10.3 |
org.xwiki.platform:xwiki-platform-rendering-wikimacro-storeMaven | >= 17.0.0-rc-1, < 17.0.0 | 17.0.0 |
Affected products
1- Range: >= 11.10.11, < 12.0
Patches
1c99d501ed41cXWIKI-22760: Execute wiki macro parameter's default values in the macro's context
7 files changed · +239 −5
xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-wikimacro/xwiki-platform-rendering-wikimacro-store/pom.xml+5 −0 modified@@ -67,6 +67,11 @@ <artifactId>xwiki-platform-rendering-async-default</artifactId> <version>${project.version}</version> </dependency> + <dependency> + <groupId>org.xwiki.platform</groupId> + <artifactId>xwiki-platform-security-requiredrights-default</artifactId> + <version>${project.version}</version> + </dependency> <!-- Test dependencies --> <dependency>
xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-wikimacro/xwiki-platform-rendering-wikimacro-store/src/main/java/org/xwiki/rendering/wikimacro/internal/DefaultWikiMacroRenderer.java+12 −4 modified@@ -27,6 +27,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -731,10 +732,11 @@ private Block resolveMacroContent(MacroMarkerBlock macroBlock) return macroBlock; } - private XDOM parseWiki(String macroContent, boolean inline) throws RenderingException + private XDOM parseWiki(String macroContent, Syntax syntax, boolean transform, boolean inline) + throws RenderingException { try { - return this.contentParser.parse(macroContent, this.syncContext, false, inline); + return this.contentParser.parse(macroContent, syntax, this.syncContext, transform, null, inline); } catch (MacroExecutionException e) { throw new RenderingException("Failed to parse the passed content", e); } @@ -750,7 +752,7 @@ private XDOM parseContent(String macroContent, boolean inline) throws RenderingE throw new RenderingException("Error while parsing the macro content in plain text.", e); } } else { - return parseWiki(macroContent, inline); + return parseWiki(macroContent, null, false, inline); } } @@ -795,7 +797,13 @@ private XDOM parseParameterValue(String macroParameterContent, String macroParam throw new MacroExecutionException("Error while parsing the macro parameter content in plain.", e); } } else { - return parseWiki(macroParameterContent, inline); + boolean executeWithMacroAuthor = + Objects.equals(macroParameterContent, parameterDescriptor.getDefaultValue()); + if (executeWithMacroAuthor) { + return parseWiki(macroParameterContent, this.wikimacro.getSourceSyntax(), true, inline); + } else { + return parseWiki(macroParameterContent, null, false, inline); + } } }
xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-wikimacro/xwiki-platform-rendering-wikimacro-store/src/main/java/org/xwiki/rendering/wikimacro/internal/WikiMacroParameterObjectRequiredRightAnalyzer.java+84 −0 added@@ -0,0 +1,84 @@ +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.xwiki.rendering.wikimacro.internal; + +import java.util.ArrayList; +import java.util.List; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.apache.commons.lang3.StringUtils; +import org.xwiki.component.annotation.Component; +import org.xwiki.component.util.ReflectionUtils; +import org.xwiki.platform.security.requiredrights.RequiredRightAnalysisResult; +import org.xwiki.platform.security.requiredrights.RequiredRightAnalyzer; +import org.xwiki.platform.security.requiredrights.RequiredRightsException; +import org.xwiki.platform.security.requiredrights.internal.analyzer.ObjectPropertyRequiredRightAnalyzer; +import org.xwiki.rendering.block.Block; + +import com.xpn.xwiki.objects.BaseObject; +import com.xpn.xwiki.objects.PropertyInterface; + +import static org.xwiki.rendering.wikimacro.internal.WikiMacroConstants.PARAMETER_DEFAULT_VALUE_PROPERTY; +import static org.xwiki.rendering.wikimacro.internal.WikiMacroConstants.PARAMETER_TYPE_PROPERTY; +import static org.xwiki.rendering.wikimacro.internal.WikiMacroConstants.PARAMETER_TYPE_WIKI; +import static org.xwiki.rendering.wikimacro.internal.WikiMacroConstants.WIKI_MACRO_PARAMETER_CLASS; + +/** + * Required rights analyzer for {@code XWiki.WikiMacroParameterClass}. + * + * @version $Id$ + * @since 17.0.0 + * @since 16.10.3 + * @since 16.4.7 + */ +@Component +@Singleton +@Named(WIKI_MACRO_PARAMETER_CLASS) +public class WikiMacroParameterObjectRequiredRightAnalyzer implements RequiredRightAnalyzer<BaseObject> +{ + @Inject + private ObjectPropertyRequiredRightAnalyzer propertyRequiredRightAnalyzer; + + @Override + public List<RequiredRightAnalysisResult> analyze(BaseObject object) throws RequiredRightsException + { + List<RequiredRightAnalysisResult> results = + new ArrayList<>(this.propertyRequiredRightAnalyzer.analyzeAllProperties(object)); + String type = object.getStringValue(PARAMETER_TYPE_PROPERTY); + try { + // Only check types that contain "<" to avoid parsing types that cannot be the list block type. + if (PARAMETER_TYPE_WIKI.equals(type) || (StringUtils.contains(type, "<") && Block.LIST_BLOCK_TYPE.equals( + ReflectionUtils.unserializeType(type, Thread.currentThread().getContextClassLoader())))) + { + String content = object.getStringValue(PARAMETER_DEFAULT_VALUE_PROPERTY); + PropertyInterface defaultField = object.getField(PARAMETER_DEFAULT_VALUE_PROPERTY); + results.addAll(this.propertyRequiredRightAnalyzer.analyzeWikiContent(object, content, + defaultField.getReference())); + } + } catch (ClassNotFoundException e) { + // Ignore an unknown parameter type as it can't be the wiki parameter type. + } + + return results; + } +}
xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-wikimacro/xwiki-platform-rendering-wikimacro-store/src/main/resources/META-INF/components.txt+1 −0 modified@@ -7,3 +7,4 @@ org.xwiki.rendering.wikimacro.internal.WikiMacroClassDocumentInitializer org.xwiki.rendering.wikimacro.internal.WikiMacroContentMacro org.xwiki.rendering.wikimacro.internal.WikiMacroParameterClassDocumentInitializer org.xwiki.rendering.wikimacro.internal.WikiMacroParameterMacro +org.xwiki.rendering.wikimacro.internal.WikiMacroParameterObjectRequiredRightAnalyzer
xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-wikimacro/xwiki-platform-rendering-wikimacro-store/src/test/java/org/xwiki/rendering/wikimacro/internal/DefaultWikiMacroTest.java+21 −0 modified@@ -707,4 +707,25 @@ void wikimacroparameterWhenInRawBlock() throws Exception assertEquals(expect, printer.toString()); } + + @Test + void wikiMacroParameterWithDefaultValueContext() throws Exception + { + String defaultValue = "{{velocity}}$xcontext.sdoc{{/velocity}}"; + List<WikiMacroParameterDescriptor> parameterDescriptors = List.of(new WikiMacroParameterDescriptor("param", + "Test parameter", false, defaultValue, Block.LIST_BLOCK_TYPE)); + + registerWikiMacro("defaultmacro", "{{wikimacroparameter name=\"param\"/}}", Syntax.XWIKI_2_1, + parameterDescriptors); + + Converter converter = this.componentManager.getInstance(Converter.class); + + DefaultWikiPrinter printer = new DefaultWikiPrinter(); + converter.convert( + new StringReader("{{defaultmacro /}} {{defaultmacro param=\"Outside: %s\" /}}".formatted(defaultValue)), + Syntax.XWIKI_2_1, Syntax.PLAIN_1_0, printer); + + assertEquals("space.macroPage Outside: sspace.sdoc", printer.toString()); + + } }
xwiki-platform-core/xwiki-platform-rendering/xwiki-platform-rendering-wikimacro/xwiki-platform-rendering-wikimacro-store/src/test/java/org/xwiki/rendering/wikimacro/internal/WikiMacroParameterObjectRequiredRightAnalyzerTest.java+106 −0 added@@ -0,0 +1,106 @@ +/* + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ +package org.xwiki.rendering.wikimacro.internal; + +import java.util.List; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.NullSource; +import org.junit.jupiter.params.provider.ValueSource; +import org.mockito.Mock; +import org.xwiki.platform.security.requiredrights.RequiredRightAnalysisResult; +import org.xwiki.platform.security.requiredrights.internal.analyzer.ObjectPropertyRequiredRightAnalyzer; +import org.xwiki.test.junit5.mockito.ComponentTest; +import org.xwiki.test.junit5.mockito.InjectMockComponents; +import org.xwiki.test.junit5.mockito.MockComponent; + +import com.xpn.xwiki.objects.BaseObject; +import com.xpn.xwiki.objects.PropertyInterface; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.xwiki.rendering.wikimacro.internal.WikiMacroConstants.PARAMETER_DEFAULT_VALUE_PROPERTY; +import static org.xwiki.rendering.wikimacro.internal.WikiMacroConstants.PARAMETER_TYPE_PROPERTY; +import static org.xwiki.rendering.wikimacro.internal.WikiMacroConstants.PARAMETER_TYPE_WIKI; + +/** + * Unit tests for {@link WikiMacroParameterObjectRequiredRightAnalyzer}. + * + * @version $Id$ + */ +@ComponentTest +class WikiMacroParameterObjectRequiredRightAnalyzerTest +{ + @InjectMockComponents + private WikiMacroParameterObjectRequiredRightAnalyzer analyzer; + + @MockComponent + private ObjectPropertyRequiredRightAnalyzer propertyRequiredRightAnalyzer; + + @Mock + private BaseObject baseObject; + + @Mock + private PropertyInterface propertyInterface; + + @ParameterizedTest + @ValueSource(strings = { PARAMETER_TYPE_WIKI, "java.util.List<org.xwiki.rendering.block.Block>" }) + void analyzeWithWikiParameterType(String type) throws Exception + { + when(this.baseObject.getStringValue(PARAMETER_TYPE_PROPERTY)).thenReturn(type); + String defaultValue = "wiki content"; + when(this.baseObject.getStringValue(PARAMETER_DEFAULT_VALUE_PROPERTY)).thenReturn(defaultValue); + when(this.baseObject.getField(PARAMETER_DEFAULT_VALUE_PROPERTY)).thenReturn(this.propertyInterface); + RequiredRightAnalysisResult allPropertiesResult = mock(); + when(this.propertyRequiredRightAnalyzer.analyzeAllProperties(this.baseObject)) + .thenReturn(List.of(allPropertiesResult)); + RequiredRightAnalysisResult wikiContentResult = mock(); + when(this.propertyRequiredRightAnalyzer.analyzeWikiContent(this.baseObject, defaultValue, + this.propertyInterface.getReference())).thenReturn(List.of(wikiContentResult)); + + List<RequiredRightAnalysisResult> results = this.analyzer.analyze(this.baseObject); + + assertEquals(List.of(allPropertiesResult, wikiContentResult), results); + verify(this.propertyRequiredRightAnalyzer).analyzeAllProperties(this.baseObject); + verify(this.propertyRequiredRightAnalyzer).analyzeWikiContent(this.baseObject, defaultValue, + this.propertyInterface.getReference()); + } + + @ParameterizedTest + @ValueSource(strings = { "java.util.List", "unknown" }) + @NullSource + void analyzeWithNonWikiParameterType(String parameterType) throws Exception + { + when(this.baseObject.getStringValue(PARAMETER_TYPE_PROPERTY)).thenReturn(parameterType); + RequiredRightAnalysisResult result = mock(); + when(this.propertyRequiredRightAnalyzer.analyzeAllProperties(this.baseObject)).thenReturn(List.of(result)); + + List<RequiredRightAnalysisResult> results = this.analyzer.analyze(this.baseObject); + + assertEquals(List.of(result), results); + verify(this.propertyRequiredRightAnalyzer).analyzeAllProperties(this.baseObject); + verify(this.propertyRequiredRightAnalyzer, never()).analyzeWikiContent(any(), any(), any()); + verify(this.baseObject, never()).getStringValue(PARAMETER_DEFAULT_VALUE_PROPERTY); + } +}
xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-requiredrights/xwiki-platform-security-requiredrights-default/src/main/java/org/xwiki/platform/security/requiredrights/internal/analyzer/ObjectPropertyRequiredRightAnalyzer.java+10 −1 modified@@ -223,7 +223,16 @@ public List<RequiredRightAnalysisResult> analyzeVelocityScriptValue(String value return result; } - private List<RequiredRightAnalysisResult> analyzeWikiContent(BaseObject object, String value, + /** + * Analyze the given wiki content in a property of the object. + * + * @param object the object to which the property belongs, used for getting the syntax and document reference + * @param value the value with the wiki content + * @param reference the reference of the property + * @return the analysis results + * @throws RequiredRightsException if an error occurs during the analysis + */ + public List<RequiredRightAnalysisResult> analyzeWikiContent(BaseObject object, String value, EntityReference reference) throws RequiredRightsException {
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-9875-cw22-f7cxghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2025-49581ghsaADVISORY
- github.com/xwiki/xwiki-platform/commit/c99d501ed41cbee6a3c02ff927714531570789deghsax_refsource_MISCWEB
- github.com/xwiki/xwiki-platform/security/advisories/GHSA-9875-cw22-f7cxghsax_refsource_CONFIRMWEB
- jira.xwiki.org/browse/XWIKI-22760ghsax_refsource_MISCWEB
News mentions
0No linked articles in our index yet.