CVE-2022-23597: Use After Free (CWE-416)
value: Element Desktop is a Matrix client for desktop platforms with Element Web at its core. Element Desktop before 1.9.7 is vulnerable to a remote program execution bug with user interaction. The exploit is non-trivial and requires clicking on a malicious link, followed by another button click. To the best of our knowledge, the vulnerability has never been exploited in the wild. If you are using Element Desktop < 1.9.7, we recommend upgrading at your earliest convenience. If successfully exploited, the vulnerability allows an attacker to specify a file path of a binary on the victim's computer which then gets executed. Notably, the attacker does *not* have the ability to specify program arguments. However, in certain unspecified configurations, the attacker may be able to specify an URI instead of a file path which then gets handled using standard platform mechanisms. These may allow exploiting further vulnerabilities in those mechanisms, potentially leading to arbitrary code execution.
CWE description: The product reuses or references memory after it has been freed. At some point afterward, the memory may be allocated again and saved in another pointer, while the original pointer references a location somewhere within the new allocation. Any operations using the original pointer are no longer valid because the memory "belongs" to the code that operates on the new pointer.
CVSS v3 base score: 8.8
Published: 2022-02-01T12:15Z
Repository: https://github.com/vector-im/element-desktop
Language: JSON
Files changed: package.json, src/protocol.ts, yarn.lock
Commit date: 2021-12-13 15:35:27 +0000
Commit message: Merge pull request from GHSA-mjrg-9f8r-h3m7

* Patch part 1: remove electronVersion

We no longer need to specify electronVersion at all since electron
is now in devDependencies. Removing it means electron can be updated
the same way as any other dependency.

* Only allow main app page to be opened via URL

We previously allowed any URL to be opened in the main electron
window. Allow only the main app page, as commented.

* use exact equals

