Skip to content

[security] Voice peer ID spoofing — no signer verification on voice messages #172

@intendednull

Description

@intendednull

Problem

crates/client/src/listeners.rs:329-343 processes VoiceJoin messages without verifying that the claimed peer_id matches the cryptographic signer of the wire message:

crate::ops::WireMessage::VoiceJoin {
    channel_id,
    peer_id,      // ← self-declared, never verified against `signer`
} => {
    let ch = channel_id.clone();
    willow_actor::state::mutate(&ctx.voice, move |v| {
        v.participants.entry(ch).or_default().insert(peer_id);
    })
    .await;
}

The signer variable (verified by unpack_wire) is available in scope but never compared to peer_id. The same issue affects VoiceLeave (line 345) and partially VoiceSignal (line 363 — signal uses signer for from_peer but the target check doesn't validate the source).

Attack Scenario

  1. Malicious peer signs a VoiceJoin message with their own key
  2. Sets peer_id to the victim's endpoint ID
  3. All peers' UIs show the victim as having joined the voice channel
  4. Can also forge VoiceLeave to make it appear someone left

Severity

HIGH — allows impersonation in voice channels and social engineering attacks.

Fix

Add signer verification before processing voice messages:

crate::ops::WireMessage::VoiceJoin { channel_id, peer_id } => {
    if peer_id != signer {
        return; // Reject spoofed voice join
    }
    // ... existing logic
}

Apply the same check to VoiceLeave.

Locations

  • crates/client/src/listeners.rs:329-343 — VoiceJoin
  • crates/client/src/listeners.rs:345-361 — VoiceLeave

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingsecurity

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions