Skip to content

In the logic for registering and deleting Invite Keys in keys-server, the logic to verify whether the key corresponding to iss is actually registered to the pkh is missing. #201

@Burnnnnny

Description

@Burnnnnny

Description

vulnerability type: [CWE-639: Authorization Bypass Through User-Controlled Key]

Repository(reown-com/keys-server): cde237b

The /invite handlers accept JWTs signed by any iss without checking that iss is one of the identity keys registered to the specified pkh via /identity. The request body carries only idAuth; invite/register.rs::handler() (src/handlers/invite/register.rs#L36C1-L58C9) runs Jwt::verify(), then uses claims.pkh to find the account and writes claims.sub as the invite key, while invite/unregister.rs::handler() (src/handlers/invite/unregister.rs#L62C3-L86C13) deletes by claims.pkh. auth/jwt/mod.rs::JwtVerifierByIssuer::verify() (src/auth/jwt/mod.rs#L133C2-L146C5,#L67C1-L93C6) derives a public key from iss and checks only the signature, and JwtClaims::is_valid() validates format but not ownership, so no issuer-account binding occurs. Because accounts are created by /identity, any existing pkh can be targeted: an attacker signs idAuth with their own did:key in iss, sets pkh to the victim, and sets sub to an attacker-controlled invite key; the server accepts it and overwrites or deletes the victim's invite key. Existing established sessions remain unaffected, but subsequent invite resolution for that account uses the attacker key or fails if deleted.

Impact:

The invite channel for any known pkh can be seized or disabled with an attacker-signed JWT, so new chat invitations are either intercepted or blocked until the key is restored. This is a direct impact on invitation delivery for existing accounts without any victim action. These two concrete outcomes drive a High severity rating.

Impact#1: Victim impersonation and eavesdropping. An attacker overwrites the victim's invite key via /invite using a JWT signed with their did:key, so subsequent chat invitations to the victim are delivered to the attacker. The attacker reads and decrypts those invitations and completes ECDH to appear as the victim in new conversations.

Impact#2: Chat invitation denial of service. An attacker sends DELETE /invite with iss set to their key and pkh set to the victim, removing the invite key without authorization. New invitations cannot be delivered until the key is re-registered, leaving the victim unreachable for new chats.

Recommendation:

Enforce iss-pkh binding so only identity keys already registered to a pkh can change that account's invite key. In invite/register.rs and invite/unregister.rs, first normalize claims.pkh the same way /identity does (e.g., CAIP-10 casing rules, lowercase for eip155 variants). Query the account document from persistent storage and read the registered identity keys (identities.identity_key) for that pkh. Reject the request if claims.iss is not one of those stored keys. Only after this check should upsert_invite_key or remove_invite_key execute, preserving the /identity trust model that binds wallet-signed Cacao to the account's identity keys.

Reproduction Steps

Below are the PoC flows for both impacts, using a victim account already registered via /identity and an attacker-controlled did:key. Impact#1 shows overwrite-driven impersonation and invitation interception, and Impact#2 shows unauthorized deletion causing invitation failure. Each scenario assumes the attacker only knows the victim's CAIP-10 account and signs JWTs with their own key.

PoC Environment:

PoC.zip

Git repository(reown-com/keys-server): cde237b

※ The PoC test environment is specified in the README.md included in the provided attachment.

Prerequisites:

  1. The victim's account must already exist in the database with a registered Identity Key at the Identity endpoint, ensuring the pkh account exists in the database. If it is not registered, the server returns a NotFound error, and the request fails.
  2. The attacker knows the target victim's CAIP-10-format account (wallet address).
  3. The attacker generates and holds an arbitrary Ed25519 key pair (attacker's key)

Impact#1 Client-Level Verification (Android Application):

Victim Impersonation and Eavesdropping Description: Overwriting the victim's Invite Key with the attacker's key to intercept and decrypt invitation messages sent to the victim.

Test Environment: Android Studio Device Manager (Pixel 6), a self-implemented chat app based on the Reown Kotlin SDK, referring to https://specs.walletconnect.com/2.0/specs/clients/chat/. The test app PoC is at https://github.com/Burnnnnny/reown-kotlin-chat-app, which is currently set to private. If you wish, provide an email, and I will invite you to the repository.

Step 1: Malicious Key Registration (Key Overwrite)

  1. The attacker creates a POST /invite request. In the JWT, the iss is set to the attacker's key, and the pkh is set to the victim's wallet address.
  2. The attacker sends the request containing a public key they control (one for which they possess the private key) in the JWT sub claim.
  3. Result: The server overwrites the victim's Invite Key with the attacker's key without proper verification.

Step 2: Message Interception & Impersonation (Eavesdropping & Impersonation)

  1. The attacker connects to the server via WebSocket and subscribes to the topic corresponding to the previously registered attacker's key.
  2. A third party sends a chat invitation to the victim's wallet address.
  3. Since the key registered to the victim's account belongs to the attacker, the invitation message is sent to the attacker's WebSocket.
  4. The attacker decrypts and reads the content of the encrypted invitation message using their private key.
  5. Result: The attacker can perform an ECDH key exchange with the sender to impersonate the victim and initiate a chat.

Note: Existing sessions established prior to the attack are unaffected and cannot be eavesdropped on. However, all subsequent new invitations or re-invitations will either be routed to the attacker or fail.

Impact#2 Server-Level Verification (API Direct Interaction)

Chat Invitation DoS Description: Exploiting the lack of authentication logic to delete or invalidate the victim's Invite Key, rendering the service unusable.

Step 1: Malicious JWT Generation

  1. The attacker generates a JWT with their own key as iss and the victim's account as pkh.

Step 2: Invite Key Deletion

  1. Call DELETE /invite endpoint with the generated JWT.
  2. Result: Server deletes the victim's Invite Key without an authorization check.

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