CVE-2023-36470: 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. By either creating a new or editing an existing document with an icon set, an attacker can inject XWiki syntax and Velocity code that is executed with programming rights and thus allows remote code execution. There are different attack vectors, the simplest is the Velocity code in the icon set's HTML or XWiki syntax definition. The [icon picker](https://extensions.xwiki.org/xwiki/bin/view/Extension/Icon%20Theme%20Application#HIconPicker) can be used to trigger the rendering of any icon set. The XWiki syntax variant of the icon set is also used without any escaping in some documents, allowing to inject XWiki syntax including script macros into a document that might have programming right, for this the currently used icon theme needs to be edited. Further, the HTML output of the icon set is output as JSON in the icon picker and this JSON is interpreted as XWiki syntax, allowing again the injection of script macros into a document with programming right and thus allowing remote code execution. This impacts the confidentiality, integrity and availability of the whole XWiki instance. This issue has been patched in XWiki 14.10.6 and 15.1. Icon themes now require script right and the code in the icon theme is executed within the context of the icon theme, preventing any rights escalation. A macro for displaying icons has been introduced to avoid injecting the raw wiki syntax of an icon set into another document. Users are advised to upgrade. There are no known workarounds for this vulnerability.

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: 8.8
Published: 2023-06-29T21:15Z
Repository: https://github.com/xwiki/xwiki-platform
Language: XML
Files changed: xwiki-platform-core/xwiki-platform-icon/xwiki-platform-icon-script/pom.xml, xwiki-platform-core/xwiki-platform-icon/xwiki-platform-icon-script/src/main/java/org/xwiki/icon/IconManagerScriptService.java, xwiki-platform-core/xwiki-platform-icon/xwiki-platform-icon-ui/src/main/resources/IconThemesCode/IconPicker.xml, xwiki-platform-core/xwiki-platform-icon/xwiki-platform-icon-ui/src/main/resources/IconThemesCode/IconPickerMacro.xml
Commit date: 2023-03-02 18:53:18 +0100
Commit message: XWIKI-20524: Improve icon rendering

* Use the icon macro for rendering icons in the icon script service.
* Properly send the JSON response in the icon picker.
* Adapt the IconPickerMacro to not use the icon service rendering for
  resource loading but to use the dedicated method for that as the old
  way no longer works.

Diff: diff --git a/xwiki-platform-core/xwiki-platform-icon/xwiki-platform-icon-script/pom.xml b/xwiki-platform-core/xwiki-platform-icon/xwiki-platform-icon-script/pom.xml
index 5597ddb2fb082..2bc5ba662da43 100644
--- a/xwiki-platform-core/xwiki-platform-icon/xwiki-platform-icon-script/pom.xml
+++ b/xwiki-platform-core/xwiki-platform-icon/xwiki-platform-icon-script/pom.xml
@@ -37,12 +37,23 @@
     <dependency>
       <groupId>org.xwiki.platform</groupId>
       <artifactId>xwiki-platform-icon-api</artifactId>
       <version>${project.version}</version>
     </dependency>
+    <dependency>
+      <groupId>org.xwiki.platform</groupId>
+      <artifactId>xwiki-platform-rendering-xwiki</artifactId>
+      <version>${project.version}</version>
+    </dependency>
     <dependency>
       <groupId>org.xwiki.commons</groupId>
       <artifactId>xwiki-commons-script</artifactId>
       <version>${commons.version}</version>
     </dependency>
+    <dependency>
+      <groupId>org.xwiki.platform</groupId>
+      <artifactId>xwiki-platform-icon-macro</artifactId>
+      <version>${project.version}</version>
+      <scope>runtime</scope>
+    </dependency>
   </dependencies>
 </project>
\ No newline at end of file
diff --git a/xwiki-platform-core/xwiki-platform-icon/xwiki-platform-icon-script/src/main/java/org/xwiki/icon/IconManagerScriptService.java b/xwiki-platform-core/xwiki-platform-icon/xwiki-platform-icon-script/src/main/java/org/xwiki/icon/IconManagerScriptService.java
index e16f86b13e088..42f770d5125cc 100644
--- a/xwiki-platform-core/xwiki-platform-icon/xwiki-platform-icon-script/src/main/java/org/xwiki/icon/IconManagerScriptService.java
+++ b/xwiki-platform-core/xwiki-platform-icon/xwiki-platform-icon-script/src/main/java/org/xwiki/icon/IconManagerScriptService.java
@@ -26,10 +26,12 @@
 import javax.inject.Named;
 import javax.inject.Singleton;
 
 import org.xwiki.component.annotation.Component;
 import org.xwiki.context.Execution;
