From 1266b56150da889dfec318e68b9b20d7c30c0bb6 Mon Sep 17 00:00:00 2001 From: rustdesk Date: Mon, 21 Sep 2026 14:19:34 +0800 Subject: [PATCH] kx_advertised: every message after the exchange, not the first alone The contract named the server's first message after the key exchange. That frame sits at a position an attacker needs no key to find, so one who had lowered the advertisement in the clear only had to drop it to be rid of the check for the whole session. With every message carrying the echo, being rid of it means dropping them all, which is a plain DoS. The client already kept the check armed until an echo arrived; this is the wording the server side is written to. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01EZ49AbZJYfm8NTp5yDPMab --- protos/rendezvous.proto | 6 ++++-- src/stream.rs | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/protos/rendezvous.proto b/protos/rendezvous.proto index e8c7d6c2a..fe0ef637a 100644 --- a/protos/rendezvous.proto +++ b/protos/rendezvous.proto @@ -297,9 +297,11 @@ message RendezvousMessage { HttpProxyResponse http_proxy_response = 28; IceCandidate ice_candidate = 29; } - // From the server, on its first message after a key exchange: the version it advertised, + // From the server, on every message after a key exchange: the version it advertised, // repeated inside the encrypted channel where nothing in transit can alter it. A client that // was shown a different value in the clear knows the handshake was altered and refuses the - // stream. Absent from servers that predate versions. + // stream. Every message and not the first alone, so that dropping one frame at a position + // that needs no key to find does not rid an attacker of the check; dropping them all is a + // plain DoS. Absent from servers that predate versions. uint32 kx_advertised = 30; } diff --git a/src/stream.rs b/src/stream.rs index 8702828da..e10c7e3d2 100644 --- a/src/stream.rs +++ b/src/stream.rs @@ -100,7 +100,7 @@ impl Stream { } } - /// Refuse the stream if the server's first encrypted message says it advertised a key + /// Refuse the stream if an encrypted message from the server says it advertised a key /// exchange version other than `seen`, the one this side received in the clear. #[inline] pub fn check_kx_advertised(&mut self, seen: u32) {