Hacking JWT
(JSON Web Token)

Q: What can go wrong with implementing 300+ pages flawed, heavy with crypto JWT standard?


A: Everything :-P



(c) securitum.pl, 

sekurak.pl/pwning18/ (this presentation)

About me

Trainer & Pentester (10+ years of practical experience)

Researcher (IoT)

Cisco RVS 4000 (unauth root)

Cisco SA 500 (unauth root)

Cisco SRP platform (unauth root)

Asmax Router (unauth root)

HP LaserJet Pro (unauth admin)

TP-Link (unauth root) x2

GANZ CCTV Camera x2 (unauth root)

Bosch CCTV Camera (unauth admin)

sekurak.pl founder

About Securitum.pl

Pentesting & Trainings

~250 pentests a year

~100 trainings a year (PL & EU)

Accredited Training Center (EC-Council) -
Certified Ethical Hacker accredited training


JSON Web Token, https://jwt.io/introduction/

JSON Web Token (JWT) is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object.


This information can be verified and trusted because it is digitally signed. JWTs can be signed using a secret (with the HMAC algorithm) or a public/private key pair using RSA.


Where JWT is used?

It is mainly used for:


Our main goal

Forge any token
(ie. with valid signature)


HttpArchive pulls down a list of URLs from the Alexa Top 1,000,000 web sites and then kicks off a bunch of WebPageTest machines to navigate to those URLs and record all of the requests made when loading the sites.

Many of the sites being tested and recorded, download Javascript and then make calls to 3rd party APIs.  The API keys used to call those APIs are therefore recorded in HttpArchive.


To search the (fresh!) dump  you can use Google Bigquery.

The fresh dumps are already there.

OK let's get back to forging any JWTs

Weak secret

Session..........: hashcat
Status...........: Running
Hash.Type........: JWT (JSON Web Token)
Hash.Target......: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMj...IDYRDg
Time.Started.....: Fri Mar 30 16:01:35 2018 (1 min, 30 secs)
Time.Estimated...: Fri Mar 30 16:12:55 2018 (9 mins, 50 secs)
Guess.Mask.......: ?1?2?2?2?2?2?2 [7]
Guess.Charset....: -1 ?l?d?u, -2 ?l?d, -3 ?l?d*!$@_, -4 Undefined 
Guess.Queue......: 7/15 (46.67%)
Speed.Dev.#1.....:   198.0 MH/s (9.68ms) @ Accel:32 Loops:8 Thr:512 Vec:1
Recovered........: 0/1 (0.00%) Digests, 0/1 (0.00%) Salts
Progress.........: 17964072960/134960504832 (13.31%)
Rejected.........: 0/17964072960 (0.00%)
Restore.Point....: 0/1679616 (0.00%)
Candidates.#1....: U7veran -> a2vbj14
HWMon.Dev.#1.....: Temp: 75c Fan: 48% Util: 98% Core:1873MHz Mem:3802MHz Bus:16

... After 3-10 coffees: 


meet the perfect sig algorithm: none 

The none algorithm is a curious addition to JWT. It is intended to be used for situations where the integrity of the token has already been verified. Interestingly enough, it is one of only two algorithms that are mandatory to implement (the other being HS256).

Unfortunately, some libraries treated tokens signed with the none algorithm as a valid token with a verified signature. The result? Anyone can create their own "signed" tokens with whatever payload they want, allowing arbitrary account access on some systems.

It's not a bug, it's a feature!

RFC 7519

An Unsecured JWT is a JWS using the "alg" Header Parameter value "none" and with the empty string for its JWS Signature value, as defined in the JWA specification [JWA]; it is an Unsecured JWS with the JWT Claims Set as its JWS Payload.

Of the signature and MAC algorithms specified in JSON Web Algorithms [JWA], only HMAC SHA-256 ("HS256") and "none" MUST be implemented by conforming JWT implementations.


meet the perfect sig algorithm: none 


Publish Date : 2018-06-26 
Last Update Date : 2018-08-30


"This attack can be exploitable when an attacker crafts a JWT token with a valid header using 'none' as algorithm and a body to requests it be validated."



sometimes even none is not necessrary



an input validation vulnerability in JWTDecoder.decode that can result in a JWT that is decoded and thus implicitly validated even if it lacks a valid signature.


Simple version: just remove a signature from a token, and it
is validated!

String token = JWT.getEncoder().encode(inputJwt, HMACSigner.newSHA256Signer(SECRET_KEY));

String tokenNoSignature = token.substring(0, token.lastIndexOf('.') + 1);

try {
            JWT outputJwt = JWT.getDecoder().decode(tokenNoSignature, HMACVerifier.newVerifier(SECRET_KEY));

            fail("Ticket claims that HS256 signature is applied ({\"alg\":\"HS256\",\"typ\":\"JWT\"}) but the signature was not checked.");
catch (JWTException e) {
            // should be thrown

Re-sign Vuln

The vulnerability is due to node-jose following the JSON Web Signature (JWS) standard for JSON Web Tokens (JWTs). This standard specifies that a JSON Web Key (JWK) representing a public key can be embedded within the header of a JWS. This public key is then trusted for verification. An attacker could exploit this by forging valid JWS objects by removing the original signature, adding a new public key to the header, and then signing the object using the (attacker-owned) private key associated with the public key embedded in that JWS header.

CVE-2018-0114: Node-jose Library JSON Web Tokens Re-sign Vulnerability


TLDR: attacker can provide his signature verfication key in the JWT

Re-sign Vuln

# sekurak: key from arg
my $key = defined $args{keypass} ? [$args{key}, $args{keypass}] : $args{key};
my $kid = exists $header->{kid} ? $header->{kid} : $unprotected_header->{kid};
if (!defined $key && defined $kid && $args{kid_keys}) {
    my $k = _kid_lookup($kid, $args{kid_keys}, $alg);
    # sekurak: key from kid (which is in JWT header)
    $key = $k if defined $k;
# if no key given, try to use 'jwk' value from header
$key = $header->{jwk} if !$key && $header->{jwk};

Who can set signature alg?

1. API uses RS256

2. verification_key is set to pubkey (server side)

3. we intercept any JWT token (RS256 signed)

4. we obtain public key (should be public, right?)

5. we change payload, set alg to HS256 and use RSA public key as a symmetric HS256 key

6. server receives our JWT, checks alg from JWT header (HS256) and uses verification_key (see point 2.)

7. JWT is validated (!!!) - in HS256 same key is used for signing / verification





Since "algorithm" isn't enforced in jwt.decode() in jwt-simple 0.3.0 and earlier, a malicious user could choose what algorithm is sent sent to the server.


If the server is expecting RSA but is sent HMAC-SHA with RSA's public key, the server will think the public key is actually an HMAC private key. This could be used to forge any data an attacker wants.

Who understands JWT? :P


Critical Security Fix Required: You disclose the correct signature with each SignatureVerificationException... #61


Verify() or Decode()?

I have seen a issue in the framework to get the payload of an JWT. To get a payload of an JWT without validation we don’t need a key/secret.

So in the file jwt/src/JWT/JwtDecoder.cs we missed a overload, for the method Decode that only need a token.

TLDR: just give me the payload!!!

Verify() or Decode()?

Sometimes we have 2 separate functions:


Which one will be used by a developer? 

hint: the first on the list ;-)


We have many, many implementations

As always, it's good

...so we have many vulns :)











Thank you for listening

REST API Security workshop (PL/EN)