Nyffenegger JWAT PDF

Download as pdf or txt
Download as pdf or txt
You are on page 1of 63

JWAT ??

Attacking JSON WEB TOKENS…

Louis Nyffenegger
@PentesterLab
[email protected]
About me

Security Engineer

Pentester/Code Reviewer/Security consultant/Security architect


Run a website to help people learn security

PentesterLab:
Platform to learn web security/penetration testing

100% Hands-on

Available for individuals (free and PRO) and enterprises

PentesterLab.com / @PentesterLab
Who uses JWT?

• 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

PentesterLab.com / @PentesterLab
Acronyms

• 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
PentesterLab.com / @PentesterLab
Crypto 101
Signature vs Encryption

Encryption gives you confidentiality

Signature gives you integrity

PentesterLab.com / @PentesterLab
Multiple ways of signing

• With a secret using HMAC


• With a private key using RSA/EC/… (asymmetric)

PentesterLab.com / @PentesterLab
Signing with a secret

Sign! Verify!

Secret

PentesterLab.com / @PentesterLab
Signing: asymmetric

Sign! Verify!

Public Private

PentesterLab.com / @PentesterLab
THE JWT FORMAT
JavaScript Object Notation (JSON)

Human readable format to store or transmit objects

PentesterLab.com / @PentesterLab
The Compact JWS Format

3 parts in a JSON Web Token:

Header Payload Signature

PentesterLab.com / @PentesterLab
The Compact JWS Format

Separated by a dot

Header
. Payload
. Signature

PentesterLab.com / @PentesterLab
The Compact JWS Format

Separated by a dot

eyJ0eXAiOiJK eyJsb2dpbi FSfvCBAwypJ4abF6


V1QiLCJhbGci
OiJIUzI1NiJ9
. I6ImFkb
WluIn0
. jFLmR7JgZhkW674
Z8dIdAIRyt1E

eyJ = Base64('{"')

PentesterLab.com / @PentesterLab
The Compact JWS Format

Header and Payload are base64* encoded JSON


* urlsafe base64 encoding without padding

Base64({…})
. Base64({…})
. Base64(…)

The signature is also base64 encoded

PentesterLab.com / @PentesterLab
The Compact JWS Format: Encoding

Urlsafe base64 encoding without padding:

* https://tools.ietf.org/html/rfc7515#appendix-C

PentesterLab.com / @PentesterLab
The JWT Format: header

The header contains an algorithm “alg” attribute:

Base64({"alg": "HS256",
"typ": "JWS"}) . …
. …

To tell how the token was signed.


In this example HMAC with SHA256 was used

PentesterLab.com / @PentesterLab
The JWT Format: Algorithms

A lot of different algorithms are supported*:


None RS256 ES256 PS256
HS256 RS384 ES384 PS384
HS384 RS512 ES512 PS512
HS512

* https://jwt.io/ covers most

PentesterLab.com / @PentesterLab
The JWT Format: Algorithms

Scenario: one client talking to multiple services

PentesterLab.com / @PentesterLab
The JWT Format: Algorithms

HMAC: All services need to know the secret HS256

HS384

HS512

Secret

PentesterLab.com / @PentesterLab
The JWT Format: Algorithms

HMAC: if one service gets compromised HS256

HS384

HS512

Secret

Compromised secret

PentesterLab.com / @PentesterLab
The JWT Format: Algorithms

HMAC: the secret is compromised for all services HS256

HS384

HS512

Compromised secret

PentesterLab.com / @PentesterLab
The JWT Format: Asymmetric
RS256
Asymmetric: sharing the key
RS384
RS512
ES256

ES384

ES512

PS256
Private PS384

Public PS512

PentesterLab.com / @PentesterLab
The JWT Format: Asymmetric
RS256
Asymmetric: Only trusted services get the
RS384
private key RS512
ES256

ES384

ES512

PS256
Private PS384

Public PS512

PentesterLab.com / @PentesterLab
The JWT Format: Asymmetric
RS256
Asymmetric: If one service gets compromised…
RS384
RS512
ES256

ES384

ES512

PS256
Private PS384

Public PS512

PentesterLab.com / @PentesterLab
The JWT Format: Asymmetric
RS256
Asymmetric: Even in the browser!
RS384
RS512
ES256

ES384

ES512

PS256
Private PS384

Public PS512

PentesterLab.com / @PentesterLab
The JWT Format: payload

The payload may contain literally anything:


. Base64({"user":"admin",
"roles": ["adm","users"]}) . …

PentesterLab.com / @PentesterLab
The JWT Format: payload

The payload may contain registered claims:


. Base64({"user":"admin",
"exp":12…, "iat":1234.. }) . …

PentesterLab.com / @PentesterLab
The JWT Format: payload

The payload may contain registered claims:

• “iss”: issuer • “exp”: expiration time


• “sub”: subject • “nbf”: not before
• “aud”: audience • “iat”: issued at*
• “jti”: claim id
* useful for async processing

PentesterLab.com / @PentesterLab
The JWT Format: creating a token

• 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
PentesterLab.com / @PentesterLab
The JWT Format: verifying a token

