The document discusses the insecurities associated with JSON Web Tokens (JWT), highlighting their structure, usage, and potential vulnerabilities inherent in various libraries. It emphasizes the importance of strong keys, proper signature verification, and the need for tokens to expire, while also addressing specific attack strategies that exploit JWT implementations. Overall, it concludes that JWTs are complex and can pose security risks if not properly managed.
About Me
PentesterLab.com /@PentesterLab
Security Engineer:
PentesterLab:
Pentester/Code Reviewer/Security consultant/Security architect
Platform to learn web security/penetration testing
100% Hands-on
Available for individuals (free and PRO) and enterprises
Run a website to help people learn security
4.
JOSE/JWE/JWS/JWT
PentesterLab.com / @PentesterLab
•JOSE:
• Javascript Object Signing and Encryption
• Also the name of the working group
• JWT: JSON Web Token == “jot” Token
• JWE: JSON Web Encryption
• JWS: JSON Web Signature
• JWK: JSON Web Key
• JWA: JSON Web Algorithm
5.
Who uses JWT
PentesterLab.com/ @PentesterLab
• A lot of people for OAuth
• A lot of people for sessions
• A lot of people to manage trust
• A lot of people for password reset
• A lot of people who care about being stateless
and multi-datacenter architecture
JavaScript Object Notation(JSON)
PentesterLab.com / @PentesterLab
Human readable format to store or transmit objects
8.
The Compact JWSFormat
PentesterLab.com / @PentesterLab
Header Payload Signature
3 parts in a JSON Web Token:
9.
The Compact JWSFormat
PentesterLab.com / @PentesterLab
Header Payload Signature
Separated by a dot
. .
10.
The Compact JWSFormat
PentesterLab.com / @PentesterLab
eyJ0eXAiOiJK
V1QiLCJhbGci
OiJIUzI1NiJ9
eyJsb2dpbi
I6ImFkb
WluIn0
FSfvCBAwypJ4abF6
jFLmR7JgZhkW674
Z8dIdAIRyt1E
Separated by a dot
. .
eyJ = Base64('{"')
11.
The Compact JWSFormat
PentesterLab.com / @PentesterLab
Base64({…}) Base64({…}) Base64(…)
Header and Payload are base64* encoded JSON
. .
* urlsafe base64 encoding without padding
The signature is also base64 encoded
12.
The Compact JWSFormat: Encoding
PentesterLab.com / @PentesterLab
Urlsafe base64 encoding without padding:
*https://tools.ietf.org/html/rfc7515#appendix-C
13.
The JWT Format:header
PentesterLab.com / @PentesterLab
Base64({"alg": "HS256",
"typ": "JWS"})
The header contains an algorithm “alg” attribute:
In this example HMAC with SHA256 was used
To tell how the token was signed.
…
. . …
14.
The JWT Format:Algorithms
PentesterLab.com / @PentesterLab
A lot of different algorithms are supported*:
None
* https://jwt.io/ covers most
HS256
HS384
HS512
RS256
RS384
RS512
ES256
ES384
ES512
PS256
PS384
PS512
15.
The JWT Format:Algorithms
PentesterLab.com / @PentesterLab
Scenario: one client talking to multiple services
16.
The JWT Format:Algorithms
PentesterLab.com / @PentesterLab
HS256
HS384
HS512
HMAC: All services need to know the secret
17.
The JWT Format:Algorithms
PentesterLab.com / @PentesterLab
HS256
HS384
HS512
HMAC: if one service gets compromised
18.
The JWT Format:Algorithms
PentesterLab.com / @PentesterLab
HS256
HS384
HS512
HMAC: the secret is compromised for all services
19.
The JWT Format:Asymmetric
PentesterLab.com / @PentesterLab
RS256
RS384
RS512
ES256
ES384
ES512
PS256
PS384
PS512
Asymmetric: sharing the key
Private
Public
20.
The JWT Format:Asymmetric
PentesterLab.com / @PentesterLab
RS256
RS384
RS512
ES256
ES384
ES512
PS256
PS384
PS512
Asymmetric: Only trusted services get the
private key
Private
Public
21.
The JWT Format:Asymmetric
PentesterLab.com / @PentesterLab
RS256
RS384
RS512
ES256
ES384
ES512
PS256
PS384
PS512
Asymmetric: If one service gets compromised…
Private
Public
22.
The JWT Format:Asymmetric
PentesterLab.com / @PentesterLab
RS256
RS384
RS512
ES256
ES384
ES512
PS256
PS384
PS512
Asymmetric: Even in the browser!
Private
Public
23.
The JWT Format:payload
PentesterLab.com / @PentesterLab
…
The payload may contain literally anything:
Base64({"user":"admin",
"roles": ["adm","users"]}). . …
24.
The JWT Format:payload
PentesterLab.com / @PentesterLab
The payload may contain registered claims:
Base64({"user":"admin",
"exp":12…, "iat":1234.. }). .… …
25.
The JWT Format:payload
PentesterLab.com / @PentesterLab
The payload may contain registered claims:
• “iss”: issuer
• “sub”: subject
• “aud”: audience
• “jti”: claim id
• “exp”: expiration time
• “nbf”: not before
• “iat”: issued at*
* useful for async processing
26.
The JWT Format:creating a token
PentesterLab.com / @PentesterLab
• Create the JSON header and base64 encode it
• Create the JSON payload and base64 encode it
• Concatenate with a dot the (encoded) header
and payload
• Sign the result (header+.+payload)
• Base64 encode the signature
• Append a dot then the signature
27.
The JWT Format:verifying a token
PentesterLab.com / @PentesterLab
• Split the token in three parts based on the dots
• Base64 decode each part
• Parse the JSON for the header and payload
• Retrieve the algorithm from the header
• Verify the signature based on the algorithm
• Verify the claims
28.
Keep in mind
PentesterLab.com/ @PentesterLab
• Multiple systems can issue tokens
• A token can be used by multiple systems
• All these systems can use different libraries
By design: verifyingsignature
PentesterLab.com / @PentesterLab
Base64({ "alg": "HS256",
"typ": "JWS"})
You need to base64 decode and parse
JSON to verify the signature:
Larger attack surface
JSON.load vs JSON.parse, Base64 decoding
…
. . …
31.
By design: verifyingsignature
PentesterLab.com / @PentesterLab
The attacker controls the algorithm used:
Downgrade attacks, confusion attack
Base64({ "alg": "HS256",
"typ": "JWS"})
…
. . …
32.
By design: Confusionattack
PentesterLab.com / @PentesterLab
Exploitation:
• Get a token signed with RSA (you only have
access to the public key)
• Decode the header and change the algorithm
from RSA “RS256” to HMAC “HS256”
• Tamper with the payload
• Sign the token with the public RSA key
• Profit
33.
By design: verifyingsignature
PentesterLab.com / @PentesterLab
…
Claims are optionals and not always supported*:
Always-valid tokens?
Base64({"user":"admin",
"exp":12…, "iat":1234.. }). . …
* Check https://jwt.io/
34.
By design: verifyingsignature
PentesterLab.com / @PentesterLab
Claims are optionals
and not always
supported*
Always-valid tokens?
* Check https://jwt.io/
35.
By design: verifyingsignature
PentesterLab.com / @PentesterLab
Signed data: you cannot (easily) manage quick-
revocation*:
The claim “jti” and a cache can be used to limit
the impact of this
No quick-revocation! Replay
* Unless you rotate the key or manage a server-side cache
36.
By design: TheNone algorithm
PentesterLab.com / @PentesterLab
JWT RFC contains a None algorithm
No integrity!
Basically an unsigned token…
37.
By design: TheNone algorithm
PentesterLab.com / @PentesterLab
Exploitation:
• Get a token
• Decode the header and change the algorithm to
“None” or “none”
• Decode and tamper with the payload
• Keep or remove the signature
• Profit
Libraries: CVE-2018-0114
PentesterLab.com /@PentesterLab
JWS allows you to add a “jwk” attribute (JSON Web
Key) to the header to tell the receiver what key was
used to sign the token:
40.
Libraries: CVE-2018-0114
PentesterLab.com /@PentesterLab
• Vulnerability in Cisco Node Jose
• Node-Jose uses the embedded “jwk” key to check
the signature
Integrity bypass!
41.
Libraries: CVE-2018-0114 -Exploitation
PentesterLab.com / @PentesterLab
Exploitation:
• Get a token
• Decode and tamper with the payload
• Generate a RSA key
• Add “n" & “e” to the header and use
RS256
• Sign the token with your RSA key
Libraries: Go-JOSE version<= 1.0.5
PentesterLab.com / @PentesterLab
From: https://rwc.iacr.org/2017/Slides/nguyen.quan.pdf
The issue:
Integrity of the protected bypass!
45.
Libraries: Go-JOSE version<= 1.0.5
PentesterLab.com / @PentesterLab
If the application trusts the protected*:
* you cannot change the payload
46.
Libraries: Go-JOSE version<= 1.0.5 - Exploitation
PentesterLab.com / @PentesterLab
Exploitation:
• Get a token (compact or full)
• Modify it to use the full format
• Add your malicious protected
• Profit
Using Libraries: weaksecret
PentesterLab.com / @PentesterLab
Some developers use weak secrets.
Reminder: you only need one token to brute force
the secret (completely offline)
Integrity bypass!
49.
Using Libraries: decodevs verify
PentesterLab.com / @PentesterLab
A lot of libraries have two functions/methods:
• decode <- don’t use this one
• verify
Integrity bypass!
50.
Using Libraries: decodevs verify
PentesterLab.com / @PentesterLab
Exploitation:
• Get a token
• Decode and tamper with the header or payload
• Profit
51.
Using Libraries: notusing exp or iat
PentesterLab.com / @PentesterLab
In many libraries you need to opt-in to use “exp” or
“iat”
Always-valid tokens?
Recommendations
PentesterLab.com / @PentesterLab
✓Use strong keys and secrets
✓ Review the libraries you pick (KISS library)
✓ Make sure you check the signature
✓ Make sure your tokens expire
✓ Enforce the algorithm
54.
Conclusion
PentesterLab.com / @PentesterLab
•JWT are complex and kind of insecure by design
• JWT libraries introduce very interesting bugs
• Make sure you test for those if you pentest or do
bug bounties
55.
Any questions?
FOR YOURTIME
THANKS!
louis@pentesterlab.com / PentesterLab.com / @PentesterLab