Skip to content

Commit 13d61c5

Browse files
authored
v1.3.5 - improved reading of nested JSON in claims
[+] Enabled reading of multiple-level nesting of JSON objects in claims (thanks @frani @fredsibcald @ASoggySandal) Fixed function names and text referencing 'key length' where it should have been 'hash length' (thanks @floyd-fuh)
1 parent 09fb9e5 commit 13d61c5

File tree

2 files changed

+85
-50
lines changed

2 files changed

+85
-50
lines changed

README.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,41 @@ Head over to the [JWT Attack Playbook](https://github.com/ticarpi/jwt_tool/wiki)
7777

7878
## Version History/Changelog
7979

80+
### v1.3.5
81+
* October 2020
82+
* Python 3.x
83+
* [+] Enabled reading of multiple-level nesting of JSON objects in claims
84+
* Fixed function names and text referencing 'key length' where it should have been 'hash length'
85+
86+
### v1.3.4
87+
* May 2020
88+
* Python 3.x
89+
* [+] Updated Tamper mode to allow users to input all JSON data types when editing or creating new claims
90+
* To specify a new JSON object just create a new empty object with curly braces: {}
91+
* To create a JSON array add it in directly: ['item1','item2']
92+
* [+] General streamlining and bug squashing
93+
* Fixed missing urlsafe_b64 decoding in validateToken()
94+
95+
### v1.3.3
96+
* April 2020
97+
* Python 3.x
98+
* [+] Changed Tamper mode to allow users to specify data type when editing or creating new claims
99+
* To specify number, true, false, null just type the relevant value
100+
* To force a string surround the input with double quotes
101+
* e.g. to include a number as a text string enclose in quotes, or leave without if you want it as a number data type
102+
103+
### v1.3.2
104+
* November 2019
105+
* Python 3.x
106+
* [+] Added ability to provide Private Key for signing in Tamper mode, or via cmdline (`jwt_tool.py [jwt] -S -u URL -pr PRIVKEY.pem`)
107+
* [+] JWKS exported as a file as well as displayed to screen
108+
* [*] Bonus tip - you can verify the JWKS with JWKS Check option ('jwt_tool.py [jwt] -J -jw JWKSFILE.json')
109+
110+
### v1.3.1
111+
* November 2019
112+
* Python 3.x
113+
* [+] Fixed tampering when signing with [3] and [4]
114+
80115
### v1.3
81116
* November 2019
82117
* Python 3.x

jwt_tool.py

Lines changed: 50 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#!/usr/bin/env python3
22
#
3-
# JWT_Tool version 1.3.4 (19_05_2020)
3+
# JWT_Tool version 1.3.5 (16_10_2020)
44
# Written by ticarpi
55
# Please use responsibly...
66
# Software URL: https://github.com/ticarpi/jwt_tool
@@ -88,10 +88,10 @@ def castInput(newInput):
8888
return newInput
8989
else:
9090
try:
91-
newInput = json.loads(newInput)
91+
newInput = json.dumps(newInput)
9292
except ValueError:
9393
try:
94-
newInput = json.loads(newInput.replace("'", "\""))
94+
newInput = json.dumps(newInput.replace("'", "\""))
9595
except ValueError:
9696
pass
9797
return newInput
@@ -186,14 +186,14 @@ def newECKeyPair():
186186
privKey = new_key.export_key(format="PEM")
187187
return pubKey, privKey
188188

189-
def signToken(headDict, paylDict, key, keyLength):
189+
def signToken(headDict, paylDict, key, hashLength):
190190
newHead = headDict
191-
newHead["alg"] = "HS"+str(keyLength)
192-
if keyLength == 384:
191+
newHead["alg"] = "HS"+str(hashLength)
192+
if hashLength == 384:
193193
newContents = base64.urlsafe_b64encode(json.dumps(newHead,separators=(",",":")).encode()).decode('UTF-8').strip("=")+"."+base64.urlsafe_b64encode(json.dumps(paylDict,separators=(",",":")).encode()).decode('UTF-8').strip("=")
194194
newSig = base64.urlsafe_b64encode(hmac.new(key.encode(),newContents.encode(),hashlib.sha384).digest()).decode('UTF-8').strip("=")
195195
badSig = base64.b64encode(hmac.new(key.encode(),newContents.encode(),hashlib.sha384).digest()).decode('UTF-8').strip("=")
196-
elif keyLength == 512:
196+
elif hashLength == 512:
197197
newContents = base64.urlsafe_b64encode(json.dumps(newHead,separators=(",",":")).encode()).decode('UTF-8').strip("=")+"."+base64.urlsafe_b64encode(json.dumps(paylDict,separators=(",",":")).encode()).decode('UTF-8').strip("=")
198198
newSig = base64.urlsafe_b64encode(hmac.new(key.encode(),newContents.encode(),hashlib.sha512).digest()).decode('UTF-8').strip("=")
199199
badSig = base64.b64encode(hmac.new(key.encode(),newContents.encode(),hashlib.sha512).digest()).decode('UTF-8').strip("=")
@@ -276,20 +276,20 @@ def jwksEmbed(headDict, paylDict):
276276
badSig = base64.b64encode(signature).decode('UTF-8').strip("=")
277277
return newSig, badSig, newContents.decode('UTF-8')
278278

279-
def signTokenRSA(headDict, paylDict, privKey, keyLength):
279+
def signTokenRSA(headDict, paylDict, privKey, hashLength):
280280
newHead = headDict
281-
newHead["alg"] = "RS"+str(keyLength)
281+
newHead["alg"] = "RS"+str(hashLength)
282282
key = RSA.importKey(open(privKey).read())
283283
newContents = base64.urlsafe_b64encode(json.dumps(newHead,separators=(",",":")).encode()).decode('UTF-8').strip("=")+"."+base64.urlsafe_b64encode(json.dumps(paylDict,separators=(",",":")).encode()).decode('UTF-8').strip("=")
284284
newContents = newContents.encode('UTF-8')
285-
if keyLength == 256:
285+
if hashLength == 256:
286286
h = SHA256.new(newContents)
287-
elif keyLength == 384:
287+
elif hashLength == 384:
288288
h = SHA384.new(newContents)
289-
elif keyLength == 512:
289+
elif hashLength == 512:
290290
h = SHA512.new(newContents)
291291
else:
292-
print("Invalid RSA key length")
292+
print("Invalid RSA hash length")
293293
exit(1)
294294
signer = PKCS1_v1_5.new(key)
295295
try:
@@ -301,20 +301,20 @@ def signTokenRSA(headDict, paylDict, privKey, keyLength):
301301
badSig = base64.b64encode(signature).decode('UTF-8').strip("=")
302302
return newSig, badSig, newContents.decode('UTF-8')
303303

304-
def signTokenEC(headDict, paylDict, privKey, keyLength):
304+
def signTokenEC(headDict, paylDict, privKey, hashLength):
305305
newHead = headDict
306-
newHead["alg"] = "ES"+str(keyLength)
306+
newHead["alg"] = "ES"+str(hashLength)
307307
key = ECC.import_key(open(privKey).read())
308308
newContents = base64.urlsafe_b64encode(json.dumps(newHead,separators=(",",":")).encode()).decode('UTF-8').strip("=")+"."+base64.urlsafe_b64encode(json.dumps(paylDict,separators=(",",":")).encode()).decode('UTF-8').strip("=")
309309
newContents = newContents.encode('UTF-8')
310-
if keyLength == 256:
310+
if hashLength == 256:
311311
h = SHA256.new(newContents)
312-
elif keyLength == 384:
312+
elif hashLength == 384:
313313
h = SHA384.new(newContents)
314-
elif keyLength == 512:
314+
elif hashLength == 512:
315315
h = SHA512.new(newContents)
316316
else:
317-
print("Invalid RSA key length")
317+
print("Invalid RSA hash length")
318318
exit(1)
319319
signer = DSS.new(key, 'fips-186-3')
320320
try:
@@ -326,17 +326,17 @@ def signTokenEC(headDict, paylDict, privKey, keyLength):
326326
badSig = base64.b64encode(signature).decode('UTF-8').strip("=")
327327
return newSig, badSig, newContents.decode('UTF-8')
328328

329-
def signTokenPSS(headDict, paylDict, privKey, keyLength):
329+
def signTokenPSS(headDict, paylDict, privKey, hashLength):
330330
newHead = headDict
331-
newHead["alg"] = "PS"+str(keyLength)
331+
newHead["alg"] = "PS"+str(hashLength)
332332
key = RSA.importKey(open(privKey).read())
333333
newContents = base64.urlsafe_b64encode(json.dumps(newHead,separators=(",",":")).encode()).decode('UTF-8').strip("=")+"."+base64.urlsafe_b64encode(json.dumps(paylDict,separators=(",",":")).encode()).decode('UTF-8').strip("=")
334334
newContents = newContents.encode('UTF-8')
335-
if keyLength == 256:
335+
if hashLength == 256:
336336
h = SHA256.new(newContents)
337-
elif keyLength == 384:
337+
elif hashLength == 384:
338338
h = SHA384.new(newContents)
339-
elif keyLength == 512:
339+
elif hashLength == 512:
340340
h = SHA512.new(newContents)
341341
else:
342342
print("Invalid RSA key length")
@@ -875,7 +875,7 @@ def tamperToken(paylDict, headDict, sig):
875875
if selection == 1:
876876
print("\nPlease enter the known key:")
877877
key = input("> ")
878-
print("\nPlease enter the keylength:")
878+
print("\nPlease enter the hash length:")
879879
print("[1] HMAC-SHA256")
880880
print("[2] HMAC-SHA384")
881881
print("[3] HMAC-SHA512")
@@ -885,15 +885,15 @@ def tamperToken(paylDict, headDict, sig):
885885
print("Invalid selection")
886886
exit(1)
887887
if selLength == 1:
888-
keyLength = 256
888+
hashLength = 256
889889
elif selLength == 2:
890-
keyLength = 384
890+
hashLength = 384
891891
elif selLength == 3:
892-
keyLength = 512
892+
hashLength = 512
893893
else:
894894
print("Invalid selection")
895895
exit(1)
896-
newSig, badSig, newContents = signToken(headDict, paylDict, key, keyLength)
896+
newSig, badSig, newContents = signToken(headDict, paylDict, key, hashLength)
897897
print("\nYour new forged token:")
898898
print("[+] URL safe: "+newContents+"."+newSig)
899899
print("[+] Standard: "+newContents+"."+badSig+"\n")
@@ -933,7 +933,7 @@ def tamperToken(paylDict, headDict, sig):
933933
else:
934934
print("Invalid selection")
935935
exit(1)
936-
print("\nPlease enter the keylength:")
936+
print("\nPlease enter the hash length:")
937937
print("[1] RSA-256")
938938
print("[2] RSA-384")
939939
print("[3] RSA-512")
@@ -943,15 +943,15 @@ def tamperToken(paylDict, headDict, sig):
943943
print("Invalid selection")
944944
exit(1)
945945
if selLength == 1:
946-
keyLength = 256
946+
hashLength = 256
947947
elif selLength == 2:
948-
keyLength = 384
948+
hashLength = 384
949949
elif selLength == 3:
950-
keyLength = 512
950+
hashLength = 512
951951
else:
952952
print("Invalid selection")
953953
exit(1)
954-
newSig, badSig, newContents = signTokenRSA(headDict, paylDict, privKeyName, keyLength)
954+
newSig, badSig, newContents = signTokenRSA(headDict, paylDict, privKeyName, hashLength)
955955
print("\nYour new forged token:")
956956
print("[+] URL safe: "+newContents+"."+newSig)
957957
print("[+] Standard: "+newContents+"."+badSig+"\n")
@@ -981,7 +981,7 @@ def tamperToken(paylDict, headDict, sig):
981981
else:
982982
print("Invalid selection")
983983
exit(1)
984-
print("\nPlease enter the keylength:")
984+
print("\nPlease enter the hash length:")
985985
print("[1] ECDSA-256")
986986
print("[2] ECDSA-384")
987987
print("[3] ECDSA-512")
@@ -991,15 +991,15 @@ def tamperToken(paylDict, headDict, sig):
991991
print("Invalid selection")
992992
exit(1)
993993
if selLength == 1:
994-
keyLength = 256
994+
hashLength = 256
995995
elif selLength == 2:
996-
keyLength = 384
996+
hashLength = 384
997997
elif selLength == 3:
998-
keyLength = 512
998+
hashLength = 512
999999
else:
10001000
print("Invalid selection")
10011001
exit(1)
1002-
newSig, badSig, newContents = signTokenEC(headDict, paylDict, privKeyName, keyLength)
1002+
newSig, badSig, newContents = signTokenEC(headDict, paylDict, privKeyName, hashLength)
10031003
print("\nYour new forged token:")
10041004
print("[+] URL safe: "+newContents+"."+newSig)
10051005
print("[+] Standard: "+newContents+"."+badSig+"\n")
@@ -1029,7 +1029,7 @@ def tamperToken(paylDict, headDict, sig):
10291029
else:
10301030
print("Invalid selection")
10311031
exit(1)
1032-
print("\nPlease enter the keylength:")
1032+
print("\nPlease enter the hash length:")
10331033
print("[1] RSA-256")
10341034
print("[2] RSA-384")
10351035
print("[3] RSA-512")
@@ -1039,15 +1039,15 @@ def tamperToken(paylDict, headDict, sig):
10391039
print("Invalid selection")
10401040
exit(1)
10411041
if selLength == 1:
1042-
keyLength = 256
1042+
hashLength = 256
10431043
elif selLength == 2:
1044-
keyLength = 384
1044+
hashLength = 384
10451045
elif selLength == 3:
1046-
keyLength = 512
1046+
hashLength = 512
10471047
else:
10481048
print("Invalid selection")
10491049
exit(1)
1050-
newSig, badSig, newContents = signTokenPSS(headDict, paylDict, privKeyName, keyLength)
1050+
newSig, badSig, newContents = signTokenPSS(headDict, paylDict, privKeyName, hashLength)
10511051
print("\nYour new forged token:")
10521052
print("[+] URL safe: "+newContents+"."+newSig)
10531053
print("[+] Standard: "+newContents+"."+badSig+"\n")
@@ -1079,7 +1079,7 @@ def tamperToken(paylDict, headDict, sig):
10791079
print("Could not load file")
10801080
exit(1)
10811081
print("File loaded: "+keyFile)
1082-
print("\nPlease enter the keylength:")
1082+
print("\nPlease enter the hash length:")
10831083
print("[1] HMAC-SHA256")
10841084
print("[2] HMAC-SHA384")
10851085
print("[3] HMAC-SHA512")
@@ -1089,15 +1089,15 @@ def tamperToken(paylDict, headDict, sig):
10891089
print("Invalid selection")
10901090
exit(1)
10911091
if selLength == 1:
1092-
keyLength = 256
1092+
hashLength = 256
10931093
elif selLength == 2:
1094-
keyLength = 384
1094+
hashLength = 384
10951095
elif selLength == 3:
1096-
keyLength = 512
1096+
hashLength = 512
10971097
else:
10981098
print("Invalid selection")
10991099
exit(1)
1100-
newSig, badSig, newContents = signToken(headDict, paylDict, key1, keyLength)
1100+
newSig, badSig, newContents = signToken(headDict, paylDict, key1, hashLength)
11011101
print("\nYour new forged token:")
11021102
print("[+] URL safe: "+newContents+"."+newSig)
11031103
print("[+] Standard: "+newContents+"."+badSig+"\n")
@@ -1322,7 +1322,7 @@ def rejigToken(headDict, paylDict, sig):
13221322
print("$$ | $$ |$$$ / \$$$ | $$ | $$ |$$ | $$ |$$ | $$ |$$ |")
13231323
print("\$$$$$$ |$$ / \$$ | $$ | $$ |\$$$$$$ |\$$$$$$ |$$ |")
13241324
print(" \______/ \__/ \__| \__|$$$$$$\\__| \______/ \______/ \__|")
1325-
print(" Version 1.3.4 \______| @ticarpi ")
1325+
print(" Version 1.3.5 \______| @ticarpi ")
13261326
print()
13271327

13281328
parser = argparse.ArgumentParser(epilog="If you don't have a token, try this one:\neyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJsb2dpbiI6InRpY2FycGkifQ.bsSwqj2c2uI9n7-ajmi3ixVGhPUiY7jO9SUn9dm15Po", formatter_class=argparse.RawTextHelpFormatter)

0 commit comments

Comments
 (0)