• 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

PentesterLab.com / @PentesterLab
Keep in mind

• Multiple systems can issue tokens


• A token can be used by multiple systems
• All these systems can use different libraries

PentesterLab.com / @PentesterLab
Attacking JWT

When attacking JWT, your main goal is to bypass


the signature mechanism

PentesterLab.com / @PentesterLab
Not checking the signature
Not checking the signature

Some libraries provide two methods:


• decode <- don’t use this one
• verify
Or just people forgetting to re-enforce the signature
check after disabling it for some quick testing

PentesterLab.com / @PentesterLab
Not checking the signature

Exploitation:
• Get a token
• Decode and tamper with the payload
• Profit

PentesterLab.com / @PentesterLab
None algorithm
The None algorithm

Remember that slide?


None RS256 ES256 PS256

Basically, don’t sign the token


Used to be supported by default in few libraries

PentesterLab.com / @PentesterLab
The None algorithm

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
PentesterLab.com / @PentesterLab
Trivial Secret
Trivial secret

The security of the signature relies on the strength of


the secret

The secret can be cracked offline with just one valid


token

Cracking is supported by hashcat

PentesterLab.com / @PentesterLab
Trivial secret

https://github.com/aichbauer/express-rest-api-boilerplate/blob/master/api/services/auth.service.js

PentesterLab.com / @PentesterLab
Trivial secret

Exploitation:
• Get a token
• Brute force the secret until you get the same
signature
• Tamper with the payload
• Re-sign the token using the secret
PentesterLab.com / @PentesterLab
Algorithm confusion
Algorithm confusion
The sender controls the algorithm used
You can tell the receiver that the token has been
signed using HMAC instead of RSA for example
With RSA, you sign with the private key and verify
with the public key
With HMAC, you sign and verify with the same key
If you tell the receiver it’s an HMAC and it verifies it
with the public key (thinking it’s RSA?)
PentesterLab.com / @PentesterLab
Algorithm confusion

With RSA, you sign with the private key and verify
with the public key
With HMAC, you sign and verify with the same key
You tell the receiver it’s an HMAC (instead of RSA)
and it verifies the signature using HMAC with the
public key as the secret (thinking it’s RSA):
You can sign the token with the public key

PentesterLab.com / @PentesterLab
Algorithm confusion

How to get the public key:


• Public key accessible in the javascript code
• Public key available in a mobile client
• Public key just available in the documentation.

PentesterLab.com / @PentesterLab
Algorithm confusion
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

PentesterLab.com / @PentesterLab
kid injection
Kid parameter
The header can contain a kid parameter:
• Key id (https://tools.ietf.org/html/
rfc7515#section-4.1.4)
• Often used to retrieve a key from:
✴ The filesystem
✴ A Database
This is done prior to the verification of the signature
If the parameter is injectable, you can bypass the
signature
PentesterLab.com / @PentesterLab
Kid Injection

Exploitation:
• Get a signed token containing a kid parameter
• Decode the header and change the kid with a
SQL injection payload
• Tamper with the payload
• Sign the token using the return value
from the SQL injection

PentesterLab.com / @PentesterLab
CVE-2018-0114
Libraries: CVE-2018-0114

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:

PentesterLab.com / @PentesterLab
Libraries: CVE-2018-0114

• Vulnerability in Cisco Node Jose


• Node-Jose uses the embedded “jwk” key to check
the signature

Integrity bypass!

PentesterLab.com / @PentesterLab
Libraries: CVE-2018-0114 - Exploitation

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
PentesterLab.com / @PentesterLab
Conclusion
Recommendations

✓ Use strong keys and secrets

✓ Don’t store them in your source code

✓ Make sure you have key rotation built-in

PentesterLab.com / @PentesterLab
Recommendations

✓ Review the libraries you pick (KISS library)

✓ Make sure you check the signature

✓ Make sure your tokens expire

✓ Enforce the algorithm


PentesterLab.com / @PentesterLab
Conclusion

• JWT are complex and kind of insecure by design


(make sure you check https://github.com/paragonie/paseto)

• JWT libraries introduce very interesting bugs

• Make sure you test for those if you write code,


pentest or do bug bounties

PentesterLab.com / @PentesterLab
Back to the future

• If you read some of the JWS RFC, you probably


learnt about jku and x5u parameter for the headers

• People are starting to use jku (JWK URL)

PentesterLab.com / @PentesterLab
Back to the future

1 HTTP Request with JWT 2 Parsing of the JWT to extract the “jku” header

User Application 4 Parsing of the JWK

6 Response 5 Verifying the JWT signature using the JWK

3 Fetching of the JWK based on the “jku” header

Trusted
Server

PentesterLab.com / @PentesterLab
Back to the future

• Turns out filtering URL is incredibly hard (for people


doing it)

➡ Server Side Request Forgery ?


➡ Authentication bypass
➡ Chaining bugs together (Openredirect, header
injection…)

PentesterLab.com / @PentesterLab
THANKS
FOR YOUR TIME !

Any questions?

[email protected] / PentesterLab.com / @PentesterLab

You might also like