CVE-2023-27479: Improper Neutralization of Special Elements in Output Used by a Downstream Component ('Injection') (CWE-74)
value: XWiki Platform is a generic wiki platform offering runtime services for applications built on top of it. In affected versions any user with view rights can execute arbitrary Groovy, Python or Velocity code in XWiki leading to full access to the XWiki installation. The root cause is improper escaping of UIX parameters. A proof of concept exploit is to log in, add an `XWiki.UIExtensionClass` xobject to the user profile page, with an Extension Parameters content containing `label={{/html}} {{async async="true" cached="false" context="doc.reference"}}{{groovy}}println("Hello " + "from groovy!"){{/groovy}}{{/async}}`. Then, navigating to `PanelsCode.ApplicationsPanelConfigurationSheet` (i.e., `<xwiki-host>/xwiki/bin/view/PanelsCode/ApplicationsPanelConfigurationSheet` where `<xwiki-host>` is the URL of your XWiki installation) should not execute the Groovy script. If it does, you will see `Hello from groovy!` displayed on the screen. This vulnerability has been patched in XWiki 13.10.11, 14.4.7 and 14.10-rc-1. Users are advised to upgrade. For users unable to upgrade the issue can be fixed by editing the `PanelsCode.ApplicationsPanelConfigurationSheet` wiki page and making the same modifications  as shown in commit `6de5442f3c`.
CWE description: The product constructs all or part of a command, data structure, or record using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify how it is parsed or interpreted when it is sent to a downstream component.
CVSS v3 base score: 9.9
Published: 2023-03-07T19:15Z
Repository: https://github.com/xwiki/xwiki-platform
Language: XML
Files changed: xwiki-platform-core/xwiki-platform-panels/xwiki-platform-panels-ui/src/main/resources/PanelsCode/ApplicationsPanelConfigurationSheet.xml
Commit date: 2022-10-27 14:50:01 +0200
Commit message: XWIKI-20294: Improve escaping in Applications Panel Configuration sheet

Diff: diff --git a/xwiki-platform-core/xwiki-platform-panels/xwiki-platform-panels-ui/src/main/resources/PanelsCode/ApplicationsPanelConfigurationSheet.xml b/xwiki-platform-core/xwiki-platform-panels/xwiki-platform-panels-ui/src/main/resources/PanelsCode/ApplicationsPanelConfigurationSheet.xml
index 3f495c6bca254..63e2c906b5b61 100644
--- a/xwiki-platform-core/xwiki-platform-panels/xwiki-platform-panels-ui/src/main/resources/PanelsCode/ApplicationsPanelConfigurationSheet.xml
+++ b/xwiki-platform-core/xwiki-platform-panels/xwiki-platform-panels-ui/src/main/resources/PanelsCode/ApplicationsPanelConfigurationSheet.xml
@@ -82,18 +82,19 @@
         &lt;/div&gt;
         &lt;div class="panel-body"&gt;
           &lt;ul class="nav nav-pills applicationsPanel"&gt;
             #foreach($app in $apps)
               #set($params = $app.uix.getParameters())
-              #if("$!params.icon" != '' &amp;&amp; "$!params.label" != '' &amp;&amp; "$!params.target" != '' &amp;&amp; $xwiki.hasAccessLevel('view', $xcontext.user, $params.target))
-                #if ($params.icon.startsWith('icon:'))
-                  #set($icon = $services.icon.renderHTML($params.icon.substring(5)))
+              #set($normalizedIcon = $stringtool.substringBefore($!params.icon, ' '))
+              #if("$!normalizedIcon" != '' &amp;&amp; "$!params.label" != '' &amp;&amp; "$!params.target" != '' &amp;&amp; $xwiki.hasAccessLevel('view', $xcontext.user, $params.target))
+                #if ($normalizedIcon.startsWith('icon:'))
+                  #set($icon = $services.icon.renderHTML($normalizedIcon.substring(5)))
                 #else
-                  #set($icon = $services.rendering.render($services.rendering.parse("image:${params.icon}", 'xwiki/2.1'), 'xhtml/1.0'))
+                  #set($icon = $services.rendering.render($services.rendering.parse("image:${normalizedIcon}", 'xwiki/2.1'), 'xhtml/1.0'))
                 #end
-                &lt;li class="draggableApp" id="$app.uix.id"&gt;
-                  &lt;a&gt;&lt;span class="application-img"&gt;$icon &lt;/span&gt; &lt;span class="application-label"&gt;$params.label&lt;/a&gt;
+                &lt;li class="draggableApp" id="$escapetool.xml($app.uix.id)"&gt;
+                  &lt;a&gt;&lt;span class="application-img"&gt;$icon &lt;/span&gt; &lt;span class="application-label"&gt;$escapetool.xml($params.label)&lt;/a&gt;
                 &lt;/li&gt;
               #end
             #end
           &lt;/ul&gt;
         &lt;/div&gt;

Vulnerable code:               #if("$!params.icon" != '' &amp;&amp; "$!params.label" != '' &amp;&amp; "$!params.target" != '' &amp;&amp; $xwiki.hasAccessLevel('view', $xcontext.user, $params.target))
                #if ($params.icon.startsWith('icon:'))
                  #set($icon = $services.icon.renderHTML($params.icon.substring(5)))
                  #set($icon = $services.rendering.render($services.rendering.parse("image:${params.icon}", 'xwiki/2.1'), 'xhtml/1.0'))
                &lt;li class="draggableApp" id="$app.uix.id"&gt;
                  &lt;a&gt;&lt;span class="application-img"&gt;$icon &lt;/span&gt; &lt;span class="application-label"&gt;$params.label&lt;/a&gt;
Fixed code:               #set($normalizedIcon = $stringtool.substringBefore($!params.icon, ' '))
              #if("$!normalizedIcon" != '' &amp;&amp; "$!params.label" != '' &amp;&amp; "$!params.target" != '' &amp;&amp; $xwiki.hasAccessLevel('view', $xcontext.user, $params.target))
                #if ($normalizedIcon.startsWith('icon:'))
                  #set($icon = $services.icon.renderHTML($normalizedIcon.substring(5)))
                  #set($icon = $services.rendering.render($services.rendering.parse("image:${normalizedIcon}", 'xwiki/2.1'), 'xhtml/1.0'))
                &lt;li class="draggableApp" id="$escapetool.xml($app.uix.id)"&gt;
                  &lt;a&gt;&lt;span class="application-img"&gt;$icon &lt;/span&gt; &lt;span class="application-label"&gt;$escapetool.xml($params.label)&lt;/a&gt;