Finding Same Origin Method Execution Vulnerabilities

Editor's note: This blog has been updated.

Introduction

This summer I had the privilege of interning on LinkedIn’s House Security team. During this period I was tasked to analyze the details of a relatively new class of vulnerabilities, and create a BurpSuite extension that would help identifying problematic sites. In addition to open-sourcing the extension, we want to shed some more light on Same Origin Method Execution (pun intended!).

What is it?

Same Origin Method Execution (SOME) is a vulnerability where an attacker can control a reflected function name that will be executed as Javascript. At first glance, this doesn't appear to be immensely helpful unless there is a sensitive Javascript function on a page that you can execute on behalf of a user. For instance, if a website has a ‘deleteAccount’ Javascript function, then an attacker would be able to call that function.

An attacker can also navigate and interact with the DOM within these constraints. For instance, if there is a button on the page, it is possible to interact with it via Javascript by navigating the DOM:

document.firstElement.nextSiblingElement.nextSiblingElement.click

And this will end up clicking on the button.

How does it work?

A website must take a parameter to be used as a function name and then execute it as Javascript. This is commonly seen in JSONP implementations where a callback parameter is defined so that the website knows which Javascript function to return data to. A vulnerable instance might look like the following PHP code:

https://example.com/main
<?php
echo ‘<script src="https://example.com/jsonp?callback=’ . $_GET[“userParam”] .‘ “>’;
?>

In the above code snapshot, user input is directly getting injected in a script tag. The JSONP endpoint (https://example.com/jsonp) will return data that looks like this:

userParam({ jsonp : data }) // where `userParam` is whatever the user specified via the earlier GET parameter

Please note that the example code snippet listed above is vulnerable to XSS and other attacks, since user-controlled data is directly being injected in Javascript execution context.

In typical production environments, the ideal approach would be to define a safe callback function that implements passlisting to accept only alphanumeric characters for such user generated input. This approach would typically suffice to prevent against XSS and other JSONP based Rosetta Flash vulnerabilities. However, this approach would not protect against SOME attacks. In fact, it is possible to navigate the DOM by using builtin methods mentioned earlier, such as firstElementChild, lastElementChild, and nextElementSibling. This allows an attacker to navigate to any element on a page with Javascript methods and interact with them.

Furthering the Exploit

If an attacker finds a web application that is affected by this vulnerability, they are not stuck to only exploiting that resource. Instead, by using the Javascript built-in 'window.opener' reference they are able to interact with any page that is from the same origin as the vulnerable web page. By using this technique, the exploitation surface is greatly increased but it requires opening multiple pages.

Using window.opener

Since we are using multiple windows to deliver the exploit, a user must land on an attacker controlled web page. The flow is as follows:

  1. User lands on the attacker’s page (referred to as Window 1)
  2. Window 1 spawns a new window (referred to as Window 2)
  3. Window 2’s ‘window.opener’ refers to the Window 1 origin on the attacker’s domain
  4. Window 1 redirects to the victim’s page that contains buttons or other Javascript methods we want to execute
  5. Window 2’s ‘window.opener’ now refers to the Window 1 origin, which is the victim’s page on the vulnerable domain
  6. Window 2 redirects to the victim’s vulnerable endpoint using ‘window.opener.functionToExecute’ as a callback
  7. Functions are executed in the context of Window 1’s origin

Now, an attacker is able to do the attack within the same origin as the webpage with the vulnerability.

Visually, the flow should look like this:

Same Origin Method Execution Diagram

Multiple steps

Earlier it was mentioned that an attack could be stopped by additional confirmation on the page. Despite this, an attacker can still circumvent these checks by spawning multiple windows. See the following flow for clicking on an additional confirmation:

  1. User lands on the attacker page (referred to as Window 1)
  2. Window 1 spawns a new window (referred to as Window 2)
  3. Window 1 spawns a new window (referred to as Window 3)
  4. Window 2 and Window 3 ‘window.opener’ refers to the Window 1 origin on the attacker’s domain
  5. Window 1 redirects to the victim’s page that contains buttons or other Javascript methods we want to execute
  6. Window 2 and Window 3 ‘window.opener’ now refers to the Window 1 origin, which is the victim’s page on the vulnerable domain
  7. Window 2 redirects to the victim’s vulnerable page using ‘window.opener.buttonToClick.click’ as a callback. At this point the button has been clicked on Window 1, but it now prompts for confirmation
  8. Window 3 redirects to the vulnerable page using ‘window.opener.buttonToConfirm.click’ as a callback. At this point the confirmation button has been clicked on Window 1.
  9. Two buttons are clicked in Window 1.

SOMEtime - A BurpSuite Plugin

SOMEtime is a passive open-source extension for the security scanner BurpSuite which will monitor HTTP requests and responses to determine if an endpoint is vulnerable to Same Origin Method Execution attacks.

For instructions on the installation and use of the plugin, please refer to the Github page here: https://github.com/linkedin/sometime

Recommendation for mitigating SOME attacks

  • If there is no need to support more than one callback endpoint, your application should use a statically defined callback value.
  • If you do need to support more than one callback endpoint, the ideal approach would be to implement a server-side passlisting approach. This would entail allowing only specific callback end-points to be invoked, and can be achieved by matching the given callback parameter value against the server-defined passlist.
  • Traditionally, JSONP has been used as a controversial technique to get around same-origin policy. If the use of JSONP is not required, window.postMessage should be used as a safer alternative to javascript callback execution for such cross-domain interactions.

Thanks to Tushar Dalvi, Luca Carettoni, and Mukul Khullar for the contributions on the plugin code and blog post.