WebGoat JWT tokens 8

WebGoat JWT tokens 8

This lessons asks us to delete Tom’s account, let’s go ahead and press “Delete” on Tom’s account

Delete action with non-valid JWT token in Burp History

The requests is a POST with a JWT token passed as parameter on the URL (GET style), this does not go through as the response feedback and output JSON parameters inform us that the token is not valid as the proposed signature does not match with the computed one

non-valid JWT token on jwt.io

By copy/pasting the JWT token we get from Burp History on to https://jwt.io a number of key-value pairs are unveiled

Of all the keys in the JWT, those that could be interesting are, in my opinion

  • kid <- key identifier

What we are looking to do is to create a valid JWT token, so the “kid” value seems something that needs tampering

WebGoat hints on this lesson tells us to try to manipulate the “kid” parameter by means of a SQL injection, so if “webgoat_key” is an identifier that is used to get an encryption key, it may be possible to force a new key thus creating a new valid token

In fact, in the https://github.com/WebGoat/WebGoat/blob/develop/webgoat-lessons/jwt/src/main/java/org/owasp/webgoat/jwt/JWTFinalEndpoint.java file from WebGoat GitHub (cheating here) there is the following SQL query

ResultSet rs = connection.createStatement().executeQuery("SELECT key FROM jwt_keys WHERE id = '" + kid + "'");
SQL query for “kid” in WebGoat source

So if the passed “kid” value is simply concatenated with the query, injection is possible by using something like the following


This form of a SQL injection allows an attacker to impose an arbitrary value, this happens because there is no “something_else” value inside the “jwt_keys” table, so the main query fails to populate the results, hence what is present in the result after the query is run is what has been put as SELECT value for the UNION query.

Moreover it is important to know that the


statement present in the query is there as the query needs a FROM <any table> statement to be valid, if it was not present the webapp would have returned a more serious java exception about the non-validity of the SQL query

Modified JWT token on jwt.io

Here a new JWT token is created by filling the details directly on jwt.io

Token not valid

But no luck, the token has not been accepted, nevertheless it is important to notice that what is not working here is the match of the passed signature with the computed signature, just like before, so the format of the JWT token is right

JWT token error — unsigned claim

In fact by trying to pass a JWT token with alg: “None” and without signature, the webapp replies with a different error

But still the lesson has not been completed so something is missing.
By going back to the source code for this lesson, just after the query, the key is returned to the calling method by means of a

return TextCodec.BASE64.decode(rs.getString(1));

The key is base64 decoded before it is used to check the JWT claims, so let’s base64 encode our key “new_key” in the SQL injection and try again

JWT token crafting with base64 encoded key in SQL injection

Here the JWT token is crafted on jwt.io by using Tom’s details, the base64 encoded key in the JWT header and the clear text key for creating the signature

Lesson completed

Paste the JWT token in Burp Repeater /delete endpoint request and the lesson is completed

This concludes WebGoat JWT token 8

I hope you liked it.

PVXs — https://twitter.com/pivixih

IT Manager / Pentester

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store