Hookery — Faking a Login by Reusing a Public Key

Challenge Overview

Hookery is a webhook service that signs outgoing webhooks with a private key and publishes the matching public key so customers can verify authenticity. The bug: that same key pair was also used to sign login session JWTs on the dashboard. Since the public key is intentionally public, anyone can grab it — and that turns out to be enough to fake a login.


Reconnaissance

The site publishes its public key at a well-known path, on purpose, for webhook verification:

GET /.well-known/webhook-key.pem

After logging in normally and inspecting the session cookie (hk_session), the token decoded to:

Header:

{"typ":"JWT","alg":"RS256","kid":"wh_2026a"}

Payload:

{
  "sub": "tester@test.com",
  "name": "test2",
  "role": "customer",
  "company": "tester",
  "iat": ...,
  "exp": ...
}

The dashboard also exposed a /app/platform link — a more privileged “Platform console” area likely gated by that role field.


Vulnerability — JWT Algorithm Confusion (RS256 → HS256)

Normally, RS256 tokens can only be signed by someone who holds the private key — the public key alone is not enough to forge one. However, JWTs also support HS256, which uses a single shared secret (not a key pair). If the server does not strictly enforce which algorithm a token must use, an attacker can:

  1. Take the token header and change alg from RS256 to HS256
  2. Sign the new token using the public key as the HS256 shared secret (the public key is freely downloadable)
  3. Submit that forged token to the server

If the server’s verification code says “verify this token with the key I have” without also enforcing “and it must be RS256,” it accepts the forged token — because technically, the HS256 signature does check out against the public key material.


Exploitation

Step 1 — Download the public key:

curl https://<challenge-host>/.well-known/webhook-key.pem -o pubkey.pem

Step 2 — Decode the real session token to confirm the algorithm and payload structure.

Step 3 — Forge a new token by hand with:

  • alg changed from RS256 to HS256
  • role changed from customer to admin
  • Signed using the public key as the HS256 secret

Most modern JWT libraries refuse to use a public key as an HS256 secret — that refusal is exactly the safety check this attack bypasses, which is why the token must be built manually.

Step 4 — Submit the forged token:

curl https://<challenge-host>/app/platform \
  -H "Cookie: hk_session=<forged_token>"

role=admin granted access and the flag was in the response.


Automation Script

hookery_forge.py grabs the public key, builds a forged token with role set to several admin-like values, and tries each against /app/platform until it succeeds:

python3 hookery_forge.py https://<challenge-host>/ -v

Root Cause

Two different jobs — webhook verification and login session signing — were sharing one key pair. That is fine for RS256-only situations. The moment login verification allows any algorithm, a key that is supposed to be public becomes a working password for HS256 forgery.


Remediation

  • Never reuse the same key for two different purposes, especially when one job relies on the key being public and the other relies on it being secret.
  • Pin the expected algorithm in your JWT verification code — explicitly accept only RS256 and reject any token claiming a different algorithm regardless of whether the signature validates.

Flag

WEBVERSE{**************REDACTED**************}



Enjoy Reading This Article?

Here are some more articles you might like to read next:

  • Google Gemini updates: Flash 1.5, Gemma 2 and Project Astra
  • Displaying External Posts on Your al-folio Blog
  • Statica — Bypassing AI Assistant Secret Masking
  • Ashwick
  • Snobble AI