
RustDesk < 1.4.9 - Missing Session-Scope Enforcement Allows Out-of-Scope Control Message Injection
RustDesk sessions are authorized for a specific connection type — Remote (full desktop control), FileTransfer, PortForward, ViewCamera, or Terminal. Before 1.4.9, the server's post-authorization message gate only checked one boolean, self.authorized, and never re-checked it against the connection's actual AuthConnType. A peer that only holds FileTransfer (or / / ) authorization could send control messages reserved for a full session — mouse/keyboard input, screenshot requests — and the server processed them anyway.
PortForwardViewCameraTerminalRemotesrc/server/connection.rs the authorized-message gate before the fix reduced to:
if self.authorized {
// any message type is processed here — no check against AuthConnType
}
No function compared the incoming message's type against what the session was actually authorized for.
Added a per-AuthConnType whitelist, checked on every authorized message:
let allowed = match conn_type {
AuthConnType::Remote => true,
AuthConnType::FileTransfer => Self::is_file_transfer_scoped_message(msg),
AuthConnType::PortForward => false,
AuthConnType::ViewCamera => Self::is_view_camera_scoped_message(msg),
AuthConnType::Terminal => Self::is_terminal_scoped_message(msg),
};
Gate as merged:
if self.authorized {
if matches!(msg.union.as_ref(), Some(message::Union::LoginRequest(_))) {
return true;
}
if let Some(message) = self.authorized_scope_violation(&msg) {
return self.handle_authorized_scope_violation(message).await;
}
}
Anything outside scope now raises a SessionScopeViolation alarm and the connection is closed instead of processed.
AuthConnType | Allowed after the fix |
|---|---|
Remote | Everything (unchanged — this is the full-control session type) |
FileTransfer | FileAction / FileResponse message families only |
PortForward | Nothing beyond housekeeping — always denied |
ViewCamera | Camera/viewing messages, including ScreenshotRequest |
Terminal | TerminalAction message family only |
Files changed: src/server/connection.rs (the gate + scope functions + unit tests), src/client/io_loop.rs and src/ui_session_interface.rs (client-side view-camera guards), flutter/lib/common/widgets/toolbar.dart (hides the "Take screenshot" control outside default desktop sessions), libs/hbb_common (submodule bump).
1. Attacker obtains (or is granted) authorization for a limited connection
type only — e.g. AuthConnType::FileTransfer
2. self.authorized flips to true; pre-1.4.9, the server never re-checks
which AuthConnType that authorization actually covers
3. Attacker sends MouseEvent / KeyEvent / ScreenshotRequest on the same
connection — message types reserved for AuthConnType::Remote
4. Vulnerable server: processes them anyway (drives input, returns screen
content) — attacker acts outside the scope they were actually granted
5. Patched server (>= 1.4.9): rejects each message, raises a
SessionScopeViolation alarm, disconnects
CVE-2026-57850_POC.py — single file, no live target or network access needed.
It reimplements both the vulnerable gate (self.authorized only) and the fixed gate (the AuthConnType whitelist above, translated 1:1 from the PR) and replays the same message sequence — LoginRequest(FileTransfer) → MouseEvent → KeyEvent → ScreenshotRequest — through each, so you can see exactly which messages a pre-1.4.9 server would have processed that a patched one now blocks.
# Run the simulation (default: attacker holds FileTransfer-only auth)
python3 CVE-2026-57850_POC.py
# Simulate from a different limited scope
python3 CVE-2026-57850_POC.py --conn-type ViewCamera
# Optional: sanity-check that a local checkout matches the described
# vulnerable/patched code shape (looks for AuthConnType, SessionScopeViolation,
# authorized_scope_violation, is_view_camera_scoped_message in
# src/server/connection.rs)
python3 CVE-2026-57850_POC.py --repo-root /path/to/rustdesk