This article is part 8 of a 10 series blog detailing the approaches and solutions to hacking through Stripe’s 2012 CTF 2.0. To continue from the parent article, or see more hacks, please click here.
This blog entry details the approach used by Michael Tarantino in attacking the Stripe CTF 2.0 Challenge Level 7.
Level 7 tests your “lower-level” security skills. At this point, you have proven that you know the basics in web security, but how is your crypto? Let’s find out.
Here is the scenario in a nutshell: A new company will be delivering waffles that you can order through an ordering client that you run locally. You provide a waffle type, the number of waffles you would like, and a lat and long for the waffles to be delivered. The information given is that the password will be a confirmation code from ordering their premium-member-only Liege waffle.
After some quick exploration of the app, we se we can view order history by going to /orders/user_id, thus exposing the orders from other users. The first attack vectors that of course come to mind are some sort of database injection, session hijacking, or some flavor of request forgery. Inspecting the code shows us that it is pretty well locked down, so injections are out, and there don’t seem to be any other active users, so session hijacking is likely also a no-go. This leaves us with forging a request, which in this case means ordering a waffle as though we are a premium user when we really are not.
On the order history page [or by playing with the order client] we can see that our orders are being signed using SHA-1 to protect their integrity, and that signature is then passed with the order and validated. I’ll admit even we did not see this as obvious right off the bat, but there is a clear and well-documented vulnerability here.
To understand this vulnerability fully, we are going to have to dive into hash functions a bit. If you are simply interested in the solution, feel free to skip the next paragraph.
SHA-1 [and many other hash functions, for that matter] works in a somewhat straightforward manner. A message is broken up in to blocks of 512 bits, and those blocks are processed against some pre-seeded values in 5 separate registers. These registers are always initialized to the same values, ensuring consistency each time a given string is hashed. A typical security scheme will use a hash as follows: Hash(secret + message). This secret is obfuscated by the hash, which is what buys us our authenticity [only people who know the secret can properly sign messages]. Now we arrive at the point of the exercise; where is the flaw? To see it, let’s back up a step. When we break up the message into blocks of 512 bits, what are we to do when the final block only has, say, 100 bits of data? The SHA-1 implementation fixes this by padding the final block with “\x00″‘s until the block is full. While this seems fairly benign, it is actually going to allow us to sign messages without actually knowing the secret itself. Pause on the technical content, let’s get back to the task at hand.
To carry out this attack, we are going to take a valid request for waffles from User 1 [a premium user who ordered a premium Chicken waffle] and generate an order for a Liege waffle. We can do this by adding an additional “waffle=liege” query param, overriding the one that appears earlier in User 1’s original request for a chicken waffle, and resigning it as we explained above. To resign it properly, we will pad the original message with “\x00″‘s, pushing it into a new block, then adding our new query param. We will then initialize the SHA-1 registers to the 5 blocks from the original signature, effectively causing the function to pick up where it left off. It will then continue its hashing process, and create a valid signature for our new message.
Sounds complex, no? Actually, it is quite simple, again with the right tools. Those tools can be found here. This site has 3 python scripts that will carry out everything described above. Just grab the signature from User 1’s chicken waffle order, paste it into the code along with the new portion of the message [in our case, “&waffle=liege”, and out pops your new signature! Now just tweak your client to make the newly created request to the Level 7 server, and you’ll be chowing down on that premium liege waffle, AND the Level 8 password!
These solutions are presented as a unique approach to a recent CTF hacking contest as an outreach of the Credera Security Team. All ‘hacking’ was performed in an ethical manner in accordance with Credera’s Core Values. For further information on Credera’s offerings in ethical hacking, security, compliance, and OWASP preparedness please contact us at email@example.com