VYPR
Medium severity4.9NVD Advisory· Published Jun 10, 2025· Updated Apr 15, 2026

CVE-2025-48937

CVE-2025-48937

Description

matrix-rust-sdk is an implementation of a Matrix client-server library in Rust. matrix-sdk-crypto since version 0.8.0 and up to 0.11.0 does not correctly validate the sender of an encrypted event. Accordingly, a malicious homeserver operator can modify events served to clients, making those events appear to the recipient as if they were sent by another user. This vulnerability is fixed in 0.11.1 and 0.12.0.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
matrix-sdk-cryptocrates.io
>= 0.8.0, < 0.11.10.11.1

Patches

2
56980745b4f2

chore: Add a changelog entry for GHSA-x958-rvg6-956w

https://github.com/matrix-org/matrix-rust-sdkDamir JelićJun 10, 2025via ghsa
1 file changed · +5 0
  • crates/matrix-sdk-crypto/CHANGELOG.md+5 0 modified
    @@ -6,6 +6,11 @@ All notable changes to this project will be documented in this file.
     
     ## [Unreleased] - ReleaseDate
     
    +### Security Fixes
    +- Check the sender of an event matches owner of session, preventing sender
    +  spoofing by homeserver owners.
    +  [#13c1d20](https://github.com/matrix-org/matrix-rust-sdk/commit/13c1d2048286bbabf5e7bc6b015aafee98f04d55) (High, [GHSA-x958-rvg6-956w](https://github.com/matrix-org/matrix-rust-sdk/security/advisories/GHSA-x958-rvg6-956w)).
    +
     ### Bug Fixes
     - Remove a wildcard enum variant import which breaks compilation if used with
       `tracing-attributes` version `0.1.29`. This is a workaround for a bug in
    
13c1d2048286

fix(crypto): Check the sender of an event matches owner of session

https://github.com/matrix-org/matrix-rust-sdkRichard van der HoffMay 23, 2025via ghsa
3 files changed · +107 2
  • crates/matrix-sdk-crypto/src/machine/mod.rs+24 2 modified
    @@ -1540,8 +1540,30 @@ impl OlmMachine {
         ) -> MegolmResult<(VerificationState, Option<OwnedDeviceId>)> {
             let sender_data = self.get_or_update_sender_data(session, sender).await?;
     
    -        let (verification_state, device_id) =
    -            sender_data_to_verification_state(sender_data, session.has_been_imported());
    +        // If the user ID in the sender data doesn't match that in the event envelope,
    +        // this event is not from who it appears to be from.
    +        //
    +        // If `sender_data.user_id()` returns `None`, that means we don't have any
    +        // information about the owner of the session (i.e. we have
    +        // `SenderData::UnknownDevice`); in that case we fall through to the
    +        // logic in `sender_data_to_verification_state` which will pick an appropriate
    +        // `DeviceLinkProblem` for `VerificationLevel::None`.
    +        let (verification_state, device_id) = match sender_data.user_id() {
    +            Some(i) if i != sender => {
    +                // For backwards compatibility, we treat this the same as "Unknown device".
    +                // TODO: use a dedicated VerificationLevel here.
    +                (
    +                    VerificationState::Unverified(VerificationLevel::None(
    +                        DeviceLinkProblem::MissingDevice,
    +                    )),
    +                    None,
    +                )
    +            }
    +
    +            Some(_) | None => {
    +                sender_data_to_verification_state(sender_data, session.has_been_imported())
    +            }
    +        };
     
             Ok((verification_state, device_id))
         }
    
  • crates/matrix-sdk-crypto/src/machine/tests/decryption_verification_state.rs+63 0 modified
    @@ -311,6 +311,69 @@ pub async fn mark_alice_identity_as_verified_test_helper(alice: &OlmMachine, bob
             .is_verified());
     }
     
    +/// Test that the verification state is set correctly when the sender of an
    +/// event does not match the owner of the device that sent us the session.
    +///
    +/// In this test, Bob receives an event from Alice, but the HS admin has
    +/// rewritten the `sender` of the event to look like another user.
    +#[async_test]
    +async fn test_verification_states_spoofed_sender() {
    +    let (alice, bob) = get_machine_pair_with_setup_sessions_test_helper(
    +        tests::alice_id(),
    +        tests::user_id(),
    +        false,
    +    )
    +    .await;
    +
    +    let room_id = room_id!("!test:example.org");
    +    let decryption_settings =
    +        DecryptionSettings { sender_device_trust_requirement: TrustRequirement::Untrusted };
    +
    +    // Alice sends a message to Bob.
    +    let (event, _) = encrypt_message(&alice, room_id, &bob, "Secret message").await;
    +    bob.decrypt_room_event(&event, room_id, &decryption_settings)
    +        .await
    +        .expect("Bob could not decrypt event");
    +    let event_encryption_info = bob.get_room_event_encryption_info(&event, room_id).await.unwrap();
    +    assert_matches!(
    +        event_encryption_info.verification_state,
    +        VerificationState::Unverified(VerificationLevel::UnsignedDevice)
    +    );
    +
    +    // Alice now sends a second message to Bob, using the same room key, but the HS
    +    // admin rewrites the 'sender' to Charlie.
    +    let encrypted_content = alice
    +        .encrypt_room_event(
    +            room_id,
    +            AnyMessageLikeEventContent::RoomMessage(RoomMessageEventContent::text_plain(
    +                "spoofed message",
    +            )),
    +        )
    +        .await
    +        .unwrap();
    +    let event = json!({
    +        "event_id": "$xxxxy:example.org",
    +        "origin_server_ts": MilliSecondsSinceUnixEpoch::now(),
    +        "sender": "@charlie:example.org",  // Note! spoofed sender
    +        "type": "m.room.encrypted",
    +        "content": encrypted_content,
    +    });
    +    let event = json_convert(&event).unwrap();
    +
    +    bob.decrypt_room_event(&event, room_id, &decryption_settings)
    +        .await
    +        .expect("Bob could not decrypt spoofed event");
    +
    +    // The verification_state of the event should be `MissingDevice` (since it
    +    // manifests as a message from Charlie which does not correspond to one of
    +    // Charlie's devices).
    +    let event_encryption_info = bob.get_room_event_encryption_info(&event, room_id).await.unwrap();
    +    assert_matches!(
    +        event_encryption_info.verification_state,
    +        VerificationState::Unverified(VerificationLevel::None(DeviceLinkProblem::MissingDevice))
    +    );
    +}
    +
     #[async_test]
     async fn test_verification_states_multiple_device() {
         let (bob, _) = get_prepared_machine_test_helper(tests::user_id(), false).await;
    
  • crates/matrix-sdk-crypto/src/olm/group_sessions/sender_data.rs+20 0 modified
    @@ -252,6 +252,26 @@ impl SenderData {
                 Self::SenderVerified { .. } => SenderDataType::SenderVerified,
             }
         }
    +
    +    /// Return our best guess of the owner of the associated megolm session.
    +    ///
    +    /// For `SenderData::UnknownDevice`, we don't record any information about
    +    /// the owner of the sender, so returns `None`.
    +    pub(crate) fn user_id(&self) -> Option<OwnedUserId> {
    +        match &self {
    +            SenderData::UnknownDevice { .. } => None,
    +            SenderData::DeviceInfo { device_keys, .. } => Some(device_keys.user_id.clone()),
    +            SenderData::VerificationViolation(known_sender_data) => {
    +                Some(known_sender_data.user_id.clone())
    +            }
    +            SenderData::SenderUnverified(known_sender_data) => {
    +                Some(known_sender_data.user_id.clone())
    +            }
    +            SenderData::SenderVerified(known_sender_data) => {
    +                Some(known_sender_data.user_id.clone())
    +            }
    +        }
    +    }
     }
     
     /// Used when deserialising and the sender_data property is missing.
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

7

News mentions

0

No linked articles in our index yet.