← All casesConfidential · Anonymized
Case
#020
Web3 · Wallet · Severity: High

Wallet Session Fixation

The wallet-connect handshake accepted a session token issued before the connect flow completed. A narrow timing window allowed a session token to be substituted, resulting in cross-origin session fixation across dApps sharing the wallet.

Industry
Web3 · Wallet
Scope
Wallet · Session · Application
Timeline
R·Q4 → V·Q4 → R·Q1
Closed
Q1
01

Impact

Session hijack
Signer authorization bound to attacker-controlled session
Cross-dApp
Token confusion across dApps sharing the wallet
Attack window
≤ 800 ms during the connect handshake
Impact vectors
  • Session hijack under connect flow
  • Cross-dApp token confusion
02

Reproduction

Steps below reproduce the finding against a staging replica. Where the reproduction affected a shared component, coordination was arranged before execution.

  1. 01Pre-issue a session token from the target dApp origin.
  2. 02Trigger a wallet connect flow from a controlled origin.
  3. 03Substitute the session token during the handshake acknowledgement.
  4. 04Confirm subsequent signer authorizations bind to the substituted session.
03

Vulnerability

Handshake accepting pre-issued tokentypescript
async function onConnectAck(msg: ConnectAck) {
  const session = await store.get(msg.sessionId);
  if (!session) return reject('no session');
  session.status = 'connected';
  await store.put(session);
}
04

Mitigation

Client-side mitigation shipped and validated during the engagement. Reference patch below.

Origin-bound challenge nonce (post-fix)typescript
async function onConnectAck(msg: ConnectAck, origin: string) {
  const session = await store.get(msg.sessionId);
  if (!session || session.origin !== origin) return reject('origin mismatch');
  if (!verifyChallenge(msg.challenge, session.nonce)) return reject('challenge failed');
  session.status = 'connected';
  session.nonce  = rotateNonce();     // one-shot per handshake
  await store.put(session);
}
05

Retest & Verify

  • Handshake rejects any token whose recorded origin differs from the ACK origin.
  • Challenge nonce is single-use and rotates on completion.
  • Cross-dApp reproduction attempts rejected in 100/100 trials.
06

Deliverables

  • Technical Report
  • Session model review
  • Origin-bound challenge implementation
  • Retesting report
07

Sign-off

Handled by
Epulyx
Reviewed by
Nolan
Closed
Case closed after fixation path verified closed.
Cookies

We use a minimal set of cookies to keep this site functional. Analytics cookies only with your consent. Cookie Policy.