Stripe CTF Level 7 - Solution SHA1 Length-Extension Vulnerability
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This level was a slight twist, you'll actually be doing an attack on their crypto. Looking at the code you'll see that they're using SHA1 hashes that are composed of the raw request that you made plus your secret. We also need to be making a request as a premium user. If you attempted to order a waffle, you'll receive a confirmation number--in this case if you order the premium waffle, the confirmation number will be your password to Level8. | |
Here is the block of code that verifies the signature... this is how we know how it is built and that it is sha1 | |
def verify_signature(user_id, sig, raw_params): | |
# get secret token for user_id | |
try: | |
row = g.db.select_one('users', {'id': user_id}) | |
except db.NotFound: | |
raise BadSignature('no such user_id') | |
secret = str(row['secret']) | |
h = hashlib.sha1() | |
h.update(secret + raw_params) | |
print 'computed signature', h.hexdigest(), 'for body', repr(raw_params) | |
if h.hexdigest() != sig: | |
raise BadSignature('signature does not match') | |
return True | |
Researching on SHA1 we can see that it has a length-extension attack vulnerability, a type of attack on certain hashes which allow inclusion of extra information. There's excellent documentation that describes this attack in the Flickr API Signature Forgery Vulnerability write-up. There's also a nice script and write-up about it at vnsecurity by RD, about how he solved a similar CodeGate 2010 challenge. For my solution I used the script that was supplied on vnsecurity to solve this problem. Since we know what the raw request will be, and we know the length of the secret (14), we can append stuff to the raw request and generate a valid hash. So looking at the /logs/ directory, we can also view other users requests… in this case we're interested in premium users, so id 1 or 2. | |
This is a request that was made by user_id 1: | |
count=10&lat=37.351&user_id=1&long=-119.827&waffle=eggo|sig:a75edb45bc6c0057e059b23bc48b84f7081a798f | |
As you can see, we have the raw request and the final hash... let's append to this and generate a new valid hash, but ordering a different waffle. | |
Bubba$ python sha-padding.py '14' 'count=10&lat=37.351&user_id=1&long=-119.827&waffle=eggo' 'a75edb45bc6c0057e059b23bc48b84f7081a798f' '&waffle=liege' | |
new msg: 'count=10&lat=37.351&user_id=1&long=-119.827&waffle=eggo\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02(&waffle=liege' | |
base64: Y291bnQ9MTAmbGF0PTM3LjM1MSZ1c2VyX2lkPTEmbG9uZz0tMTE5LjgyNyZ3YWZmbGU9ZWdnb4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIoJndhZmZsZT1saWVnZQ== | |
new sig: 4c230b26a20f192c4a258f529662d3dd0ad8b62d | |
And here we are... the script has supplied the correct amount of padding needed and gave us the raw required and a valid hash... let’s go ahead and make the request using a simple python script.... | |
Bubba$ cat post.py | |
import urllib | |
import urllib2 | |
url = 'https://level07-2.stripe-ctf.com/user-dsccixwxvo/orders' | |
data = 'count=10&lat=37.351&user_id=1&long=-119.827&waffle=eggo\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02(&waffle=liege|sig:4c230b26a20f192c4a258f529662d3dd0ad8b62d' | |
req = urllib2.Request(url, data) | |
response = urllib2.urlopen(req) | |
print response.read() | |
Bubba$ python post.py | |
{"confirm_code": "BdxavaMIKC", "message": "Great news: 10 liege waffles will soon be flying your way!", "success": true} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment