**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         ║
╚══════════════════════════════════════════════════════════════╝

Attachment: spice_full_chain.c
Description: Binary data

Attachment: spice_protocol_inject.c
Description: Binary data

Reply via email to