ticarpi / jwt_tool Goto Github PK
View Code? Open in Web Editor NEW:snake: A toolkit for testing, tweaking and cracking JSON Web Tokens
License: GNU General Public License v3.0
:snake: A toolkit for testing, tweaking and cracking JSON Web Tokens
License: GNU General Public License v3.0
when try to run in windows 10, the out put is not colored, or this only work for linux only ?
It would be great if jwt_tool
would support encrypted JWT variants as described in https://datatracker.ietf.org/doc/html/rfc7519#appendix-A.
At the very least, it would be great if the JOSE header would be parsed and a hint would be displayed that the encountered data has such a header but is no "classical" (as in "unencrypted") JWT.
Windows 10
JWT_Tool 2.2.5
When jwt_tool.py run for the first time, it mentioned the creation of jwtconf.ini, but I couldn't find it in the jwt_tool.py directory (C:\Users\PC\Desktop\jwt_tool-2.2.5\jwt_tool-2.2.5), and still jwt_tool worked totally fine like it found that config file.
So I added a print statement for the path variable, and it printed this path (C:\Users\PC/.jwt_tool)
I not sure why it created it there and why the path separation character is inverted, but it seems like the function "os.path.join()" wasn't used to create paths, it handles the operating system pathing standards.
I've seen a lot of this recently, but it's a valid token.
Invalid token:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL3dzLzIwMDgvMDYvaWRlbnRpdHkvY2xhaW1zL3VzZXJkYXRhIjoie1wiVXNlcklEXCI6MjMyLFwiVXNlck5hbWVcIjpcImFkbWluXCIsXCJEb21haW5OYW1lXCI6XCJDUERcIixcIk1vYmlsZVBob25lXCI6XCIxMzI0NDQ0NDQ0NFwiLFwiSUVNSVwiOlwiXCIsXCJXb3JrUm9sZU5hbWVcIjpcIuezu-e7n-euoeeQhuWRmFwiLFwiV29ya1JvbGVDb2RlXCI6XCIxXCIsXCJJc1JlZ2lvblwiOjAsXCJQYXJlbnRJRFwiOm51bGwsXCJQYXJlbnROYW1lXCI6XCJcIixcIlN0YXR1c0lEXCI6MyxcIlJlZ2lvbklEXCI6bnVsbCxcIlJlZ2lvbk5hbWVzXCI6XCLlhajlm71cIixcIkNpdHlJRFwiOjEsXCJMb2dpbklEXCI6XCJhZG1pblwiLFwiUGFyZW50TG9naW5JRFwiOlwiXCIsXCJEb21haW5JRFwiOjEsXCJUaGlyZENvbXBhbnlJRFwiOm51bGwsXCJJc1RoaXJkQ29tcGFueVVzZXJcIjp0cnVlLFwiRFBSb2xlVHlwZVwiOjksXCJEUFR5cGVJRFwiOm51bGwsXCJQZXJtaXNzaW9uc1wiOm51bGwsXCJVc2VyQ2hhbm5lbFR5cGVcIjpudWxsLFwiQXZhdGFyVXJsXCI6XCIvc2ZhaW1hZ2UxLzIwMTkwMzIxLzIzMlxcXFw1MDcwNzdfMjAxOTAzMjExNjU1NTlmMGIwZTYxZS1hMDNiLTQyNjUtOWRlNi03NWE4ZDFiNDY5YjYuanBnXCIsXCJJc1Byb21vdGVyVGVhbVwiOm51bGwsXCJCdXNpbmVzc01vZGVsSURTXCI6bnVsbCxcIkNvdW50ZXJCcmFuZElEU1wiOm51bGwsXCJDaGFpbnN0b3JlSURTXCI6bnVsbCxcIkVtYWlsXCI6bnVsbH0iLCJuYmYiOjE1NzUwMDMxNTAsImV4cCI6MTU3NTAwNDk1MCwiaWF0IjoxNTc1MDAzMTUwLCJpc3MiOiJBNUZHREU2NC1FODRELTQ4NUEtQkU1MS01NkUyOTNEMDlBNjkiLCJhdWQiOiIwOTkxNTNjMjYyNTE0OWJjOGVjYjNlODVlMDNmMDAyMiJ9.xp5TmBFi0Nr89SeClOIcbtIRcU4BTZDCMNVZiBew9W4
Thanks for the tool, it works very well. However, I was a little confused when I resigned a token with RS256 with a private key I had because the tool asked me for the key length. So i checked the private key and it was a key with a key length of 1024bit, but I was only able to choose from an option of 256, 384, 512:
Line 335 in 09fb9e5
Hi, I'm using jwt_tool to perform attacks on a JWT token using the RS256 algorithms. I have the public key to check for the signature, but the tool is telling me that the signature is invalid. My token is valid, I can use it on my application and I checked with the PyJWT library which also say that the token is valid. So I'm wondering why the jwt_tool.py script say that the signature is not valid.
$ cat key.pub
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAq/Uptco13UyCUkJA9yTB
ZXsQWDA7UeAXr6qzD8TlEgwrV+bygZfa/sENCEf1c+ih78nlcxAYcnOE5tBToxH4
p7RqhosrshZnfQa7vycP4U9z0LEAJLOrfVi/FOEoIyXJXQ4A3wLDOm2kWLGUZTpA
A+RaQKkunwNkUbATL1N9jbG19VEH0/hsBPr5JDs9UlIqTs8h1DjzDfXr6dapdUd6
3ZjBzO+GTheyZoRf93Aqfp54f/AN0VsZ5QhUcFRqr6koZPB7cPDK4HsYAhRuf4xD
pTeILoQ72teCrpLfCZKh0nAView8MbJP3UYZZNmzMOlaE/zTi2W3HWVp4uDEkF8B
cQIDAQAB
-----END PUBLIC KEY-----
$ python3 jwt_tool.py -V -pk key.pub eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJWLWluY0lORkJRUF9Ucm9MbGZuYUFvdnk0VGgxNUxLQzNBR2IzZFI5cVg0In0.eyJleHAiOjE2MTc4ODM4MzYsImlhdCI6MTYxNzg4MzUzNiwiYXV0aF90aW1lIjoxNjE3ODgzNTM1LCJqdGkiOiI4MTllMWM3ZC1mNTAwLTQ3MmQtODk1ZC01NmVjMWQ4NTEzY2EiLCJpc3MiOiJodHRwczovLzAuMC4wLjA6ODQ0My9hdXRoL3JlYWxtcy90ZXN0IiwiYXVkIjoiYWNjb3VudCIsInN1YiI6IjI3ZTYzNWFjLTEyMDAtNGRiYy05ZmIzLWFkNjE2ZWI2MjZmMCIsInR5cCI6IkJlYXJlciIsImF6cCI6ImFjY291bnQtY29uc29sZSIsIm5vbmNlIjoiYjQwOWM5ZTctZjVhOC00YzFkLTg2NTctOGIyODAyZTdhZWM5Iiwic2Vzc2lvbl9zdGF0ZSI6IjFkMjJkODM2LTVjY2MtNDBjZi1hNzNlLWYxNGYzYjhjYTdhNSIsImFjciI6IjEiLCJyZXNvdXJjZV9hY2Nlc3MiOnsiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsIm1hbmFnZS1hY2NvdW50LWxpbmtzIl19fSwic2NvcGUiOiJvcGVuaWQgZW1haWwgcHJvZmlsZSIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwicHJlZmVycmVkX3VzZXJuYW1lIjoidGVzdCJ9.koCgf_1AIzusXLsiMuKTBKP0-dBdC8SPXEe1Bk5KeAZFQptXeMW8RZWWOEarJy9mp9HCVTv4eQRRx7lTjmVlVvpd2InrdjzcLjesmhOYYz7jBPPfFQm9lsu04WaSw9IBXeYp7f0m45kxjwX8eb36rtLUKQDssFyWWdjE5-_JRrHjA7wxHsHRFwcf4lSCnTnULXsA-7tvSHb7bnN93u2FBP9-J97eInb89pCoM13xKiwD9UBrkVoXiWNCqRPaq20zTyH9tajh8J9ebaIkBklb9xby1_224o6g6ilqQUy_8Hwy6ByEBL6q83EfQxfwpi8tVTyqCsNtlXBAEO1OorXy5Q
\ \ \ \ \ \
\__ | | \ |\__ __| \__ __| |
| | \ | | | \ \ |
| \ | | | __ \ __ \ |
\ | _ | | | | | | | |
| | / \ | | | | | | | |
\ | / \ | | |\ |\ | |
\______/ \__/ \__| \__| \__| \______/ \______/ \__|
Version 2.2.2 \______| @ticarpi
Original JWT:
RSA Signature is INVALID
With the PyJWT library the signature verification suceeds:
>>> token = "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJWLWluY0lORkJRUF9Ucm9MbGZuYUFvdnk0VGgxNUxLQzNBR2IzZFI5cVg0In0.eyJleHAiOjE2MTc4ODM4MzYsImlhdCI6MTYxNzg4MzUzNiwiYXV0aF90aW1lIjoxNjE3ODgzNTM1LCJqdGkiOiI4MTllMWM3ZC1mNTAwLTQ3MmQtODk1ZC01NmVjMWQ4NTEzY2EiLCJpc3MiOiJodHRwczovLzAuMC4wLjA6ODQ0My9hdXRoL3JlYWxtcy90ZXN0IiwiYXVkIjoiYWNjb3VudCIsInN1YiI6IjI3ZTYzNWFjLTEyMDAtNGRiYy05ZmIzLWFkNjE2ZWI2MjZmMCIsInR5cCI6IkJlYXJlciIsImF6cCI6ImFjY291bnQtY29uc29sZSIsIm5vbmNlIjoiYjQwOWM5ZTctZjVhOC00YzFkLTg2NTctOGIyODAyZTdhZWM5Iiwic2Vzc2lvbl9zdGF0ZSI6IjFkMjJkODM2LTVjY2MtNDBjZi1hNzNlLWYxNGYzYjhjYTdhNSIsImFjciI6IjEiLCJyZXNvdXJjZV9hY2Nlc3MiOnsiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsIm1hbmFnZS1hY2NvdW50LWxpbmtzIl19fSwic2NvcGUiOiJvcGVuaWQgZW1haWwgcHJvZmlsZSIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwicHJlZmVycmVkX3VzZXJuYW1lIjoidGVzdCJ9.koCgf_1AIzusXLsiMuKTBKP0-dBdC8SPXEe1Bk5KeAZFQptXeMW8RZWWOEarJy9mp9HCVTv4eQRRx7lTjmVlVvpd2InrdjzcLjesmhOYYz7jBPPfFQm9lsu04WaSw9IBXeYp7f0m45kxjwX8eb36rtLUKQDssFyWWdjE5-_JRrHjA7wxHsHRFwcf4lSCnTnULXsA-7tvSHb7bnN93u2FBP9-J97eInb89pCoM13xKiwD9UBrkVoXiWNCqRPaq20zTyH9tajh8J9ebaIkBklb9xby1_224o6g6ilqQUy_8Hwy6ByEBL6q83EfQxfwpi8tVTyqCsNtlXBAEO1OorXy5Q"
>>> pub_key = "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAq/Uptco13UyCUkJA9yTBZXsQWDA7UeAXr6qzD8TlEgwrV+bygZfa/sENCEf1c+ih78nlcxAYcnOE5tBToxH4p7RqhosrshZnfQa7vycP4U9z0LEAJLOrfVi/FOEoIyXJXQ4A3wLDOm2kWLGUZTpAA+RaQKkunwNkUbATL1N9jbG19VEH0/hsBPr5JDs9UlIqTs8h1DjzDfXr6dapdUd63ZjBzO+GTheyZoRf93Aqfp54f/AN0VsZ5QhUcFRqr6koZPB7cPDK4HsYAhRuf4xDpTeILoQ72teCrpLfCZKh0nAView8MbJP3UYZZNmzMOlaE/zTi2W3HWVp4uDEkF8BcQIDAQAB\n-----END PUBLIC KEY-----"
>>> jwt.decode(token, key=pub_key, algorithms=['RS256'], verify_signature=True, audience="account")
{'exp': 1617883836, 'iat': 1617883536, 'auth_time': 1617883535, 'jti': '819e1c7d-f500-472d-895d-56ec1d8513ca', 'iss': 'https://0.0.0.0:8443/auth/realms/test', 'aud': 'account', 'sub': '27e635ac-1200-4dbc-9fb3-ad616eb626f0', 'typ': 'Bearer', 'azp': 'account-console', 'nonce': 'b409c9e7-f5a8-4c1d-8657-8b2802e7aec9', 'session_state': '1d22d836-5ccc-40cf-a73e-f14f3b8ca7a5', 'acr': '1', 'resource_access': {'account': {'roles': ['manage-account', 'manage-account-links']}}, 'scope': 'openid email profile', 'email_verified': False, 'preferred_username': 'test'}
Thanks !
Hello,
The JTW token i was playing with contained some URL encoding in the payload.
Changing
payl = base64.b64decode(...)
to
payl = base64.urlsafe_b64decode(..)
Fixed the issue for me. :) Great tool btw.
The JWKS URL (specified with -ju) should be able to handle % encoded URL characters.
A parsing error happens when a % encoded URL character is found during parsing.
It looks like pythons configParser libary attempts to interpolate anything with a % sign preceding it, possibly replace all single % signs with a double % sign (%%) as this should escape the configParser trying to interpolate the string.
./jwt_tool.py -X s -ju http://notarealsite.net/?value=test%20 eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJsb2dpbiI6InRpY2FycGkifQ.bsSwqj2c2uI9n7-ajmi3ixVGhPUiY7jO9SUn9dm15Po
In the meantime, I might just write a small script that uses regex to replace single % with double %% before passing it through to the tool.
In jwt_tool.py, after a payload is modified, you use json.dumps() to convert the ordered dictionary into a json object, then b64 encode it.
newPaylB64 = base64.urlsafe_b64encode(json.dumps(newpaylDict,separators=(",",":")).encode()).decode('UTF-8').strip("=")
My question is regarding the json.dumps() compact encoding (i.e. the separators=(",",":")
). The result is that spaces in the json object are removed so a payload like this {"login": "taylor"}
becomes {"login":"taylor"}
. The space after the colon has been removed.
Referring to RFC 7519 standard:
"The JSON object consists of zero or more name/value pairs (or members), where the names are strings and the values are arbitrary JSON values. These members are the claims represented by the JWT. This JSON object MAY contain whitespace and/or line breaks before or after any JSON values or structural characters ..."
Thank you for the tool!
how can I use the scripts with multi headers?
I know this is really bad practise, but currently I'm pentesting an API which is sending the JWT as a simple get parameter. JWT_Tool does not support this case.
Maybe it can/should be added for such cases?
Thanks for this great tool!
when i try to tamper the nested value of a jwt token i got this error :
Traceback (most recent call last):
File "/usr/local/bin/jwt_tool", line 1403, in
tamperToken(paylDict, headDict, sig)
File "/usr/local/bin/jwt_tool", line 795, in tamperToken
newVal[subclaim] = paylDict[pair][subclaim]
TypeError: 'int' object is not subscriptable
When using crackstation's wordlist (which is about 15GB) I went into swapping hell but luckily the python interpreter was nice and killed the process. Is there an easy way to load the wordlist in batches?
Hi @ticarpi
While trying to tamper JWT values, I figured out that, when providing an input, it adds annoying quotation marks, resulting in an error for integers (e.g. ValueError: invalid literal for int() with base 10: '"12345"'
) and making invalid strings.
When i add values choosing 1 but after that no cracking options shows.
Hi,
After reading this blog post about the CVE-2022-21449, I was wondering if it can be interesting to add a test case with a JWT token for which the signature will be filled with 0 and the algorithm will be ES256, ES256K, ES384 or ES512 (see here)?
Example:
eyJhbGciOiJFUzI1NiJ9.eyJzdWIiOiJURVNUIn0.MAYCAQACAQA
Sample vulnerable app:
It is just a suggestion so feel free to close this issue if it is not relevant ๐
Can you provide a setup.py script?
I recently encountered a valid JWT (well, valid at least for the app that used it) that could not be handled by jwt_tools because it used an invalid value in the 'alg' header; instead of HS512 it contained "http://www.w3.org/2001/04/xmldsig-more#hmac-sha512". This is apparently used for XML formatted data, and is detailed in RFC 4051.
I am not sure if this is commonly used, maybe it could be interesting to support it.
Commit of a quick and dirty implementation for my use case: DidierA@d3b1bb8
RIght now jwt_tool uses sys.path[0] to store the config and the log files(https://github.com/ticarpi/jwt_tool/blob/master/jwt_tool.py#L1882) which makes it impossible to package it, and makes it annoying to share a single install between multiple projects. It would be nice to have it configurable via a CLI flag, have it use the CWD or use an envvar
Hey! I'm trying to add multiple headers to the request but i'm getting erros.
How the syntax is supposed to be? In my example I need to use three headers: Authorization, Origin and Host
Appreciate any help
command used
python3 ./jwt_tool.py "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJ1c2VybmFtZSI6InRlc3QifQ.GU72t7mfy31jMvyY7hSinJBtAntSqjeuqJa6el2PGPaq36hkZtn8fVo8JEgv7hnEdOHkibVLz9MLUca12yLmbylSxl-Nh2_pMf2s03JBsKs7oIJeBKjj7Pw4lXp1TQQj6ISTwzeBNAUlv4VXJ11G-mPFKwYxTOQg7IX0FxyGMlGbLKoe3TXbw7trXwXevC9O_q_cxHRFMINg9vPAATKI0_PfMJPGBdewILLf1aExd37QhTUts8IE11ak3To8TDnQZ14h14evccnWfVp8sQOFo81Rlp5r1j3WBQnaEsYhVMKuBgW2osceqgFG8ABIYj8eF7vtRzaJUMTVe_dUk0x43A8Meb5Xe2TdyIOkhoQPHTZ3BYxLX4pW_yrjjPSAWSfCAEm07fqYc4tP7IXvZ7rtlGwq_eMoBotGj8KJAI1FqAc1kh6fC0KdQvvAY2XhifJZArCpXsRiyoSdjB5oJVeDlsjyQ4HUcgfn8Yn0sEdC6tqyATIAMMWaGMDb54Iw0NX7F4P2VrCeZ75A3K-patffZFxyssqeS-rMYkbn8O7lXfaxoe8us-IKN5wCwNBp82CSU0qR8U2iWU4Or22kNBRFuVV5sr2huMkIf1dodVmpodAExfiwEs28DCkKf9y5uV6fHJohX1Bo31JdghbsgPufM_z3GD1HSfBaMUpUSO6vJME" -v -X k -I -pc username -pv admin -pk ../public.pem
new payload generated
eyJ1c2VybmFtZSI6ImFkbWluIn0 with '=' stripped off. I understand that during transport = can be stripped off and but then inserted before decoding. Some server APIs processing jwt don't do that, hence it causes the failure. I would request not to strip off "=" signs to be safe.
I spent several hours trouble shooting this.
echo eyJ1c2VybmFtZSI6ImFkbWluIn0 | base64 -d. ( this causes the } in payload to strip off
{"username":"admin%
------- correct
echo eyJ1c2VybmFtZSI6ImFkbWluIn0= | base64 -d
{"username":"admin"}%
Hello :)
I have an error when i run the tool at windows with the following command:
python jwt_tool.py -M at -t "https:///api/auth/myself" -rh "Authorization: Bearer *" -rc "COOKIES"
The following error occurred (like in the screenshot):
UnicodeDecodeError: 'charmap' codec can't decode byte 0x8f in position 1824: character maps to <undefined>
Can you take care of it?
Thanks
HTTP header values with colons like "Origin: https://www.example.com" gives "ValueError: too many values to unpack (expected 2)" error
Line 141 in 3d92dcd
Hi,
are you thinking to add support for HTTP/2 reuqests?
I was able to replace the protocol with a proxy but a native support would be great.
Thanks
There is no space between the curl command and its parameter (injectUrl). Is it done purposely? In what situation would it execute properly?
Line 1466 in bba0607
{"typ":"JWT","alg":"HS256","kid":"|curlhttp://example.com/RCE_in_kid"}
In version 2.2.3, when -T is combined with -X k -pk, the output does not contain the tampered results. It seems these flags cannot be used together.
Workaround: Using -T alone works. The output from a -T run can then be used as the input for a -X k -pk run to get the desired results.
It seems like loading postdata from a file is not supported (or if it is it's not obvious). This would be a really nice feature to have for those times when a the request is larger.
Traceback (most recent call last):
File "/opt/jwt_tool/jwt_tool.py", line 2028, in <module>
rejigToken(headDict, paylDict, sig)
File "/opt/jwt_tool/jwt_tool.py", line 1291, in rejigToken
comparestamps, expiredtoken = dissectPayl(paylDict)
File "/opt/jwt_tool/jwt_tool.py", line 1185, in dissectPayl
timestamp = datetime.fromtimestamp(int(paylDict[claim]))
ValueError: year 53831 is out of range
$ python jwt_tool.py 'ew...Ry' /usr/share/wordlists/password/rockyou.txt
Token header values:
[+] alg = ES512
[+] typ = JWT
# 6: Crack signature with supplied dictionary file #
When I use option 6 on a ES512, I have the following error.
Algorithm is not HMAC-SHA - cannot test with this tool.
Which seems normal but the problem is that it loops over it indefinitely.
I made a asciinema "video" of it.
To play it: asciinema play xxx.cast
SOLVED
Hi,
for me, launching the tool produces the following error when checking for external interaction :
Traceback (most recent call last):
File "/home/csaudit/jwt_tool/jwt_tool.py", line 2098, in
runScanning()
File "/home/csaudit/jwt_tool/jwt_tool.py", line 1703, in runScanning
scanModePlaybook()
File "/home/csaudit/jwt_tool/jwt_tool.py", line 1489, in scanModePlaybook
for payloadClaim in backupDict:
RuntimeError: OrderedDict mutated during iteration
It seems to be caused by the del statement in the function injectExternalInteractionPayload (line 1639), as replacing it with a print statement fixes the issue. Am I doing anything wrong or is this a bug?
Best,
FH
Traceback (most recent call last):
File "/home/emily/Tools/jwt_tool/jwt_tool.py", line 2024, in
rejigToken(headDict, paylDict, sig)
File "/home/emily/Tools/jwt_tool/jwt_tool.py", line 1327, in rejigToken
jwtOut(newContents+"."+sig, "Sending token")
File "/home/emily/Tools/jwt_tool/jwt_tool.py", line 231, in jwtOut
resData = sendToken(token, cookiedict, logID, headertoken[0], posttoken[0])
File "/home/emily/Tools/jwt_tool/jwt_tool.py", line 150, in sendToken
response = requests.get(url, headers=headers, cookies=cookiedict, proxies=False, verify=False)
File "/usr/lib/python3/dist-packages/requests/api.py", line 76, in get
return request('get', url, params=params, **kwargs)
File "/usr/lib/python3/dist-packages/requests/api.py", line 61, in request
return session.request(method=method, url=url, **kwargs)
File "/usr/lib/python3/dist-packages/requests/sessions.py", line 528, in request
prep = self.prepare_request(req)
File "/usr/lib/python3/dist-packages/requests/sessions.py", line 456, in prepare_request
p.prepare(
File "/usr/lib/python3/dist-packages/requests/models.py", line 317, in prepare
self.prepare_headers(headers)
File "/usr/lib/python3/dist-packages/requests/models.py", line 451, in prepare_headers
check_header_validity(header)
File "/usr/lib/python3/dist-packages/requests/utils.py", line 957, in check_header_validity
raise InvalidHeader("Invalid return character or leading space in header: %s" % name)
requests.exceptions.InvalidHeader: Invalid return character or leading space in header: Authorization
I made sure to triple check that the character/leading space in the header Authorization is correct as well as the rest of the syntax for my arguments. Any ideas? Thanks!!
I am using the most recent version of Kali Linux that's running on virtualbox installed on my Windows machine.
Your tool/software has been inventoried on Rawsec's CyberSecurity Inventory.
https://inventory.rawsec.ml/tools.html#jwt_tool
An inventory of tools and resources about CyberSecurity. This inventory aims to help people to find everything related to CyberSecurity.
More details about features here.
Note: the inventory is a FLOSS (Free, Libre and Open-Source Software) project.
Mainly because this is giving visibility to your tool and improve its referencing.
The badge shows to your community that your are inventoried. It looks good but also shows you care about your project, that your tool is referenced.
Feel free to claim your badge here: http://inventory.rawsec.ml/features.html#badges, it looks like that , but there are several styles available.
If you want to thank us, you can help make our open project better known by tweeting about it! For example:
That's all, this message is just to notify you if you care. Else you can close this issue.
In the latest version (2.2.5) the regex to find the JWT token is set to the following. However, the payload of the token I was about to test started with eyI
. This didn't match the regex and resulted in Cannot find a valid JWT
.
eyJ[A-Za-z0-9_\/+-]*\.eyJ[A-Za-z0-9_\/+-]*\.[A-Za-z0-9._\/+-]*
Modifying all regexes in the code to the following resolved my issue.
eyJ[A-Za-z0-9_\/+-]*\.ey[A-Za-z0-9_\/+-]*\.[A-Za-z0-9._\/+-]*
Following up from Twitter, hoping to get more details on CVE-2020-28637. Thanks! (tag @ticarpi)
I'm new to Python, and also newer to security.
This seems like a newer project.
Why did you write it in Python 2 when end of life for python 2 now has a definite date?
No judgement here...I'm just trying to learn.
On shells, the exit code 1 means error. However, if you run these commands below, they all exit the error code 1
python3 jwt_tool.py eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJsb2dpbiI6InRpY2FycGkifQ.bsSwqj2c2uI9n7-ajmi3ixVGhPUiY7jO9SUn9dm15Po
python3 jwt_tool.py --exploit n eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJsb2dpbiI6InRpY2FycGkifQ.bsSwqj2c2uI9n7-ajmi3ixVGhPUiY7jO9SUn9dm15Po
python3 jwt_tool.py --query jwttool_298d3877b773b51a46e8dc441d1a9453
What are the reasons of using exit(1)
instead of exit(0)
?
And thinking about integration with other tools (like CI/CD for automated testing, like I am doing here with CORScanner for securing our own applications), could we change the behavior to the one below?
1
when there is an error on CLI params/options (Ex: python3 jwt_tool.py -X hello -p
)3-125
range for vulnerabilities found with given tests (Ex: found CVE-2015-2951 alg=none on https://example.com/)0
(for success) when no vulnerability is found with the given tests (Ex: tested https://example.com/ against CVE-2018-0114 Key injection but website passed test)PS C:\Users\WhoAmI\Desktop> python3 $jwt_tool eyJraWQiOiJlYThmNjRjNi1hM2VmLTRiZDctOWRmYi1iNjM0YmRkZjViMmIiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJwb3J0c3dpZ2dlciIsInN1YiI6IndpZW5lciIsImV4cCI6MTY1NTM4NDY1NX0.MCxsDgrmR5GDgncc7o0hh1eM5jcnzOnl7eKPoCiZ6Yc -I -hc kid -hv "../../../../../../dev/null" -S hs256 -p ""
usage: jwt_tool.py [-h] [-b] [-t TARGETURL] [-rc COOKIES] [-rh HEADERS] [-pd POSTDATA] [-cv CANARYVALUE] [-np]
[-M MODE] [-X EXPLOIT] [-ju JWKSURL] [-S SIGN] [-pr PRIVKEY] [-T] [-I] [-hc HEADERCLAIM]
[-pc PAYLOADCLAIM] [-hv HEADERVALUE] [-pv PAYLOADVALUE] [-C] [-d DICT] [-p PASSWORD] [-kf KEYFILE]
[-V] [-pk PUBKEY] [-jw JWKSFILE] [-Q QUERY] [-v]
[jwt]
jwt_tool.py: error: argument -p/--password: expected one argument
When running python3 jwt_tool.py -T or -R i get:
Traceback (most recent call last):
File "jwt_tool.py", line 1393, in
tamperToken(paylDict, headDict, sig)
File "jwt_tool.py", line 761, in tamperToken
comparestamps, expiredtoken = dissectPayl(paylDict, count=True)
File "jwt_tool.py", line 1187, in dissectPayl
elif type(castInput(paylDict[claim][subclaim])) == str:
File "jwt_tool.py", line 91, in castInput
newInput = json.loads(newInput)
File "C:**\AppData\Local\Programs\Python\Python38-32\lib\json_init_.py", line 341, in loads
raise TypeError(f'the JSON object must be str, bytes or bytearray, '
TypeError: the JSON object must be str, bytes or bytearray, not list
or
Traceback (most recent call last):
File "jwt_tool.py", line 1483, in
rejigToken(headDict, paylDict, sig)
File "jwt_tool.py", line 1280, in rejigToken
comparestamps, expiredtoken = dissectPayl(paylDict)
File "jwt_tool.py", line 1187, in dissectPayl
elif type(castInput(paylDict[claim][subclaim])) == str:
File "jwt_tool.py", line 91, in castInput
newInput = json.loads(newInput)
File "C:**\AppData\Local\Programs\Python\Python38-32\lib\json_init_.py", line 341, in loads
raise TypeError(f'the JSON object must be str, bytes or bytearray, '
TypeError: the JSON object must be str, bytes or bytearray, not list
Firstly, thanks for a great tool!
I'm not sure there's much that can be done about this, but I wanted to log it in case anyone else has the same experience.
If the original JWT token includes a (correctly) escaped forward slash in the issuer, such as {"iss":"my.domain.com\/iss-endpoint"}
, then jwt_tool fails to recognise the canary value test has been met, such as:
[+] Sending token
jwttool_b5c2514b08df054d76b80e7d6a98c70d Sending token Response Code: 403, 91 bytes
Running Scanning Module:
Running prescan checks...
[+] FOUND "clickref" in response:
jwttool_86deb84549be4dcfd8661a928c5b76f8 Prescan: original token Response Code: 200, 254 bytes
Canary value (myvalue) was not found in base request - check that this token is valid and you are still logged in
Do you wish to continue anyway? ("Y" or "N")
Here you can see the original request failed with a 403 response, this seems to be due to how the rejigToken function works.
Having spent sometime looking in to this, it appears to be due to the json decoding and re-encoding not resulting in the same result, which invalidates the original signature.
This Python code may help illustrate it more:
import json
json_string = '{"iss":"my.domain.com\/iss-endpoint"}'#
json_object = json.loads(json_string)
new_json_string = json.dumps(json_object)
if json_string == new_json_string:
print("json remains unchanged:" + new_json_string)
else:
print("json had changed! original:"+ json_string + " new:" + new_json_string)
Running this will output:
$ python example.py
json had changed! original:{"iss":"my.domain.com\/iss-endpoint"} new:{"iss": "my.domain.com/iss-endpoint"}
For clarity my jwt_tool command was like:
python jwt_tool.py -pd "{MY POST DATA}" -t {MY URL} -rh "Authorization: Bearer {MY JWT}" -M pb -cv "myvalue"
The escaping of the forward slash as "\/" is valid (but optional) according to the specification.
I'm not sure whether it would be possible to "fix" this in jwt_tool or not?
Selecting to modify the second element with an intention to modify the username causes an error
Please select an option from above (1-5):
> 2
[1] status = "success"
[2] data = JSON object:
[+] id = true
[+] username = ""
Results in:
(or 0 to Continue)
id
Traceback (most recent call last):
File "jwt2_tool.py", line 1888, in <module>
runActions()
File "jwt2_tool.py", line 1590, in runActions
tamperToken(paylDict, headDict, sig)
File "jwt2_tool.py", line 370, in tamperToken
newVal[subclaim] = paylDict[pair][subclaim]
TypeError: 'int' object is not subscriptable
This issue happened for me on Windows 10.
JWT_Tool 2.2.5
The following error happened:-
Traceback (most recent call last):
File "C:\Users\PC\Desktop\jwt_tool-2.2.5\jwt_tool-2.2.5\jwt_tool.py", line 2104, in <module>
runScanning()
File "C:\Users\PC\Desktop\jwt_tool-2.2.5\jwt_tool-2.2.5\jwt_tool.py", line 1697, in runScanning
scanModePlaybook()
File "C:\Users\PC\Desktop\jwt_tool-2.2.5\jwt_tool-2.2.5\jwt_tool.py", line 1496, in scanModePlaybook
commonPass = commonPassList.readline().rstrip()
File "C:\Users\PC\AppData\Local\Programs\Python\Python39\lib\encodings\cp1252.py", line 23, in decode
return codecs.charmap_decode(input,self.errors,decoding_table)[0]
UnicodeDecodeError: 'charmap' codec can't decode byte 0x8f in position 1734: character maps to <undefined>
I fixed it by adding the argument "encoding='utf-8'" to the open() function in line 1495.
After that the following error happened:-
Traceback (most recent call last):
File "C:\Users\PC\Desktop\jwt_tool-2.2.5\jwt_tool-2.2.5\jwt_tool.py", line 2104, in <module>
runScanning()
File "C:\Users\PC\Desktop\jwt_tool-2.2.5\jwt_tool-2.2.5\jwt_tool.py", line 1697, in runScanning
scanModePlaybook()
File "C:\Users\PC\Desktop\jwt_tool-2.2.5\jwt_tool-2.2.5\jwt_tool.py", line 1499, in scanModePlaybook
jwtOut(newContents+"."+newSig, "Checking for alternative accepted HMAC signatures, based on common passwords. Testing: "+commonPass+"", "This token can exploit a hard-coded common password in the config")
File "C:\Users\PC\Desktop\jwt_tool-2.2.5\jwt_tool-2.2.5\jwt_tool.py", line 261, in jwtOut
setLog(token, genTime, logID, fromMod, curTargetUrl, additional)
File "C:\Users\PC\Desktop\jwt_tool-2.2.5\jwt_tool-2.2.5\jwt_tool.py", line 270, in setLog
logFile.write(logID+" - "+logLine+" - "+jwt+"\n")
File "C:\Users\PC\AppData\Local\Programs\Python\Python39\lib\encodings\cp1252.py", line 19, in encode
return codecs.charmap_encode(input,self.errors,encoding_table)[0]
UnicodeEncodeError: 'charmap' codec can't encode characters in position 152-182: character maps to <undefined>
I fixed it by adding the argument "encoding='utf-8'" to the open() function in line 269.
After that the script worked totally fine, the "encoding" argument should always be specified in any call for the "open()" function to prevent any confusion for characters that is not included in ASCII charset, other argument can be added to to ignore any characters that don't exist even in the utf-8 charset, the "ignore" parameter.
help!!!
When you load a too big dictionary (here rockyou.txt that is 134Mo):
,----.,----.,----.,----.,----.,----.,----.,----.,----.,----.
----''----''----''----''----''----''----''----''----''----'
,--.,--. ,--.,--------.,--------. ,--.
| || | | |'--. .--''--. .--',---. ,---. | |
,--. | || |.'.| | | | | | | .-. || .-. || |
| '-' /| ,'. | | |,----.| | ' '-' '' '-' '| |
`-----' '--' '--' `--''----'`--' `---' `---' `--'
,----.,----.,----.,----.,----.,----.,----.,----.,----.,----.
'----''----''----''----''----''----''----''----''----''----'
Traceback (most recent call last):
File "jwt_tool/jwt_tool.py", line 253, in <module>
keyLst = f.readlines()
MemoryError
Hi!
@silentsignal recently published an article on how to reconstruct RSA keys from RSA signatures, and how it can be useful with key-confusion attacks on JWT.
They published rsa_sign2n, which can be used to recreate RSA keys from JWTs. It would be useful to have this attack implemented in jwt_tool.
Thank you for this tool, and for the very detailed wiki!
I would notify that there is a bug on the script jwt_tool.py at line 704 which cause the following exception when running a jwt scan:
AttributeError: 'RsaKey' object has no attribute 'export_key'
To fix it is necessary to substitute the affected line 704 containing:
privKey = key.export_key(format="PEM")
with the following:
privKey = key.exportKey(format="PEM")
On version 2.2.1 but I think i saw it on the previous version as well:
Seen timestamps:
[] exp was seen <-nothing here
[] iat is earlier than exp by: 0 days, 0 hours, 0 mins
Hello there,
I get the following error message. It looks like it is failing on the TIMESTAMP
:
> python3 jwt_tool.py [JWT_TOKEN]
=====================
Decoded Token Values:
=====================
Token header values:
[+] typ = "JWT"
[+] alg = "HS512"
Token payload values:
[+] UserId = "215"
[+] App = "C467**********************"
[+] iss = "************* JWT"
[+] VenueId = 265
[+] UserName = "*************"
[+] VenueName = "*************"
[+] VenueType = 1
[+] SysCategory = 0
[+] aud = "C46***************************************"
[+] exp = 1594211740 ==> TIMESTAMP = 2020-07-08 13:35:40 (UTC)
Traceback (most recent call last):
File "jwt_tool.py", line 1483, in <module>
rejigToken(headDict, paylDict, sig)
File "jwt_tool.py", line 1280, in rejigToken
comparestamps, expiredtoken = dissectPayl(paylDict)
File "jwt_tool.py", line 1171, in dissectPayl
timestamp = datetime.datetime.fromtimestamp(int(paylDict[claim]))
ValueError: invalid literal for int() with base 10: '07/07/2020 12:35:40'
Thanks
error:
File "/mnt/hgfs/Tools/jwt_tool/jwt_tool.py", line 1238, in validateToken
cprintc(head.decode('UTF-8'))
line 1238 and 1248 miss the color arg.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.