The Click that shouldn’t have worked: RCE via clickjacking in Internet Explorer

Author

Web Application Security Expert

Author’s note: this article describes vulnerabilities in ascending order of severity. If you want to skip straight to the most interesting part, feel free to read it from the bottom up.

Even though Internet Explorer officially reached its end of life in 2020, its core engine remains widely used in the form of the WebBrowser control. This component is used in applications written in Visual Basic, .NET, and C#.

Recently, I have discovered and documented several vulnerabilities in software using WebBrowser. A notable example is my research titled WinRAR’s vulnerable trialware: when free software isn’t free, which details how a MITM attack could lead to remote code execution in one of the world’s most popular file archivers.

While preparing this research, I tried to find information regarding the official status of the WebBrowser control. Surprisingly, no official documentation states that it is no longer supported or about to be deprecated. Furthermore, it appears that while I was working on this article, Microsoft restricted access to IE Mode in Edge even further. This came in response to the discovery of APT attacks using social engineering tactics.

Useful information

Before we dive into the research, let’s break down some key terms that will appear frequently. This will help avoid any confusion when we analyze the vulnerabilities.

  • ActiveX. A deprecated Microsoft framework that allows Internet Explorer to access the APIs of third-party libraries installed on the operating system. A new ActiveX object can be instantiated using a CLSID or a ProgID.
  • CLSID (class identifier). A 128-bit number serving as a universally unique identifier for a specific software component or COM object.
  • ProgID (programmatic identifier). A human-readable text string used in Windows to identify COM objects or file associations.
  • Component Object Model (COM). A Microsoft technology that enables software components to interact independently of the programming language they were written in. Developers interact with an object through its interface without needing to understand its internal implementation.
  • Mark of the Web (MOTW). A tag applied to files downloaded from the internet. When you run such a file, Windows displays a security warning. You can check for this tag using the following PowerShell command:
    Get-Content –Path C:/file.txt –Stream zone.identifier
  • Same-origin policy (SOP). A fundamental security mechanism in browsers that prevents a document or script loaded by one origin to interact with a resource from another origin.
  • NTLM (NT LAN Manager). A Windows authentication mechanism. An NTLM leak occurs when an attacker captures encrypted authentication requests for subsequent GPU-based bruteforcing.
  • Security zones. A set of rules and restrictions in Internet Explorer assigned to a resource based on its origin.

ActiveX: how it works

The first thing we need to discuss is Microsoft’s legacy ActiveX framework. This framework was used to create software components that ran in the Windows environment, including in Internet Explorer. Due to security risks and obsolescence, its use is highly discouraged; however, it still functions within the WebBrowser component and IE itself.
Let’s look at an example of code using CLSID 22D6F312-B0F6-11D0-94AB-0080C74C7E95. This identifier is stored in the registry at HKEY_LOCAL_MACHINE\Software\Microsoft\Active Setup\Installed Components and corresponds to Windows Media Player.
Using this code, we can play a media file named test.mp3 via ActiveX:

<object classid="CLSID:22D6F312-B0F6-11D0-94AB-0080C74C7E95">
    <param name="URL" value="test.mp3"/>
</object>

There is another way to instantiate an ActiveX object: via a ProgID. In the example below, we access Windows Media Player through the string WMPlayer.OCX, creating an instance of the object using JavaScript:

<script type="text/javascript">
    var player = new ActiveXObject("WMPlayer.OCX");
    player.URL = 'test.mp3';
</script>

What happens when this code is executed? 
An instance of Windows Media Player will be created, and the test.mp3 file will be sent to it to be played.

Mark of the Web (MOTW)

Now let’s look at security markers. Imagine I have two HTML files in my ~/Downloads folder:

  • I created the first file in Notepad (local file).
  • The second one was downloaded from the internet.

Let’s check their MOTW tags:

The results are as follows:

  • test_motw_1.html (🖥️ – local file). The file has no MOTW tag; an empty Stream error is returned.
  • test_motw_2.html (🌐 – downloaded from internet). The file is tagged with MOTW, security zone 3 (ZoneId=3).

Security zones

Since we mentioned ZoneId=3, let’s take a closer look at how security zones actually work. Internet Explorer uses a zone-based security model, where each zone has a specific privilege level and component access rights.

ValueSettingDetails
0My Computerfile:///
1Local Intranet Zonehttp(s)://192.168.*.*; http://10.*.*.*
2Trusted sites ZoneConfigurable by the user
3Internet Zonehttp(s)://internet.site/
4Restricted Sites ZoneConfigurable by the user

During my experiments, I discovered that the browser has two special zones that are difficult to place in the list above: localhost and file://smb/.

Characteristics of localhost:

  • Has access to numerous ActiveX components (unlike arbitrary internet sites).
  • Has access to remote SMB servers by default, as if they were in the same zone.

