Cracking Signatures
Some examples of signature implementations (often HMAC) that can be cracked using hashcat
Last updated
Some examples of signature implementations (often HMAC) that can be cracked using hashcat
Last updated
See Hashcat for general information on how to use hashcat
, this page explains some practical usages in relation to signatures which are often used in cookies to authenticate users
JSON Web Tokens are strings of Base64-encoded data signed using some secret key. This means the client can store and read the data inside of this token, but they cannot change it without knowing the correct signing key. Only the server should be able to generate these tokens for you in a secure scenario.
The fact that we know the plaintext data, and everything is stored on the client, means that it is inherently vulnerable to brute-force attacks. We can guess many different signing keys to find when it aligns with the expected signature, which is exactly what we will use hashcat for.
There are multiple different algorithms for signing the data, which is always given by the "alg"
value in the header of the token. Two of the most common are:
HS256: HMAC (with SHA256) using a password as a secret
RS256: RSA (with SHA256) using a cryptographic private key
RSA is not easily brute-forced without a weak key, or some other special vulnerability in the generated key. Because of this, only the HMAC version is viable for brute-forcing with a password dictionary in hashcat. Luckily, this is a well-known algorithm used in many other places and is implemented in a pretty simple way for JWTs.
Let's take the following simple example:
Hashcat has a special JWT mode (-m 16500
) that automatically extracts the payload and signature to create a simple HMAC input/output which can run at very high speeds (~500 MH/s
on my laptop). It even auto-detects this mode for us, meaning we only need to provide the hash and how passwords should be generated:
Now that we know the password, we can use the debugger site to forge any new data. Simply input your original JWT, and set the secret where it says "your-256-bit-secret", which should now make a "Signature Verified" checkmark appear. At this point, you can change any data in the Payload section to forge other users' data, perform deeper injections, or anything else you can imagine.
A developer might use a less-known HMAC function to sign their JWTs, which hashcat may not be able to parse for you directly. In these cases, it is a matter of extracting the useful information manually, and then using lower-level hashcat modes to crack the secret.
Imagine an MD5 HMAC for example:
An HMAC signature works using some data as the payload, and a key which is the password. In JWT this is implemented in the following way:
The header
and payload
are both urlsafe-Base64 encoded, and joined together by a .
period. This is the raw data provided to the HMAC function, together with the secret
which is often just put in raw, but could in some cases also be Base64 encoded.
Inside the JWT, the header and payload are already put in the right format, being the part before the second period (eyJhbGciOiAiTUQ1X0hNQUMifQ.eyJ1c2VybmFtZSI6ICJqMHIxYW4ifQ
). Then the last part is the signature, meaning the result of the HMAC function. It is Base64 encoded while hashcat expects it as hex like a regular hash. This means we just have to convert this last part to hex to get a string it understands:
Finally, we have all the parts we need, and can write out the hash in the way hashcat expects:
Then we simply use the raw HMAC-MD5 (-m 50
) mode in hashcat to crack the secret like before:
When we have a secret key, we can try to forge new data with it by implementing the algorithm in a simple script. There are many easy libraries that do most of the work already, so it is simply a matter of understanding the required steps. For JWTs:
First the header
and payload
JSON data is encoded into a string, and are separately Base64 encoded, which are then joined together by .
period
An HMAC signature is made by putting the data from step 1 in the data argument, and the secret in the key argument, selecting the correct hash function (MD5, SHA256, etc.)
This resulting signature is Base64 encoded and appended together with a .
in front to the header and payload value from step 1, which becomes the resulting JWT
Putting this into code, it looks something like this:
In this way, you can generate any type of signature for your JWT after cracking it, regardless of if the online debugger has it.
See the Brute-Force section for Flask Session Cookies. An all-in-one tool can crack it for you, or use hashcat to do so much faster
cookie-session
from Express (session.sig=
)See this writeup for an example using express-session
instead, which looks like:
s:lkdh18zhtZX-vve8gThP8_NEoTkr-OsT.T4zrDEc9N2RbIViBsst5ZlWo1DfWL
(recognizable by the s:
prefix)
The cookie-session
library from the NodeJS Express framework is a common way of managing sessions. Similarly to JWTs and Flask Sessions, it also stores data on the client together with a signature that prevents it from being changed using HMAC. We simply have to adapt the HMAC function to use a different hash and alter the input format.
The information is split into the data as session=
and the signature as session.sig=
. For example:
From a test here we can find that the cookies
library uses another library named Keygrip with the default configuration to generate this signature from data and a key. The implementation is simply another HMAC this time using SHA1, and the data is the cookie data, meaning "session=eyJ1c2VybmFtZSI6ICJqMHIxYW4ifQ"
with whatever the payload is. Lastly, the signature is encoded in Base64 again, we need to decode and encode it to hex for hashcat to understand:
Knowing this we can easily create a hashcat hash in this format (note that session=
may be different depending on your cookie name):
Finally, we can use the HMAC-SHA1 mode (-m 150
) to crack it:
Now that the secret key is found, we can forge any cookie payload with a valid signature: