A little write up of a Networking challenge The Web Shell on the CTF site 247CTF.com.
Can you forge a new identity to upgrade your access from an anonymous user to an admin?
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()
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.
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
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.
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à.