* Make url logic clearer
Diff: diff --git a/package.json b/package.json
index c958137..2ce2dda 100644
--- a/package.json
+++ b/package.json
@@ -81,11 +81,10 @@
     "matrix-seshat": "^2.3.0",
     "keytar": "^5.6.0"
   },
   "build": {
     "appId": "im.riot.app",
-    "electronVersion": "13.5.1",
     "files": [
       "package.json",
       {
         "from": ".hak/hakModules",
         "to": "node_modules"
diff --git a/src/protocol.ts b/src/protocol.ts
index 5ffbc40..06b78c0 100644
--- a/src/protocol.ts
+++ b/src/protocol.ts
@@ -17,21 +17,41 @@ limitations under the License.
 import { app } from "electron";
 import { URL } from "url";
 import path from "path";
 import fs from "fs";
 
-const PROTOCOL = "element://";
+const PROTOCOL = "element:";
 const SEARCH_PARAM = "element-desktop-ssoid";
 const STORE_FILE_NAME = "sso-sessions.json";
 
 // we getPath userData before electron-main changes it, so this is the default value
 const storePath = path.join(app.getPath("userData"), STORE_FILE_NAME);
 
 function processUrl(url: string): void {
     if (!global.mainWindow) return;
-    console.log("Handling link: ", url);
-    global.mainWindow.loadURL(url.replace(PROTOCOL, "vector://"));
+
+    const parsed = new URL(url);
+    // sanity check: we only register for the one protocol, so we shouldn't
+    // be getting anything else unless the user is forcing a URL to open
+    // with the Element app.
+    if (parsed.protocol !== PROTOCOL) {
+        console.log("Ignoring unexpected protocol: ", parsed.protocol);
+        return;
+    }
+
+    const urlToLoad = new URL("vector://vector/webapp/");
+    // ignore anything other than the search (used for SSO login redirect)
+    // and the hash (for general element deep links)
+    // There's no reason to allow anything else, particularly other paths,
+    // since this would allow things like the internal jitsi wrapper to
+    // be loaded, which would get the app stuck on that page and generally
+    // be a bit strange and confusing.
+    urlToLoad.search = parsed.search;
+    urlToLoad.hash = parsed.hash;
+
+    console.log("Opening URL: ", urlToLoad.href);
+    global.mainWindow.loadURL(urlToLoad.href);
 }
 
 function readStore(): object {
     try {
         const s = fs.readFileSync(storePath, { encoding: "utf8" });
@@ -60,14 +80,14 @@ export function recordSSOSession(sessionID: string): void {
     writeStore(store);
 }
 
 export function getProfileFromDeeplink(args): string | undefined {
     // check if we are passed a profile in the SSO callback url
-    const deeplinkUrl = args.find(arg => arg.startsWith('element://'));
+    const deeplinkUrl = args.find(arg => arg.startsWith(PROTOCOL + '//'));
     if (deeplinkUrl && deeplinkUrl.includes(SEARCH_PARAM)) {
         const parsedUrl = new URL(deeplinkUrl);
-        if (parsedUrl.protocol === 'element:') {
+        if (parsedUrl.protocol === PROTOCOL) {
             const ssoID = parsedUrl.searchParams.get(SEARCH_PARAM);
             const store = readStore();
             console.log("Forwarding to profile: ", store[ssoID]);
             return store[ssoID];
         }
@@ -94,10 +114,10 @@ export function protocolInit(): void {
         });
     } else {
         // Protocol handler for win32/Linux
         app.on('second-instance', (ev, commandLine) => {
             const url = commandLine[commandLine.length - 1];
-            if (!url.startsWith(PROTOCOL)) return;
+            if (!url.startsWith(PROTOCOL + '//')) return;
             processUrl(url);
         });
     }
 }
diff --git a/yarn.lock b/yarn.lock
index ae661ba..7304096 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -140,23 +140,23 @@
   dependencies:
     ajv "^6.12.0"
     ajv-keywords "^3.4.1"
 
 "@electron/get@^1.0.1":
-  version "1.13.0"
-  resolved "https://registry.yarnpkg.com/@electron/get/-/get-1.13.0.tgz#95c6bcaff4f9a505ea46792424f451efea89228c"
-  integrity sha512-+SjZhRuRo+STTO1Fdhzqnv9D2ZhjxXP6egsJ9kiO8dtP68cDx7dFCwWi64dlMQV7sWcfW1OYCW4wviEBzmRsfQ==
+  version "1.13.1"
+  resolved "https://registry.yarnpkg.com/@electron/get/-/get-1.13.1.tgz#42a0aa62fd1189638bd966e23effaebb16108368"
+  integrity sha512-U5vkXDZ9DwXtkPqlB45tfYnnYBN8PePp1z/XDCupnSpdrxT8/ThCv9WCwPLf9oqiSGZTkH6dx2jDUPuoXpjkcA==
   dependencies:
     debug "^4.1.1"
     env-paths "^2.2.0"
     fs-extra "^8.1.0"
     got "^9.6.0"
     progress "^2.0.3"
     semver "^6.2.0"
     sumchecker "^3.0.1"
   optionalDependencies:
-    global-agent "^2.0.2"
+    global-agent "^3.0.0"
     global-tunnel-ng "^2.7.1"
 
 "@electron/universal@1.0.5":
   version "1.0.5"
   resolved "https://registry.yarnpkg.com/@electron/universal/-/universal-1.0.5.tgz#b812340e4ef21da2b3ee77b2b4d35c9b86defe37"
@@ -756,13 +756,13 @@
   version "14.17.16"
   resolved "https://registry.yarnpkg.com/@types/node/-/node-14.17.16.tgz#2b9252bd4fdf0393696190cd9550901dd967c777"
   integrity sha512-WiFf2izl01P1CpeY8WqFAeKWwByMueBEkND38EcN8N68qb0aDG3oIS1P5MhAX5kUdr469qRyqsY/MjanLjsFbQ==
 
 "@types/node@^14.6.2":
-  version "14.17.21"
-  resolved "https://registry.yarnpkg.com/@types/node/-/node-14.17.21.tgz#6359d8cf73481e312a43886fa50afc70ce5592c6"
-  integrity sha512-zv8ukKci1mrILYiQOwGSV4FpkZhyxQtuFWGya2GujWg+zVAeRQ4qbaMmWp9vb9889CFA8JECH7lkwCL6Ygg8kA==
+  version "14.18.0"
+  resolved "https://registry.yarnpkg.com/@types/node/-/node-14.18.0.tgz#98df2397f6936bfbff4f089e40e06fa5dd88d32a"
+  integrity sha512-0GeIl2kmVMXEnx8tg1SlG6Gg8vkqirrW752KqolYo1PHevhhZN3bhJ67qHj+bQaINhX0Ra3TlWwRvMCd9iEfNQ==
 
 "@types/plist@^3.0.1":
   version "3.0.2"
   resolved "https://registry.yarnpkg.com/@types/plist/-/plist-3.0.2.tgz#61b3727bba0f5c462fe333542534a0c3e19ccb01"
   integrity sha512-ULqvZNGMv0zRFvqn8/4LSPtnmN4MfhlPNtJCTpKuIIxGVGZ2rYWzFXrvEBoh9CVyqSE7D6YFRJ1hydLHI6kbWw==
@@ -1611,15 +1611,10 @@ configstore@^5.0.1:
 console-control-strings@^1.0.0, console-control-strings@~1.1.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e"
   integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=
 
-core-js@^3.6.5:
-  version "3.18.2"
-  resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.18.2.tgz#63a551e8a29f305cd4123754846e65896619ba5b"
-  integrity sha512-zNhPOUoSgoizoSQFdX1MeZO16ORRb9FFQLts8gSYbZU5FcgXhp24iMWMxnOQo5uIaIG7/6FA/IqJPwev1o9ZXQ==
-
 core-util-is@1.0.2:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
   integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=
 
@@ -1706,11 +1701,11 @@ debounce-fn@^4.0.0:
   resolved "https://registry.yarnpkg.com/debounce-fn/-/debounce-fn-4.0.0.tgz#ed76d206d8a50e60de0dd66d494d82835ffe61c7"
   integrity sha512-8pYCQiL9Xdcg0UPSD3d+0KMlOjp+KGU5EPwYddgzQ7DATsg4fuUDjQtsYLmWjnk2obnNHgV3vE2Y4jejSOJVBQ==
   dependencies:
     mimic-fn "^3.0.0"
 
-debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2:
+debug@4, debug@^4.0.1, debug@^4.3.1, debug@^4.3.2:
   version "4.3.2"
   resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b"
   integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==
   dependencies:
     ms "2.1.2"
@@ -1727,10 +1722,17 @@ debug@^3.2.6:
   resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a"
   integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==
   dependencies:
     ms "^2.1.1"
 
+debug@^4.1.0, debug@^4.1.1:
+  version "4.3.3"
+  resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664"
+  integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==
+  dependencies:
+    ms "2.1.2"
+
 decompress-response@^3.3.0:
   version "3.3.0"
   resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3"
   integrity sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=
   dependencies:
@@ -1959,13 +1961,13 @@ electron-window-state@^5.0.3:
   dependencies:
     jsonfile "^4.0.0"
     mkdirp "^0.5.1"
 
 electron@13.5:
-  version "13.5.1"
-  resolved "https://registry.yarnpkg.com/electron/-/electron-13.5.1.tgz#76c02c39be228532f886a170b472cbd3d93f0d0f"
-  integrity sha512-ZyxhIhmdaeE3xiIGObf0zqEyCyuIDqZQBv9NKX8w5FNzGm87j4qR0H1+GQg6vz+cA1Nnv1x175Zvimzc0/UwEQ==
+  version "13.5.2"
+  resolved "https://registry.yarnpkg.com/electron/-/electron-13.5.2.tgz#5c5826e58a5e12bb5ca8047b789d07b45260ecbc"
+  integrity sha512-CPakwDpy5m8dL0383F5uJboQcVtn9bT/+6/wdDKo8LuTUO9aER1TF41v7feZgZW2c+UwoGPWa814ElSQ3qta2A==
   dependencies:
     "@electron/get" "^1.0.1"
     "@types/node" "^14.6.2"
     extract-zip "^1.0.3"
 
@@ -2497,17 +2499,16 @@ glob@^7.1.3, glob@^7.1.4, glob@^7.1.6:
     inherits "2"
     minimatch "^3.0.4"
     once "^1.3.0"
     path-is-absolute "^1.0.0"
 
-global-agent@^2.0.2:
-  version "2.2.0"
-  resolved "https://registry.yarnpkg.com/global-agent/-/global-agent-2.2.0.tgz#566331b0646e6bf79429a16877685c4a1fbf76dc"
-  integrity sha512-+20KpaW6DDLqhG7JDiJpD1JvNvb8ts+TNl7BPOYcURqCrXqnN1Vf+XVOrkKJAFPqfX+oEhsdzOj1hLWkBTdNJg==
+global-agent@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/global-agent/-/global-agent-3.0.0.tgz#ae7cd31bd3583b93c5a16437a1afe27cc33a1ab6"
+  integrity sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==
   dependencies:
     boolean "^3.0.1"
-    core-js "^3.6.5"
     es6-error "^4.1.1"
     matcher "^3.0.0"
     roarr "^2.15.3"
     semver "^7.3.2"
     serialize-error "^7.0.1"

Vulnerable code:     "electronVersion": "13.5.1",
const PROTOCOL = "element://";
    console.log("Handling link: ", url);
    global.mainWindow.loadURL(url.replace(PROTOCOL, "vector://"));
    const deeplinkUrl = args.find(arg => arg.startsWith('element://'));
        if (parsedUrl.protocol === 'element:') {
            if (!url.startsWith(PROTOCOL)) return;
  version "1.13.0"
  resolved "https://registry.yarnpkg.com/@electron/get/-/get-1.13.0.tgz#95c6bcaff4f9a505ea46792424f451efea89228c"
  integrity sha512-+SjZhRuRo+STTO1Fdhzqnv9D2ZhjxXP6egsJ9kiO8dtP68cDx7dFCwWi64dlMQV7sWcfW1OYCW4wviEBzmRsfQ==
    global-agent "^2.0.2"
  version "14.17.21"
  resolved "https://registry.yarnpkg.com/@types/node/-/node-14.17.21.tgz#6359d8cf73481e312a43886fa50afc70ce5592c6"
  integrity sha512-zv8ukKci1mrILYiQOwGSV4FpkZhyxQtuFWGya2GujWg+zVAeRQ4qbaMmWp9vb9889CFA8JECH7lkwCL6Ygg8kA==
core-js@^3.6.5:
  version "3.18.2"
  resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.18.2.tgz#63a551e8a29f305cd4123754846e65896619ba5b"
  integrity sha512-zNhPOUoSgoizoSQFdX1MeZO16ORRb9FFQLts8gSYbZU5FcgXhp24iMWMxnOQo5uIaIG7/6FA/IqJPwev1o9ZXQ==

debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2:
  version "13.5.1"
  resolved "https://registry.yarnpkg.com/electron/-/electron-13.5.1.tgz#76c02c39be228532f886a170b472cbd3d93f0d0f"
  integrity sha512-ZyxhIhmdaeE3xiIGObf0zqEyCyuIDqZQBv9NKX8w5FNzGm87j4qR0H1+GQg6vz+cA1Nnv1x175Zvimzc0/UwEQ==
global-agent@^2.0.2:
  version "2.2.0"
  resolved "https://registry.yarnpkg.com/global-agent/-/global-agent-2.2.0.tgz#566331b0646e6bf79429a16877685c4a1fbf76dc"
  integrity sha512-+20KpaW6DDLqhG7JDiJpD1JvNvb8ts+TNl7BPOYcURqCrXqnN1Vf+XVOrkKJAFPqfX+oEhsdzOj1hLWkBTdNJg==
    core-js "^3.6.5"
Fixed code: const PROTOCOL = "element:";

    const parsed = new URL(url);
    // sanity check: we only register for the one protocol, so we shouldn't
    // be getting anything else unless the user is forcing a URL to open
    // with the Element app.
    if (parsed.protocol !== PROTOCOL) {
        console.log("Ignoring unexpected protocol: ", parsed.protocol);
        return;
    }

    const urlToLoad = new URL("vector://vector/webapp/");
    // ignore anything other than the search (used for SSO login redirect)
    // and the hash (for general element deep links)
    // There's no reason to allow anything else, particularly other paths,
    // since this would allow things like the internal jitsi wrapper to
    // be loaded, which would get the app stuck on that page and generally
    // be a bit strange and confusing.
    urlToLoad.search = parsed.search;
    urlToLoad.hash = parsed.hash;

    console.log("Opening URL: ", urlToLoad.href);
    global.mainWindow.loadURL(urlToLoad.href);
    const deeplinkUrl = args.find(arg => arg.startsWith(PROTOCOL + '//'));
        if (parsedUrl.protocol === PROTOCOL) {
            if (!url.startsWith(PROTOCOL + '//')) return;
  version "1.13.1"
  resolved "https://registry.yarnpkg.com/@electron/get/-/get-1.13.1.tgz#42a0aa62fd1189638bd966e23effaebb16108368"
  integrity sha512-U5vkXDZ9DwXtkPqlB45tfYnnYBN8PePp1z/XDCupnSpdrxT8/ThCv9WCwPLf9oqiSGZTkH6dx2jDUPuoXpjkcA==
    global-agent "^3.0.0"
  version "14.18.0"
  resolved "https://registry.yarnpkg.com/@types/node/-/node-14.18.0.tgz#98df2397f6936bfbff4f089e40e06fa5dd88d32a"
  integrity sha512-0GeIl2kmVMXEnx8tg1SlG6Gg8vkqirrW752KqolYo1PHevhhZN3bhJ67qHj+bQaINhX0Ra3TlWwRvMCd9iEfNQ==
debug@4, debug@^4.0.1, debug@^4.3.1, debug@^4.3.2:
debug@^4.1.0, debug@^4.1.1:
  version "4.3.3"
  resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664"
  integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==
  dependencies:
    ms "2.1.2"

  version "13.5.2"
  resolved "https://registry.yarnpkg.com/electron/-/electron-13.5.2.tgz#5c5826e58a5e12bb5ca8047b789d07b45260ecbc"
  integrity sha512-CPakwDpy5m8dL0383F5uJboQcVtn9bT/+6/wdDKo8LuTUO9aER1TF41v7feZgZW2c+UwoGPWa814ElSQ3qta2A==
global-agent@^3.0.0:
  version "3.0.0"
  resolved "https://registry.yarnpkg.com/global-agent/-/global-agent-3.0.0.tgz#ae7cd31bd3583b93c5a16437a1afe27cc33a1ab6"
  integrity sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==