This is the first crypto challenge I solved which was really interesting to work on.
When entering the given URL from the challenge description, the following link is shown:
http://35.198.133.163:1337/files/952bb2a215b032abe27d24296be099dc3334755c/?f=sample.gif
Browsing to the /files
folder also shows two other files: dont.gif and flag.
Visiting the sample.gif url will show a sample.gif
file. Changing the hash in this URL results in a redirection to a dont.gif
file which might indicate that this might be a MAC preventing an attacker to fiddle with the f
parameter and accessing other files.
This can be confirmed when looking at the provided source. I only included the relevant parts here:
def mac(msg):
return hashlib.sha1(SECRET + msg).hexdigest()
@route('/files/<umac>/')
def download(umac):
delim = msg = ''
for k,v in request.query.allitems():
msg += delim + k + '=' + v
delim = '&'
if mac(msg) == umac:
return static_file(request.query.f, root='./files')
else:
return redirect('/files/' + mac('f=dont.gif') + '/?f=dont.gif')
Because all GET parameters are concatenated and a MAC is created from a secret and this concatenated string, this code is vulnerable to a hash length extension attack. By using the sample.gif filename and the given hash as a starting point, we can generate a new hash for a different url/filename, allowing us to request and download other files. A very good summary of this attack can be found here.
Because the length of the secret is not known, I tried different lengths and was finally able to retrieve the flag. For doing the actual hash length extension attack, I used a small script called hlextend which can be found here.
The final exploit code:
import hlextend
import urllib
import urllib2
append = '&f=flag'
for i in range(1, 20):
print("current: %d" % i)
sha = hlextend.new('sha1')
s = sha.extend(append, 'sample.gif', i, '952bb2a215b032abe27d24296be099dc3334755c', True)
urldata = {'f': s[:-len(append)]}
url = 'http://35.198.133.163:1337/files/%s/?%s%s' % (
sha.hexdigest(),
urllib.urlencode(urldata),
s[-len(append):]
)
print('Trying %s' % url)
resp = urllib2.urlopen(url).read()
if '34C3' in resp:
print('Flag found: %s' % resp)
break
Running this gives us the flag: