CVE-2022-25759 (published 2022-07-22T20:15Z)

The package convert-svg-core before 0.6.2 are vulnerable to Remote Code Injection via sending an SVG file containing the payload.

CVSS v3 base score: 9.8, CRITICAL (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H)

Weakness information:
cwe_id: CWE-94
cwe_name: Improper Control of Generation of Code ('Code Injection')
description: The product constructs all or part of a code segment using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the syntax or behavior of the intended code segment.
extended_description: 
            When a product allows a user's input to contain code syntax, it might be possible for an attacker to craft the code in such a way that it will alter the intended control flow of the product. Such an alteration could lead to arbitrary code execution.
            Injection problems encompass a wide variety of issues -- all mitigated in very different ways. For this reason, the most effective way to discuss these weaknesses is to note the distinct features which classify them as injection weaknesses. The most important issue to note is that all injection problems share one thing in common -- i.e., they allow for the injection of control plane data into the user-controlled data plane. This means that the execution of the process may be altered by sending code in through legitimate data channels, using no other mechanism. While buffer overflows, and many other flaws, involve the use of some further issue to gain execution, injection problems need only for the data to be parsed. The most classic instantiations of this category of weakness are SQL injection and format string vulnerabilities.
         
         

Fix information:
repo_url: https://github.com/neocotic/convert-svg
commit_details: repo_url: https://github.com/neocotic/convert-svg
author: Alasdair
author_date: 2022-05-29 16:37:58+01:00
msg: Strip onload attribute from SVG input

In order to prevent a remote code injection, the `onload` attribute needs to be removed from the SVG input as Puppeteer/Chromium will execute any code within it which could potentially be malicious.

Fixes #81
repository: repo_url: https://github.com/neocotic/convert-svg
repo_name: neocotic/convert-svg
description: Node.js packages for converting SVG into other formats using headless Chromium
repo_language: JavaScript
owner: neocotic
file_changes: filename: package-lock.json
change_type: ModificationType.MODIFY
diff: @@ -8,7 +8,7 @@
 			"license": "MIT",
 			"dependencies": {
 				"chalk": "^4.1.2",
-				"cheerio": "^1.0.0-rc.10",
+				"cheerio": "^1.0.0-rc.11",
 				"commander": "^9.2.0",
 				"file-url": "^3.0.0",
 				"get-stdin": "^8.0.0",

programming_language: Go
filename: package.json
change_type: ModificationType.MODIFY
diff: @@ -36,7 +36,7 @@
   },
   "dependencies": {
     "chalk": "^4.1.2",
-    "cheerio": "^1.0.0-rc.10",
+    "cheerio": "^1.0.0-rc.11",
     "commander": "^9.2.0",
     "file-url": "^3.0.0",
     "get-stdin": "^8.0.0",

programming_language: JavaScript
filename: Converter.js
change_type: ModificationType.MODIFY
diff: @@ -47,6 +47,7 @@ const _parseOptions = Symbol('parseOptions');
 const _provider = Symbol('provider');
 const _roundDimension = Symbol('roundDimension');
 const _roundDimensions = Symbol('roundDimensions');
+const _sanitize = Symbol('sanitize');
 const _setDimensions = Symbol('setDimensions');
 const _tempFile = Symbol('tempFile');
 const _validate = Symbol('validate');
@@ -114,9 +115,7 @@ class Converter {
 
     options = this[_parseOptions](options);
 
-    const output = await this[_convert](input, options);
-
-    return output;
+    return await this[_convert](input, options);
   }
 
   /**
@@ -191,11 +190,13 @@ class Converter {
     input = Buffer.isBuffer(input) ? input.toString('utf8') : input;
 
     const { provider } = this;
-    const svg = cheerio.default.html(cheerio.load(input, null, false)('svg'));
+    const svg = cheerio.default.html(this[_sanitize](cheerio.load(input, null, false)('svg')));
+
+    if (!svg) {
+      throw new Error('SVG element not found in input. Check the SVG input');
+    }
 
-    let html = '';
-    if (svg) {
-      html += `<!DOCTYPE html>
+    const html = `<!DOCTYPE html>
 <html>
 <head>
 <base href="${options.baseUrl}">
@@ -207,9 +208,6 @@ html { background-color: ${provider.getBackgroundColor(options)}; }
 </head>
 <body>${svg}</body>
 </html>`;
-    } else {
-      throw new Error('SVG element not found in input. Check the SVG input');
-    }
 
     const page = await this[_getPage](html);
 
@@ -226,12 +224,10 @@ html { background-color: ${provider.getBackgroundColor(options)}; }
 
     await page.setViewport(dimensions);
 
-    const output = await page.screenshot(Object.assign({
+    return await page.screenshot(Object.assign({
       type: provider.getType(),
       clip: Object.assign({ x: 0, y: 0 }, dimensions)
     }, provider.getScreenshotOptions(options)));
-
-    return output;
   }
 
   async [_getDimensions](page, options) {
@@ -389,6 +385,10 @@ html { background-color: ${provider.getBackgroundColor(options)}; }
     };
   }
 
+  [_sanitize](svg) {
+    return svg.removeAttr('onload');
+  }
+
   async [_setDimensions](page, dimensions) {
     if (typeof dimensions.width !== 'number' && typeof dimensions.height !== 'number') {
       return;

programming_language: TypeScript
filename: issue-81.svg
change_type: ModificationType.ADD
diff: @@ -0,0 +1 @@
+<svg viewBox="0 0 240 80" height="1000" width="1000" onload="eval(atob(this.id))" id="ZG9jdW1lbnQud3JpdGUoJzxzdmctZHVtbXk+PC9zdmctZHVtbXk+PGlmcmFtZSBzcmM9ImZpbGU6Ly8vZXRjL3Bhc3N3ZCIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwMHB4Ij48L2lmcmFtZT48c3ZnIHZpZXdCb3g9IjAgMCAyNDAgODAiIGhlaWdodD0iMTAwMCIgd2lkdGg9IjEwMDAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHRleHQgeD0iMCIgeT0iMCIgY2xhc3M9IlJycnJyIiBpZD0iZGVtbyI+ZGF0YTwvdGV4dD48L3N2Zz4nKTs="></svg>

programming_language: Lua
filename: tests.json
change_type: ModificationType.MODIFY
diff: @@ -163,6 +163,11 @@
     "file": "cve-2021-23631.svg",
     "message": "should only read SVG element"
   },
+  {
+    "name": "when SVG has onload attribute",
+    "file": "issue-81.svg",
+    "message": "should strip onload attribute"
+  },
   {
     "name": "when setting both baseFile and baseUrl options",
     "file": "external-file.svg",

programming_language: Swift