CREATIVE CHAOS   ▋ blog

Flag Authoriser (net)

PUBLISHED ON 30/11/2021 — EDITED ON 11/12/2023 — 247CTF, INFOSEC

Intro

A little write up of a Networking challenge The Web Shell on the CTF site 247CTF.com.

Instructions

Can you forge a new identity to upgrade your access from an anonymous user to an admin?

Howto

Code

The challenge provide us with the following code:

from flask import Flask, redirect, url_for, make_response, render_template, flash
from flask_jwt_extended import JWTManager, create_access_token, jwt_optional, get_jwt_identity
from secret import secret, admin_flag, jwt_secret

app = Flask(__name__)
cookie = "access_token_cookie"

app.config['SECRET_KEY'] = secret
app.config['JWT_SECRET_KEY'] = jwt_secret
app.config['JWT_TOKEN_LOCATION'] = ['cookies']
app.config['DEBUG'] = False

jwt = JWTManager(app)

def redirect_to_flag(msg):
    flash('%s' % msg, 'danger')
    return redirect(url_for('flag', _external=True))

@jwt.expired_token_loader
def my_expired_token_callback():
    return redirect_to_flag('Token expired')

@jwt.invalid_token_loader
def my_invalid_token_callback(callback):
    return redirect_to_flag(callback)

@jwt_optional
def get_flag():
    if get_jwt_identity() == 'admin':
        return admin_flag

@app.route('/flag')
def flag():
    response = make_response(render_template('main.html', flag=get_flag()))
    response.set_cookie(cookie, create_access_token(identity='anonymous'))
    return response

@app.route('/')
def source():
    return "

%s

" % open(__file__).read()

if __name__ == "__main__":
    app.run()

Reconossaince

Reviewing the code shows us that we are looking at Flask webapp using some JWT (JSON Web Tokens). We need to hit the /flag, and to obtain it we need to have identity parameter set to admin.

So search for JWT vulnerabilities.

It is all about JWT!

First we need to obtain a valid JWT cookie from the site:

curl -c - https://xxx/flag

We get a base64 encoded string, that can be pasted into a tool like https://jwt.io

{
  "alg": "HS256",
  "typ": "JWT"
}
{
  "csrf": "c74e65f2-e234-45d6-bddf-fb59e4915c74",
  "jti": "d21963e3-9241-42d8-bd46-9c2efc662f58",
  "exp": 1638045444,
  "fresh": false,
  "iat": 1638044544,
  "type": "access",
  "nbf": 1638044544,
  "identity": "anonymous"
}

Okay, simple stuff, change the identity and resend the token.

But there is still another thing we need to do to make it. The token in this case is signed and when we modify it, we need to sign it again to make it valid.

Breaking the secret key

Take the encoded token and save it as jwt.txt. Break the secret signing key with John The Ripper:

$ john jwt.txt --wordlist=rockyou.txt --format=HMAC-SHA256
$ john --show jwt.txt

Forging the token

Take the token, plug it again in https://www.jwt.io

Add the secret.

We know we need the identity needs to be admin so set that up in the parameters.

Submitting our forgery

Take the base64 encoded token, use Cookie Quick Manager in Firefox to edit it, paste in the forged token instead of the provided one and voilà.

Other useful tools

jwt_tool

See Also