CSAW 2013 WidgetCorp Writeup, with bonus coolness

The “WidgetCorp” challenge for the 2013 CSAW CTF was a really neat web exploitation problem. It was a challenge where I was super excited to work through the whole thing (not always the case for sure!)

Selection_002

It’s a light blue because I solved it but you get the gist. No information/comments are provided about what to expect so you’re going in blind here.

Here is a picture of the homepage for this site:

Very pretty, almost 10 minutes of CSS work in there

Very pretty, almost 10 minutes of CSS work in there

Interesting, looks like a very well put together site eh?

Selection_005

On our main page once we’ve logged in we have options to:

  • Check our widgets
  • Make a new widget
  • Check out the new feature (a link to the logout page, also an attempt at humor)

Great, so let’s make a widget!

Selection_006

 

Alright, looks good. Once we submit this page we get:

Selection_007

So we made a new widget with ID 7574 – let’s check it out now.

Selection_008

Great, so we have options to view/edit our newly created widget. Let’s view it:

Selection_009

As expected we have a page were we can view our widget details. The URL for this page looks like:

Selection_010

Obviously the id=7574 is showing that we are viewing a widget with the ID of 7574 (as we saw the website tell us earlier).

Now any good hacker would try to go to this URL next:

Selection_011

Which sadly yields us a:

Selection_012

Well, what were you expecting?

So they are checking if we own the widget before displaying the information. But how do they do that?

Selection_017

 

Cookies should always be checked during any sort of web auditing, they are often forgotten about or not coded securely.

Great so we have the following cookies:

  • PHPSESSID, which is PHP’s way of doing cookies.
  • widget_tracker, an interesting field which I immediately recognized as being a base64 encoded value
  • widget_validate, some seemingly long string of characters (128 characters in length – nudge nudge)

Let’s checkout that widget_tracker value first shall we?

YToxOntpOjA7aTo3NTc0O30%3D

**So we decode the url encoded string:

YToxOntpOjA7aTo3NTc0O30==

Oh my, that double equal sign makes me smile every time! (Almost always means base64 encoding!)

Base64 decoding that value gives us:

a:1:{i:0;i:7574;}

What is that?! Did someone screw up JSON? Nope, actually it’s the PHP serialize function. I got it right away because I do far to much programming in PHP. Basically it’s like JSON encoding where you take an array and turn it into a string.

So let’s script this:

Selection_019

This will allow us to better understand what is going on.

Selection_020

Interesting, so we see it’s an array of the widgets we’ve created! So we can simply reverse this function to create a valid cookie value, bypassing the authentication.

Not done yet! What about that other value widget_validate?

Now this one I knew right away from experience that it was a SHA-1 hash (based off it’s length/character set, if you ever need to figure out a hash type see https://code.google.com/p/hash-identifier/)

But what is being hashed? My username? Our PHP session ID?

So I figured this out by simply monitoring the cookies being used. Hacking is really just like being a scientist…

Looks like concentrated science!

Looks like concentrated science!

So all I did was watch when the widget_validate cookie value changed, in this way I could see patterns and hopefully determine how they do their authorization.

Selection_022

Tedious but yields results!

So I had multiple theories of how they did it but I noticed basically that anytime I added a widget the cookie value would change (after I viewed the new widget).

In this way I knew that the widget_validate cookie was changing based on the widgets that I had created.

After a bit of testing I found that the value was simple a SHA-1 hash of the serialized widget array (a:1:{i:0;i:7574;}).

Great, now lets translate that into some code:

Selection_023

So this code will generate a valid cookie to view any widget that we want. Basically you call it with the wanted widget ID and a valid PHP session ID and it will auto-build the cookie for you.

So here is a simple function that gets any page I give it, this function also has the “generate_legit_cookies” function in it so it will auto make the appropriate cookies, etc.

Selection_024

 

I ended up make a script that views everyone’s widgets and outputs their values and that was a BIG mistake. So much script kiddy input values (people running Acunetix, automated tools).

Here’s a funny screenshot of that output:

Selection_029

So big mistake, luckily I screwed up my code at some point when I put in a null value for the ID and got an SQL error from the website.

Selection_026

So I got that as an output and it immediately clicked for me, we’re doing SQL injection via cookies. This is something I’ve always wanted to do (security nerds have different dreams I suppose).

Now, I did this manually step-by-step but I’m going to show a better method in this blog post. The better idea I had after I won is what I’ll continue with here because it’ll convey a good idea for future problems like this.

The idea I had was to simply make my script this:

Selection_030

 

So basically I made a PHP script that acts as a psuedo proxy for any SQL injection tool I want. I just tell my tool that the site I’m injection is http://127.0.0.1/widget_wut.php?id=1. That PHP script just returns the output from the http://128.238.66.224/widget_list.php I guess you would call this being a “hybrid script kiddy” because the PHP script simply does all the cookie crazyness for the SQL tool you’re using.

So here is the PHP script in action:

Selection_032

Selection_031

So hopefully this makes it more clear what I’m doing here but I’ll continue regardless (my brain is fried from constant CTF playing, maybe I’ll come back later and clean this up).

So after this I pointed sqlmap towards this local page:

Selection_033

My output:

Selection_034

So it’s working alright, let’s get the databases to ensure it’s all good:

Selection_036

Great everything works well, the next steps are pretty straightforward.

Selection_040

Selection_043

So hopefully this conveys everything that I’ve done correctly 😉 I’m pretty burned out so I’ll come back later and edit things that I screwed up.

-mandatory

Matthew Bryant (mandatory)

Matthew Bryant (mandatory)
Security researcher who needs to sleep more. Opinions expressed are solely my own and do not express the views or opinions of my employer.