Characteristics of file://smb/:

  • Uses the file:// URL scheme.
  • Leads to NTLM leaks by design.
  • Does not have direct access to local files (unlike file:///C:/).

Let’s check if the privileges of a page hosted on localhost differ from those on an intranet page. To do this, we’ll try opening a file from an SMB server by clicking the following link:

<a href="file://smb/folder/file" target="test"></a>

Based on the test results, we observed the following:

You can open files from an SMB server (file://smb/) using a page hosted at http://localhost/. You cannot download files from an SMB server (file://smb/) using a page hosted at http://10.0.0.100/.
It appears that localhost has elevated privileges compared to standard internet or intranet sites. Based on this, we can build a hierarchy of security zones based on their privileges:

ZonePrivileges
🌐http://internet.site/
(Lowest privileges)
Can only access the safest ActiveX components.
Can open http://localhost and ftp://localhost
🖥️http://localhost/ and ftp://localhost/
(Medium privileges)
Can access file://smb/
Have access to numerous ActiveX components
💻 file:///C:/ and file://localhost/C:/
(Highest privileges)
Have direct access to local files.
Have access to the most insecure ActiveX components, such as WScript.Shell

Backstory

Part 1. Access to local files

A few years ago, I started hunting for insufficient HTML sanitization in desktop applications that could be exploited for Cross-Site Scripting (XSS). My goal was to perform remote code execution or find something interesting or unusual by executing arbitrary scripts within the application.

I installed a bunch of potentially vulnerable apps and kept a running log of my findings in a spreadsheet. Today, we will break down one of those findings. Initially, I dismissed this particular case because, at first glance, it seemed difficult to escalate to RCE. My primary focus was finding instances where JavaScript is executed under the file:// origin. Later, I revisited my notes, tested my hypothesis thoroughly, and realized that the issue I had accidentally stumbled upon wasn’t rooted in the application itself or its configuration, but in Internet Explorer itself.

One of those notes (a random result from manual fuzzing)

When I executed alert(location) in the desktop app, the popup displayed http://localhost. All standard methods for opening new windows were disabled, except for showModalDialog. I tried opening an HTML page from a test SMB server—and it loaded successfully.

<script>
    showModalDialog('file://localtest.me/users/root/test.html')
</script>

My next step was to execute HTML code on the SMB server.  Later, I discovered that the SMB server wasn’t even necessary: the exact same behavior could be reproduced on a local web application (http://localhost) by specifying the <base href="file:///"> tag.

Here is the code that would be hosted on the SMB server (test.html file):

<!-- file://localtest.me/users/root/test.html -->
<iframe></iframe>
<script>
frames[0].name='test';
window.open('//localhost//\\\\localhost\\c:\\users\\root\\test321.html', 'test');
window.open('//localhost/c:/users/root/test321.html', 'test'); // equivalent file:///c:/users/root/test321.html
</script>

In this example, we open two links in a row. If you open the first link, pause, and then open the second, a popup will appear before the second link executes, stating that the URL is invalid or malformed. However, if you open the links in rapid succession, the popup doesn’t have time to appear.

During testing, I manually tested various links. One of them triggered that exact popup, but after I closed it, links that were not supposed to open suddenly did. I managed to reproduce this behavior and eventually found a way to bypass the popup entirely, minimizing user interaction.

Ultimately, this behavior allows an attacker to open local HTML files. Simply put, we found a portal from the internet to local files, using http://localhost as a proxy.

Important limitation: to exploit this feature, an attacker must first find a way to drop an HTML file onto the victim’s computer (for example, via previous attack stages or social engineering), and the victim must be running a web application vulnerable to XSS at http://localhost. 

Today, having a web application constantly running at http://localhost is not uncommon. This could be:

  • An email client
  • A development environment
  • LAMP/XAMPP builds
  • Electron-based applications
  • Software like uTorrent

Part 2. Downloading arbitrary files without MOTW

The most logical next step would be to try forcing the browser to download a malicious file, right? However, Internet Explorer prevents file from being saved to disk without user interaction; it always requires explicit confirmation.

It took me eight months of research to find the easiest bypass.  It turns out that Internet Explorer can open a page in Microsoft Edge without any user interaction by using a link formatted as microsoft-edge:http://site. And because modern Edge is Chromium-based, it can automatically save files to ~/Downloads/ without asking any questions.

Unfortunately, I cannot disclose the details of the vulnerable application used in this example, but the screenshot above shows an ActiveX security warning. This warning appears when a local file attempts to instantiate a highly insecure COM object—WScript.Shell. I am showing the final stage of the attack where the malicious WScript.Shell is attempting to instantiate and is requesting user permission. Not all COM objects request such permissions—only those that are well known and lead to command execution, data leaks, or other unsafe events. 

When testing this behavior, I used http://localhost for the page that initiated the file download. When the file was saved and executed, I achieved 1-click RCE by clicking “Yes” in the warning.

However, when I repeated the test under more realistic conditions using http://attacker/ instead of http://localhost, RCE failed. After a few experiments, I figured out why: when downloaded via http://localhost, the file is saved without the MOTW tag. Without this tag, Windows does not block dangerous operations. I