+import org.xwiki.rendering.internal.util.XWikiSyntaxEscaper;
+import org.xwiki.rendering.syntax.Syntax;
 import org.xwiki.script.service.ScriptService;
 
 /**
  * Script services to render an icon from the current icon set.
  *
@@ -56,24 +58,22 @@ public class IconManagerScriptService implements ScriptService
     private IconRenderer iconRenderer;
 
     @Inject
     private Execution execution;
 
+    @Inject
+    private XWikiSyntaxEscaper escaper;
+
     /**
      * Display an icon with wiki code from the current {@link org.xwiki.icon.IconSet}.
      *
      * @param iconName name of the icon to display
      * @return the wiki code that displays the icon
      */
     public String render(String iconName)
     {
-        try {
-            return iconManager.render(iconName);
-        } catch (IconException e) {
-            setLastError(e);
-            return null;
-        }
+        return String.format("{{displayIcon name=\"%s\"/}}", escapeXWiki21(iconName));
     }
 
     /**
      * Display an icon with wiki code from the specified {@link org.xwiki.icon.IconSet}.
      *
@@ -82,16 +82,12 @@ public String render(String iconName)
      * @return the wiki code that displays the icon
      * @since 6.3RC1
      */
     public String render(String iconName, String iconSetName)
     {
-        try {
-            return iconManager.render(iconName, iconSetName);
-        } catch (IconException e) {
-            setLastError(e);
-            return null;
-        }
+        return String.format("{{displayIcon name=\"%s\" iconSet=\"%s\"/}}", escapeXWiki21(iconName),
+            escapeXWiki21(iconSetName));
     }
 
     /**
      * Display an icon with wiki code from the specified {@link org.xwiki.icon.IconSet}.
      *
@@ -101,16 +97,17 @@ public String render(String iconName, String iconSetName)
      * @return the wiki code that displays the icon
      * @since 6.3RC1
      */
     public String render(String iconName, String iconSetName, boolean fallback)
     {
-        try {
-            return iconManager.render(iconName, iconSetName, fallback);
-        } catch (IconException e) {
-            setLastError(e);
-            return null;
-        }
+        return String.format("{{displayIcon name=\"%s\" iconSet=\"%s\" fallback=\"%b\"/}}", escapeXWiki21(iconName),
+            escapeXWiki21(iconSetName), fallback);
+    }
+
+    private String escapeXWiki21(String value)
+    {
+        return this.escaper.escape(value, Syntax.XWIKI_2_1);
     }
 
     /**
      * Display an icon with HTML code from the current {@link org.xwiki.icon.IconSet}.
      *
diff --git a/xwiki-platform-core/xwiki-platform-icon/xwiki-platform-icon-ui/src/main/resources/IconThemesCode/IconPicker.xml b/xwiki-platform-core/xwiki-platform-icon/xwiki-platform-icon-ui/src/main/resources/IconThemesCode/IconPicker.xml
index a6b5091c152bd..aa8c5588b5d0a 100644
--- a/xwiki-platform-core/xwiki-platform-icon/xwiki-platform-icon-ui/src/main/resources/IconThemesCode/IconPicker.xml
+++ b/xwiki-platform-core/xwiki-platform-icon/xwiki-platform-icon-ui/src/main/resources/IconThemesCode/IconPicker.xml
@@ -46,11 +46,11 @@
 ###########################
 #if($request.action == 'data_iconthemes')
   #set($map = {})
   #set($discard = $map.put('iconThemes', $services.icon.iconSetNames))
   #set($discard = $map.put('currentIconTheme', $services.icon.currentIconSetName))
-  $jsontool.serialize($map)
+  #jsonResponse($map)
 ###########################
 ## DATA: ICONS
 ###########################
 #elseif($request.action == 'data_icons')
   #set($icons = [])
@@ -59,11 +59,11 @@
     #set($icon = {})
     #set($discard = $icon.put('name', $xwikiIcon))
     #set($discard = $icon.put('render', $services.icon.renderHTML($xwikiIcon, $iconTheme)))
     #set($discard = $icons.add($icon))
   #end
-  $jsontool.serialize($icons)
+  #jsonResponse($icons)
 #else
 = Presentation =
 The Icon Picker is a jQuery plugin written by XWiki to help user selecting an icon. See [[IconPickerMacro]] for using this picker easily. If you want to use it manually, read the following.
 
 == Example ==
diff --git a/xwiki-platform-core/xwiki-platform-icon/xwiki-platform-icon-ui/src/main/resources/IconThemesCode/IconPickerMacro.xml b/xwiki-platform-core/xwiki-platform-icon/xwiki-platform-icon-ui/src/main/resources/IconThemesCode/IconPickerMacro.xml
index d712e024ec463..bed45622e00ed 100644
--- a/xwiki-platform-core/xwiki-platform-icon/xwiki-platform-icon-ui/src/main/resources/IconThemesCode/IconPickerMacro.xml
+++ b/xwiki-platform-core/xwiki-platform-icon/xwiki-platform-icon-ui/src/main/resources/IconThemesCode/IconPickerMacro.xml
@@ -273,15 +273,15 @@
       <async_enabled>0</async_enabled>
     </property>
     <property>
       <code>{{velocity output="false"}}
   $xwiki.ssx.use('IconThemesCode.IconPicker')
-  ## The icons themes may need some SSX, so we ask for a rendering of an icon of each icon theme, to be able to display
-  ## all icon themes in the picker
+  ## The icons themes may need some SSX, so we ask to load the resources of all icon sets to be able to display them
+  ## in the picker.
   ## ToDo: since it is a bit hacky, a better system would be to dynamically load the needed SSX on demand
   #foreach($iconSetName in $services.icon.iconSetNames)
-    $services.icon.render('wiki', $iconSetName)
+    $services.icon.use($iconSetName)
   #end
 {{/velocity}}
 
 {{velocity}}
 {{html clean="false"}}

Vulnerable code:         try {
            return iconManager.render(iconName);
        } catch (IconException e) {
            setLastError(e);
            return null;
        }
        try {
            return iconManager.render(iconName, iconSetName);
        } catch (IconException e) {
            setLastError(e);
            return null;
        }
        try {
            return iconManager.render(iconName, iconSetName, fallback);
        } catch (IconException e) {
            setLastError(e);
            return null;
        }
  $jsontool.serialize($map)
  $jsontool.serialize($icons)
  ## The icons themes may need some SSX, so we ask for a rendering of an icon of each icon theme, to be able to display
  ## all icon themes in the picker
    $services.icon.render('wiki', $iconSetName)
Fixed code:     <dependency>
      <groupId>org.xwiki.platform</groupId>
      <artifactId>xwiki-platform-rendering-xwiki</artifactId>
      <version>${project.version}</version>
    </dependency>
    <dependency>
      <groupId>org.xwiki.platform</groupId>
      <artifactId>xwiki-platform-icon-macro</artifactId>
      <version>${project.version}</version>
      <scope>runtime</scope>
    </dependency>
import org.xwiki.rendering.internal.util.XWikiSyntaxEscaper;
import org.xwiki.rendering.syntax.Syntax;
    @Inject
    private XWikiSyntaxEscaper escaper;

        return String.format("{{displayIcon name=\"%s\"/}}", escapeXWiki21(iconName));
        return String.format("{{displayIcon name=\"%s\" iconSet=\"%s\"/}}", escapeXWiki21(iconName),
            escapeXWiki21(iconSetName));
        return String.format("{{displayIcon name=\"%s\" iconSet=\"%s\" fallback=\"%b\"/}}", escapeXWiki21(iconName),
            escapeXWiki21(iconSetName), fallback);
    }

    private String escapeXWiki21(String value)
    {
        return this.escaper.escape(value, Syntax.XWIKI_2_1);
  #jsonResponse($map)
  #jsonResponse($icons)
  ## The icons themes may need some SSX, so we ask to load the resources of all icon sets to be able to display them
  ## in the picker.
    $services.icon.use($iconSetName)