Set up a passkey
Logged in as …. Click Register passkey and complete the biometric / security key prompt your browser shows you. Watch the activity log and credentials list as it happens.
evil-cyber-hacker.com has monkey-patched
navigator.credentials.create. When you click Register, your authenticator's
real prompt will fire and you will complete a genuine biometric. The hook then drops
the real attestation and substitutes a synthetic credential built with an
attacker-controlled keypair. The page submits the substituted credential as if nothing
had happened — the server has no way to tell the difference.
💡 Open DevTools → Network. After you click Register, watch
the POST to /passkeys/api/register/finish.php carry an attestationObject
whose embedded public key you don't possess, and the beacon to
evil-cyber-hacker.com/demo/steal/ exfiltrating the JWK.
↪️ Sister demo: /passkeys/1/ shows the same outcome without any user interaction — useful counter-example to "but the user would notice if no prompt fired".
The hook installs at script-load time and waits. When the page calls
navigator.credentials.create(), the hook forwards to the real
implementation so your authenticator does its normal thing — you see Touch ID /
Windows Hello / your security key, you confirm, the device generates a real keypair
and signs a real attestation. The hook then throws that response away and constructs
its own: a fake attestationObject with fmt:"none",
an attacker-generated public key embedded in authData, and the *real*
clientDataJSON from the original ceremony (so origin and challenge bytes
still match what the server expects). The page submits this synthetic credential. The
server stores it, indistinguishable from a legitimate registration.
Pedagogical takeaway: the biometric prompt validates *you*, not the
credential the page sends to the server. Anything between
navigator.credentials.create() and your /register/finish
handler can replace the keypair without touching the prompt.
Credentials registered to your account
Live — polls /passkeys/api/credentials.php every second.
Forgeries from this attack are submitted with the page's normal label
(legitimate), since the server can't tell them apart from real ones —
the page detects the forgery via the in-memory attacker stash and flags it for you.
- Loading…
Activity log
Real-time trace of legitimate ceremony steps, hook installation and substitution events, and CSP violations.