Learn how you can use some JavaScript/Node.js black magic to crack JWT tokens and impersonate other users or escalate privileges. Just add a pinch of ZeroMQ, a dose of parallel computing, a 4 leaf clover, mix everything applying some brute force and you'll get a powerful JWT cracking potion!
Behind the Scenes From the Manager's Chair: Decoding the Secrets of Successfu...
Cracking JWT tokens: a tale of magic, Node.js and parallel computing - WebRebels Oslo, 5 June 2018
1. Cracking JWT tokensCracking JWT tokens
a tale ofa tale of magicmagic,, Node.jsNode.js andand parallel computingparallel computing
Oslo - 5 JUN 2018
Luciano Mammino (Luciano Mammino ( ))@loige@loige
loige.link/jwt-crack-oslo 1
3. Luciano... who?Luciano... who?
Visit my castles:
- (@loige)
- (lmammino)
-
- (loige.co)
Twitter
GitHub
Linkedin
Blog
Solution Architect at
with @mariocasciaro
with @andreaman87
with @ Podgeypoos79 3
4. Based on prior workBased on prior work
Chapters 10 & 11 in (book)
2-parts article on RisingStack:
" "
Node.js design patterns
ZeroMQ & Node.js Tutorial - Cracking JWT Tokens
github.com/lmammino/jwt-cracker
github.com/lmammino/distributed-jwt-cracker
4
6. — RFC 7519— RFC 7519
is a compact, URL-safe means of representing claims to be
transferred between two parties. The claims in a JWT are
encoded as a JSON object that is used as the payload of a JSON
Web Signature (JWS) structure or as the plaintext of a JSON
Web Encryption (JWE) structure, enabling the claims to be
digitally signed or integrity protected with a Message
Authentication Code (MAC) and/or encrypted.
JSON Web Token (JWT)JSON Web Token (JWT)
6
14. URL Safe...URL Safe...
It's a string that can be safely used as part of a URLIt's a string that can be safely used as part of a URL
(it doesn't contain URL separators like "(it doesn't contain URL separators like "==", "", "//", "", "##" or "" or "??")")
unicorntube.pl/?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
11
15. Stateless?Stateless?
Token validity can be verified without having to interrogate aToken validity can be verified without having to interrogate a
third-party servicethird-party service
(Sometimes also defined as "self-contained")
12
17. some information to transfersome information to transfer
identityidentity (login session)(login session)
authorisation to perform actionsauthorisation to perform actions (api key)(api key)
ownershipownership (a ticket belongs to somebody)(a ticket belongs to somebody)
14
18. also...also...
validity constraintsvalidity constraints
token time constraintstoken time constraints (dont' use before/after)(dont' use before/after)
audienceaudience (a ticket only for a specific concert)(a ticket only for a specific concert)
issuer identityissuer identity (a ticket issued by a specific reseller)(a ticket issued by a specific reseller)
15
35. HEADERHEADER::
The decoded info is JSON!The decoded info is JSON!
PAYLOADPAYLOAD::
{"alg":"HS256","typ":"JWT"}{"alg":"HS256","typ":"JWT"}
{"message":"hello people"}{"message":"hello people"}
22
36. HEADERHEADER::
{"alg":"HS256","typ":"JWT"}{"alg":"HS256","typ":"JWT"}
alg:alg: the kind of algorithm usedthe kind of algorithm used
"HS256""HS256" HMACSHA256 SignatureHMACSHA256 Signature (secret based hashing)(secret based hashing)
""RS256RS256" RSASHA256 Signature" RSASHA256 Signature (public/private key hashing)(public/private key hashing)
""nonenone" NO SIGNATURE!" NO SIGNATURE! (This is " (This is " ")")infamousinfamous
23
38. PAYLOADPAYLOAD::
"registered" (or standard) claims:"registered" (or standard) claims:
iss: issuer ID ("auth0")
sub: subject ID ("johndoe@gmail.com")
aud: audience ID ("https://someapp.com")
exp: expiration time ("1510047437793")
nbf: not before ("1510046471284")
iat: issue time ("1510045471284")
25
43. If a system knows theIf a system knows the secretsecret
It can verify the authenticityIt can verify the authenticity
of the tokenof the token
With HS256With HS256
30
44. Let's create a token from scratchLet's create a token from scratch
runkit.com/lmammino/create-jwt-token
31
62.
Browser
1. POST /login
3. JWT Token
{"sub":"luciano"}
user:"luciano"
pass:"mariobros"
Server
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJz
dWIiOiJsdWNpYW5vIn0.V92iQaqMrBUhkgEAyRaCY
7pezgHKls85DY8wHnFrk4
Create Token for "luciano"
Add signature
2. create
JWT
37
63.
Browser
1. POST /login
3. JWT Token
{"sub":"luciano"}
user:"luciano"
pass:"mariobros"
Server
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJz
dWIiOiJsdWNpYW5vIn0.V92iQaqMrBUhkgEAyRaCY
7pezgHKls85DY8wHnFrk4
4. GET /profile
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJz
dWIiOiJsdWNpYW5vIn0.V92iQaqMrBUhkgEAyRaCY
7pezgHKls85DY8wHnFrk4
Create Token for "luciano"
Add signature
2. create
JWT
37
64.
Browser
1. POST /login
3. JWT Token
{"sub":"luciano"}
user:"luciano"
pass:"mariobros"
Server
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJz
dWIiOiJsdWNpYW5vIn0.V92iQaqMrBUhkgEAyRaCY
7pezgHKls85DY8wHnFrk4
4. GET /profile
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJz
dWIiOiJsdWNpYW5vIn0.V92iQaqMrBUhkgEAyRaCY
7pezgHKls85DY8wHnFrk4
Token says this is "luciano"
Signature looks OK
5. verify
Create Token for "luciano"
Add signature
2. create
JWT
37
65.
Browser
1. POST /login
3. JWT Token
{"sub":"luciano"}
user:"luciano"
pass:"mariobros"
6. (page)
<h1>hello luciano</h1>
Server
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJz
dWIiOiJsdWNpYW5vIn0.V92iQaqMrBUhkgEAyRaCY
7pezgHKls85DY8wHnFrk4
4. GET /profile
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJz
dWIiOiJsdWNpYW5vIn0.V92iQaqMrBUhkgEAyRaCY
7pezgHKls85DY8wHnFrk4
Token says this is "luciano"
Signature looks OK
5. verify
Create Token for "luciano"
Add signature
2. create
JWT
37
66.
Browser
1. POST /login
3. JWT Token
{"sub":"luciano"}
user:"luciano"
pass:"mariobros"
6. (page)
<h1>hello luciano</h1>
Server
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJz
dWIiOiJsdWNpYW5vIn0.V92iQaqMrBUhkgEAyRaCY
7pezgHKls85DY8wHnFrk4
4. GET /profile
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJz
dWIiOiJsdWNpYW5vIn0.V92iQaqMrBUhkgEAyRaCY
7pezgHKls85DY8wHnFrk4
Token says this is "luciano"
Signature looks OK
5. verify
Create Token for "luciano"
Add signature
2. create
JWT
Note: Only the server
knows the secret
37
67. Cookie/sessionCookie/session
Needs a database to store the
session data
The database is queried for every
request to fetch the session
A session is identified only by a
randomly generated string
(session ID)
No data attached
Sessions can be invalidated at any
moment
JWTJWT
Doesn't need a session database
The session data is embedded in
the token
For every request the token
signature is verified
Attached metadata is readable
Sessions can't be invalidated, but
tokens might have an expiry flag
VSVS
38
68. Another great JWT use caseAnother great JWT use case
Creating Secure Password Reset LinksCreating Secure Password Reset Links
loige.link/jwt-pwd-reset
39
69. JWT LOOKS GREAT!JWT LOOKS GREAT!
But there are pitfalls...But there are pitfalls...
40
71. Data is public!Data is public!
If you have a token,If you have a token,
you can easily read the claims!you can easily read the claims!
41
72. Data is public!Data is public!
If you have a token,If you have a token,
you can easily read the claims!you can easily read the claims!
You only have to Base64Url-decode the
token header and payload
and you have a readable JSON 41
73. There's no token database...There's no token database...
...if I can forge a token...if I can forge a token
nobody will know it's notnobody will know it's not
authentic!authentic!
42
80. The idea...The idea...
try to "guess" the secret and validate the token against ittry to "guess" the secret and validate the token against it
Take a valid JWT tokenTake a valid JWT token
47
81. The idea...The idea...
if the token is validated, then you found theif the token is validated, then you found the secretsecret!!
try to "guess" the secret and validate the token against ittry to "guess" the secret and validate the token against it
Take a valid JWT tokenTake a valid JWT token
47
82. The idea...The idea...
YOU CAN NOWYOU CAN NOW CREATE AND SIGNCREATE AND SIGN
ANY JWT TOKENANY JWT TOKEN FOR THISFOR THIS
APPLICATIONAPPLICATION!!
if the token is validated, then you found theif the token is validated, then you found the secretsecret!!
try to "guess" the secret and validate the token against ittry to "guess" the secret and validate the token against it
Take a valid JWT tokenTake a valid JWT token
47
87. ZeroMQZeroMQ
an open source embeddablean open source embeddable networkingnetworking
librarylibrary and aand a concurrency frameworkconcurrency framework
49
88. The brute force problemThe brute force problem
"virtually infinite" solutions space"virtually infinite" solutions space
all the strings (of any length) that can be generated within a given alphabet
(empty string), a, b, c, 1, aa, ab, ac, a1, ba, bb, bc, b1, ca, cb, cc, c1, 1a, 1b, 1c, 11, aaa,
aab, aac, aa1, aba, ...
50
89. bijection (int) ⇒(string)bijection (int) ⇒(string)
if we sort all the possible strings over an alphabet
Alphabet = [a,b]
0 ⟶ (empty string)
1 ⟶ a
2 ⟶ b
3 ⟶ aa
4 ⟶ ab
5 ⟶ ba
6 ⟶ bb
7 ⟶ aaa
8 ⟶ aab
9 ⟶ aba
10 ⟶ abb
11 ⟶ baa
12 ⟶ bab
13 ⟶ bba
14 ⟶ bbb
15 ⟶ aaaa
16 ⟶ aaab
17 ⟶ aaba
18 ⟶ aabb
...
51
92. Server stateServer state
the solution space can be sliced intothe solution space can be sliced into
chunkschunks of fixed length (batch size)of fixed length (batch size)
54
93. Server stateServer state
the solution space can be sliced intothe solution space can be sliced into
chunkschunks of fixed length (batch size)of fixed length (batch size)
0 3 6 9 ...
54
94. Server stateServer state
the solution space can be sliced intothe solution space can be sliced into
chunkschunks of fixed length (batch size)of fixed length (batch size)
0
batch 1
3 6 9 ...
54
95. Server stateServer state
the solution space can be sliced intothe solution space can be sliced into
chunkschunks of fixed length (batch size)of fixed length (batch size)
0
batch 1 batch 2
3 6 9 ...
54
96. Server stateServer state
the solution space can be sliced intothe solution space can be sliced into
chunkschunks of fixed length (batch size)of fixed length (batch size)
0
batch 1 batch 2 batch 3
3 6 9 ...
54
97. Server stateServer state
the solution space can be sliced intothe solution space can be sliced into
chunkschunks of fixed length (batch size)of fixed length (batch size)
0
...batch 1 batch 2 batch 3
3 6 9 ...
54
112. How a chunk is processedHow a chunk is processed
Given chunk [3,6] over alphabet "ab"
[3,6] ⇒
3 ⟶ aa
4 ⟶ ab
5 ⟶ ba
6 ⟶ bb
⇠check if one of the
strings is the secret
that validates the
current token
63
113. const jwt = require('jsonwebtoken')
const generator = require('indexed-string-variation').generator;
const variations = generator('someAlphabet')
const processChunk = (token, from, to) => {
let secret
for (let i = from; i < to; i++) {
try {
secret = variations(i)
jwt.verify(token, pwd, {
ignoreExpiration: true,
ignoreNotBefore: true
})
// finished, password found
return ({found: secret})
} catch (err) {} // password not found, keep looping
}
// finished, password not found
return null
}
Client
64
119. Use a strong (≃long)Use a strong (≃long) secretsecret and keep it SAFE! and keep it SAFE!
Or, even better
Use RS256 (RSA public/private key pair) signatureUse RS256 (RSA public/private key pair) signature
Use it wisely!Use it wisely!
70
120. But, what if I createBut, what if I create
onlyonly
short lived tokensshort lived tokens......
71
121. JWT is STATELESS!JWT is STATELESS!
the expiry time is contained in the token...
if you can edit tokens, you can extend the expiry time as needed!
72
122. Should I be worried aboutShould I be worried about
brute forcebrute force??
73
123. Not reallyNot really
... As long as you know the basic rules... As long as you know the basic rules
(and the priorities) to defend yourself(and the priorities) to defend yourself
74
124. TLDR;TLDR;
JWT is aJWT is a cool & stateless™cool & stateless™ way toway to
transfer claims!transfer claims!
Choose the right Algorithm
With HS256, choose a good secret and keep it safe
Don't disclose sensitive information in the payload
Don't be too worried about brute force, but understand how it works!
75