For over a decade, Internet Explorer has enabled developers to extend the browser with new URL protocol schemes. These protocols can be one of two types:
- Asynchronous Pluggable Protocols - COM objects that implement the IInternetProtocolRoot interface and return content to URLMon, usually for rendering content inside of Internet Explorer or Web Browser controls
- Application Protocols - launch a program outside of Internet Explorer when invoked.
In today’s post, I’ll explain the difference between these two protocol types, and share what I’ve learned about them over the years.
Asynchronous Pluggable Protocols
Pluggable Protocols include the http, https, ftp, and file protocols, and many less-common ones. This type of protocol can be registered at runtime by a browser extension, but most “permanent” protocols are registered inside the HKCR\Protocols\Handler registry key, as you can see in the screenshot below:
When Internet Explorer (or the web browser control) encounters a URL whose protocol scheme is registered in this key, URLMon will create the COM object specified, pass in the URL provided, and begin to read data from the protocol handler object. Because support for Pluggable Protocols is implemented by URLMon, typically only IE-derived browsers (e.g. IE, Maxthon, etc) are able to load Pluggable Protocols. Other browsers offer similar proprietary mechanisms for implementing custom protocols.
Writing an Asynchronous Pluggable Protocol is an extremely challenging exercise – beyond being complex to implement, they are very often the target of hackers. That’s because permanently registered protocols can be freely invoked by any webpage and malicious input (e.g. a malformed or over-long URL) can be passed into the implementation of the Start method. In some cases, cross-site scripting (XSS), script-injection, or cross-site request forgery (CSRF) attacks can be conducted against these protocols. Because the browser accesses Pluggable Protocols asynchronously, and using multiple threads, and navigations can abort downloads at any time, object lifetime issues are prevalent. The IE team often first learns about a new Pluggable Protocol when we see a flood of crashes reported to Watson or learn about active exploits of vulnerable protocols by bad guys.
Developing an Pluggable Protocol is far beyond the scope of this article, but if you want a quick look, check out this simple Pluggable Protocol example on CodeProject—it shows how you could implement support for Data URIs before they were built into IE8. There’s a similar example written in C# which you can find here, with the caveat that you should never try to develop “production quality” IE extensions (protocols, BHOs, ActiveX controls, toolbars, etc) in .NET because of the performance and stability problems that inevitably result.
In contrast to Pluggable Protocols, Application Protocols are much simpler. Rather than returning content to the browser, they simply enable the browser (or another program like a word processor or PDF reader) to launch a program, passing the requested URL to that program. Common examples of Application Protocols include the mailto: news:, and onenote: protocols.
Application Protocols are conceptually simple—a protocol scheme (e.g. onenote:) is associated in the registry with a locally installed application (e.g. onenote.exe). The association is created by adding a new REG_SZ named URL Protocol inside the key HKCR\<protocolscheme>\.
The invoked URL is injected to replace the %1 parameter in the registered \Shell\Open\Command:
Thanks to their simplicity, and to the fact that the Windows ShellExecuteEx function can easily be used to launch such protocols, all major Windows web browsers (IE, Firefox, Chrome, Safari, Opera) support Application Protocols. However, there are some important differences in behavior between browsers.
The first thing to understand is that despite their simplicity, Application Protocols have nevertheless been the source of a large number of vulnerabilities over the years, and thus nearly all browsers (except Safari) will prompt the user before launching the specified program.
Internet Explorer 8+ (and Web Browser Controls that opt-in to FEATURE_SHOW_APP_PROTOCOL_WARN_DIALOG) will show the following prompt:
The user may control whether this prompt appears by unticking the Always ask before opening this type of address box; the decision is stored inside a REG_DWORD named WarnOnOpen inside HKCU\SOFTWARE\Microsoft\Internet Explorer\ProtocolExecute\; the default value is 0x1, which enables the warning, and the value 0x0 disables the warning. The HKLM version of this key is also respected in the event that the administrator or application developer has set the policy on the user’s behalf, but for security reasons suppressing this prompt is generally discouraged.
Other browsers show similar prompts, which you can see here:
In Internet Explorer 7 and later on Windows Vista and later, launching an application to handle an Application Protocol URL will also consult the Protected Mode Elevation policy for the target executable. By default, this policy is that the user will be prompted for permission to launch the program at the Medium Integrity Level:
You can learn more about Protected Mode Elevation policy by reading my previous blog post on that topic.
Many vulnerabilities in Application Protocols stem from the fact that developers often try to quickly “bolt on” support for this feature into their existing programs. This is frequently a source of security bugs because many programs are not written with the expectation that untrusted data (e.g. the web-supplied URL) will be passed in on their command line. Vulnerable programs often have simple logic bugs (e.g. buffer-overflowing because they fail to handle command line arguments larger than some fixed size) but some have more complicated problems. For instance, some programs will immediately undertake a permanent configuration change without first confirming it with the user. For example, one buggy implementation I once saw would immediately delete user data when passed a particular command line argument—the command to do so existed for many years before a later developer came in and quickly added the code to expose the program as an Application Protocol handler without performing an analysis of the threats posed by untrusted arguments.
Another behavior to be aware of is that some callers will decode or encode URLs before passing them along to the target program. For historical reasons, Internet Explorer performs a single percent-decoding pass on the URL before calling ShellExecute; by default IE9 and IE10 still perform this decoding unless the protocol’s registry key contains a REG_DWORD named UseOriginalUrlEncoding with value 0x1. However, the Windows Shell’s Start > Run command performs no such decoding pass.
The following image shows the behavior in four different scenarios when invoking the URL alert:Escaped%22Quoted%22and"UnescapedQuoted" to launch a trivial program.
Detecting Installed Protocols
Web developers often ask if there’s some way to detect whether the client has a given protocol available. Generally, the answer is no, this isn’t possible. In IE, if a page attempts to navigate to a protocol which is not registered, the user will see the following notification page:
Opera and Safari show a similar page, while Firefox shows a modal error alert, and Chrome will simply fail the navigation. Only Firefox fires an OnError event when a navigation is attempted to an unregistered protocol.
Unfortunately, that means that there is no cross-browser, standardized method to detect whether the user’s browser can invoke a particular protocol. In the past, some setup programs would try to inject a token into the user’s User-Agent string or add a Version Vector (see the same article) that a web page can detect. Alternatively, some packages include both a protocol and and ActiveX control—the ActiveX control only exists to enable the web page to infer whether or not the Application Protocol is installed. All three of these methods have significant downsides, and none of the three approaches will work in non-IE browsers.
 Technically speaking, IE includes some special-case code for mailto that makes it behave a little differently than most Application Protocols. But it’s pretty close.