Hi, after few months I have been busy with works ,study new things and doing hacking challenges. I feel that , this knowledge must be share :D

what actually you want to share on this topics?.. well, some of you must changes your perspective how you look base64 encode after reading this. why?.. because I have the same experience what you want to be learn today!

If you want to know about JWT token and how to exploit?.. nah, not this article. But you may refer the link I share below, It is proper guide for you.

1 Vickie Li (https://medium.com/swlh/hacking-json-web-tokens-jwts-9122efe91e4a)

2. https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from flask import Flask, request, jsonify
from flask_jwt_extended import JWTManager, jwt_required, create_access_token, decode_token
import datetime
from apscheduler.schedulers.background import BackgroundScheduler
import threading
import jwt
from config import *
 
# Setup flask
app = Flask(__name__)
 
app.config['JWT_SECRET_KEY'] = SECRET
jwtmanager = JWTManager(app)
blacklist = set()
lock = threading.Lock()
# Free memory from expired tokens, as they are no longer useful
def delete_expired_tokens():
    with lock:
        to_remove = set()
        global blacklist
        for access_token in blacklist:
            try:
                jwt.decode(access_token, app.config['JWT_SECRET_KEY'],algorithm='HS256')
            except:
                to_remove.add(access_token)
  
        blacklist = blacklist.difference(to_remove)
     
@app.route("/web-serveur/ch63/")
def index():
    eturn "POST : /web-serveur/ch63/login <br>\nGET : /web-serveur/ch63/admin"
     
# Standard login endpoint
@app.route('/web-serveur/ch63/login', methods=['POST'])
def login():
    try:
        username = request.json.get('username', None)
        password = request.json.get('password', None)
    except:
        return jsonify({"msg":"""Bad request. Submit your login / pass as {"username":"admin","password":"admin"}"""}), 400
if username != 'admin' or password != 'admin':
        return jsonify({"msg": "Bad username or password"}), 401
access_token = create_access_token(identity=username,expires_delta=datetime.timedelta(minutes=3))
    ret = {
       'access_token': access_token,
    }
       
    with lock:
        blacklist.add(access_token)
     
    return jsonify(ret), 200
     
# Standard admin endpoint
@app.route('/web-serveur/ch63/admin', methods=['GET'])
@jwt_required
def protected():
    access_token = request.headers.get("Authorization").split()[1]
    with lock:
        if access_token in blacklist:
            return jsonify({"msg":"Token is revoked"})
        else:
            return jsonify({'Congratzzzz!!!_flag:': FLAG})
if __name__ == '__main__':
    scheduler = BackgroundScheduler()
    job = scheduler.add_job(delete_expired_tokens, 'interval', seconds=10)
    scheduler.start()
    app.run(debug=False, host='0.0.0.0', port=5000)

I have shown you the source code, what it does is … you need to obtain API token which is in JWT format and login by it.But the hurdle here is, when you generate a token,it automatically go to blacklist set. Then during login action is performed, it check our JWT with blacklist set.Now hands-on time.

None
Image 1 : two method is support GET and POST on the response.
None
Image 2: I try to request GET method from admin API. Now we follow the response and meet the requirement.
None
Image 3: Authorization header is implemented on request, now the response want it in Bearer with JWT token.

Now our investigation on admin API is done.

None
Image 4: Now check on "login", It need us to post credential in JSON format.
None
Image 5: We successfully obtain the access_token. But now, what if we change to difference username or password?
None
Image 6: try username with admin1, response is 401.
None
Image 7: It same goes to password with admin1.
None
Image 8: On line 45, there is "or" which mean if either one username or password is different from what has been set, response will be fail. On line 48–54. what ever username or password is put,token will automatically go to blacklist set. Now we know that this one is not our target anymore.
None
Image 9: Here the part is we enable to manipulate is Authorization header only. Now let us check which part we can inject.
None
Image 10: Now we know where to inject the code, but there is checking for JWT formats "@jwt_required"(line 60) and expired token.you can refer Image 11 and 12.
None
Image 11: main function schedule job to check delete_expired_tokens.
None
Image 12: Checking function.
None
Image 13: After few minutes, I try the token. Now the token is expired.Meaning , it successfully check the token.

Now we are on the edge of testing,So let us look on JWT token, if there some part we can manipulate.Just for the case of testing :D , just want to cover some JWT manipulation technique.

None
Image 14: Check algo for JWT token, and change to "none".
None
Image 15: encoded again with base64.
None
Image 16: Change the JWT header with the new one. as response "The specified alg value is not allowed"

Now the second trick we can use is weak JWT secret key. you can try bruteforce with wordlist.If lucky, we can obtain the secret. On this case, I'm not success to obtain secret by using rockyou wordlist.

https://github.com/aress31/jwtcat

None
Image 17 : jwtcat tool.

What if we put padding on the JWT token?. It can be pass?. let us take one examples.

None
Image 18: I put some padding "=" for proper payload output.
None
Image 19: Now it has been closed with "}" bracket.
None
Image 20: Now we got this message error, which mean our method is fail. :(

The reason why? , If we changes some part of header and payload, the signature will be changes also!

None
Image 21

Now we have done all the possible test case, none of these is working :( .What we can do now?.. it is RFC(Request for Comments)!!! by googling base64 RFC, I mean this one :D "rfc3548" . Part that we are interest is in section 8, security consideration.

None
Image 22: rfc3548 manual
None
Image 23

which mean, what if we put non-alphabet on our base64?.. it can be pass?..

None
image 24: we generate some Chinese character.
None
image 25: We put it on the end of our signature, "we can't put on header or payload because the reason is it affected our signature".

Now we success to bypass it!! Congratulations. But there is another method to bypass it?.. yes :D

First, you need to generate admin token until our signature have an underscore "_" , then replace with "/" as image 26 and 27.

None
Image 26
None
Image 27

Last method that I kept is because I want to show you different method. Actually you can solve by adding "=" to our signature.

None
Image 28

As for conclusion, you learn the methodology of testing, JWT "none" injection technique and JWT weak secret key cracking.Lastly, the behavior of base64 and it other security consideration. Thanks :D