Skip to content

[SECURITY - CRITICAL] Relay architecture enables trivial traffic decryption via malicious relay + server compromise (CVSS 9.1/7.1) #336

Closed as not planned

Description

@nsp

I've discovered a critical vulnerability in the relay architecture that enables two distinct attack vectors, both stemming from the same root cause: server-side storage of decryption keys. The good news is that one coordinated fix will completely protect against both.

CVSS Scores:

  • Vector 1 (Malicious Relay): 9.1 Critical - CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:N
  • Vector 2 (Server Compromise): 7.1 High - CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:N

Attack Vector 1 - Malicious Relay Deployment (Critical)

Anyone can deploy a malicious relay server and decrypt all traffic from users who connect to it. This requires no sophisticated capabilities:

  1. Deploy the existing relay server code with logging enabled
  2. Advertise the relay URL (as "fast/free/premium" service, target specific organizations, etc.)
  3. Users connect unknowingly
  4. All traffic is decrypted and logged server-side

Attack Vector 2 - Legitimate Relay Compromise (High)

If any legitimate relay server is compromised, the attacker gains access to stored decryption keys and can decrypt all traffic passing through that server.

Root Cause

The current implementation stores decryption keys on the relay server, fundamentally violating the documented zero-knowledge architecture. This means:

  • Users must completely trust their relay operator
  • No cryptographic guarantees protect against malicious or compromised relays
  • The "zero knowledge" security claim is false

Current Behavior

  • QR codes transmit secrets to the server
  • Relay servers store decryption keys for all connected machines
  • Server has full ability to decrypt all relayed traffic

Proposed Solution

Implement true end-to-end encryption:

  • QR codes contain secrets in URL fragments (after #) so they're never transmitted to server
  • Server stores only public keys for each machine
  • CLI receives authentication challenge to prove key ownership
  • Server acts as truly untrusted relay with no decryption capability

Impact

This requires breaking changes across all three repositories and coordinated updates of clients, CLIs, and relay servers.

Disclosure Request

Given the critical nature of Vector 1 (trivial exploitation, no privileges required) and the immediate risk to current users, I recommend:

  1. Private discussion of technical details
  2. Expedited patch development
  3. Coordinated security advisory
  4. User notification about relay trust requirements until patched

I'm very busy for the next few weeks, but I can assist to the best of my availability.

Activity

  1. ex3ndr commented on Jan 11, 2026

    @ex3ndr
    Contributor

    this is bizarre: we are end to end encrypted and even decrypting tls won't yield much useful data

  2. slhck commented on Jan 12, 2026

    @slhck

    This appears to be an AI-written report that is not aligned with what's actually going on in the code. @nsp did you actually look at the code?

    The QR code contains a public key, no secrets:

    data={'happy:///account?' + encodeBase64(keypair.publicKey, 'base64url')}

    The secret is encrypted before being sent, and the server stores only encrypted keys:

    // Use public key for encryption (encrypt TO ourselves)
    const encrypted = encryptBox(key, this.contentKeyPair.publicKey);
    const result = new Uint8Array(encrypted.length + 1);
    result[0] = 0; // Version byte
    result.set(encrypted, 1);
    return result;

    Decryption requires a private keys:

    const decrypted = decryptBox(encryptedKey.slice(1), this.contentKeyPair.privateKey);

    As far as I can tell, private keys never leave the device.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions