Bug 1173749

Summary: AUDIT-0: spice-vdagent: spice-vdagentd.service can be implicitly started by default
Product: [Novell Products] SUSE Security Incidents Reporter: Fabian Vogt <fvogt>
Component: IncidentsAssignee: Matthias Gerstner <matthias.gerstner>
Status: RESOLVED FIXED QA Contact: Security Team bot <security-team>
Severity: Normal    
Priority: P5 - None CC: brogers, jsegitz, lnussel, matthias.gerstner, meissner
Version: unspecified   
Target Milestone: ---   
Hardware: Other   
OS: Other   
Whiteboard:
Found By: --- Services Priority:
Business Priority: Blocker: ---
Marketing QA Status: --- IT Deployment: ---
Attachments: final version of security patches
tarball containing the reproducers for the issues described in the report in comment 15

Description Fabian Vogt 2020-07-06 09:49:15 UTC
spice-vdagent is installed by default due to a modalias supplements on virtio.
The systemd preset has spice-vdagentd.service disabled, which means that it does not work OOTB.
Comment 1 Fabian Vogt 2020-07-06 10:06:37 UTC
Meh, it's not quite that simple.

This works fine when booting into graphical.target, but fails when booting into multi-user.target and switching to graphical.target with "systemctl isolate graphical.target" or "init 5".

There is /usr/lib/udev/rules.d/70-spice-vdagentd.rules, which enables the socket once the virtio port appears. Apparently this is not part of the state which is kept across isolate.

@security: Has this service not been audited before due to ^ evading the usual triggers?

@systemd-maintainers:
Is there anything that can be done to make udev triggered units stay across isolate?
Maybe spice-vdagentd.socket needs IgnoreOnIsolate=true?
Comment 2 Matthias Gerstner 2020-07-07 09:46:34 UTC
systemd services do not require a security review per se. Adding a systemd
service to the systemd presets to have it started automatically does require a
review, however.

The notion behind that is that we want to keep the default security on a high
level. systemd services can for example be installed implicitly as
dependencies of other packages. We usually don't want them to start up right
away. It should be a conscious user decision to do so.

The list of services that are automatically started is pretty short at the
moment. So when you absolutely need this and have a convincing use case why it
needs to be that way then we can do the AUDIT and add it to
systemd-presents-branding-openSUSE.
Comment 3 Matthias Gerstner 2020-08-12 11:39:34 UTC
So are there any news here? Do you still needs this?
Comment 4 Fabian Vogt 2020-08-26 09:47:05 UTC
(In reply to Matthias Gerstner from comment #2)
> systemd services do not require a security review per se. Adding a systemd
> service to the systemd presets to have it started automatically does require
> a
> review, however.
> 
> The notion behind that is that we want to keep the default security on a high
> level. systemd services can for example be installed implicitly as
> dependencies of other packages. We usually don't want them to start up right
> away. It should be a conscious user decision to do so.

The udev rule starts the service, so effectively the service is automatically started, but only if the rule matches. That's why I'm asking whether an audit is required for that as well.

(In reply to Matthias Gerstner from comment #3)
> So are there any news here? Do you still needs this?

If no audit is neccessary, only the systemd bug remains, so feel free to reassign.
Comment 5 Matthias Gerstner 2020-08-26 13:02:40 UTC
(In reply to fvogt@suse.com from comment #4)
> The udev rule starts the service, so effectively the service is
> automatically started, but only if the rule matches. That's why I'm asking
> whether an audit is required for that as well.

Is there a reason why the rule shouldn't match?

It shows that we have a loophole in our security restrictions. udev rules can
circumvent our systemd autostart restrictions. So maybe we should review this
service here after all.

> If no audit is neccessary, only the systemd bug remains, so feel free to
> reassign.

What do you mean by "the systemd bug"?
Comment 6 Fabian Vogt 2020-08-26 13:07:18 UTC
(In reply to Matthias Gerstner from comment #5)
> (In reply to fvogt@suse.com from comment #4)
> > The udev rule starts the service, so effectively the service is
> > automatically started, but only if the rule matches. That's why I'm asking
> > whether an audit is required for that as well.
> 
> Is there a reason why the rule shouldn't match?

The service is for connecting to QEMU, so it only works if it's running in a VM with the appropriate guest device enabled.

> It shows that we have a loophole in our security restrictions. udev rules can
> circumvent our systemd autostart restrictions. So maybe we should review this
> service here after all.
> 
> > If no audit is neccessary, only the systemd bug remains, so feel free to
> > reassign.
> 
> What do you mean by "the systemd bug"?

See comment 1. The udev rule has ENV{SYSTEMD_WANTS}="spice-vdagentd.socket" but the .socket unit is stopped on "systemctl isolate", even though the device (and therefore the wants relationship) is still there.
Comment 7 Matthias Gerstner 2020-08-26 14:06:27 UTC
(In reply to fvogt@suse.com from comment #6)
> > Is there a reason why the rule shouldn't match?
> 
> The service is for connecting to QEMU, so it only works if it's running in a VM with the appropriate guest device enabled.

Does it help to enable the service when the device is not enabled?

> See comment 1. The udev rule has ENV{SYSTEMD_WANTS}="spice-vdagentd.socket"
> but the .socket unit is stopped on "systemctl isolate", even though the
> device (and therefore the wants relationship) is still there.

Is this actually a systemd bug / something that systemd is likely to fix in
the sense of this bug here?

After a review we can add this service to the systemd presets. Hopefully there
won't be any side effects ... the package is not installed by default, I hope,
so for users not needing this nothing should change.
Comment 8 Fabian Vogt 2020-08-26 14:10:27 UTC
(In reply to Matthias Gerstner from comment #7)
> (In reply to fvogt@suse.com from comment #6)
> > > Is there a reason why the rule shouldn't match?
> > 
> > The service is for connecting to QEMU, so it only works if it's running in a VM with the appropriate guest device enabled.
> 
> Does it help to enable the service when the device is not enabled?

It would most likely just fail immediately.

> > See comment 1. The udev rule has ENV{SYSTEMD_WANTS}="spice-vdagentd.socket"
> > but the .socket unit is stopped on "systemctl isolate", even though the
> > device (and therefore the wants relationship) is still there.
> 
> Is this actually a systemd bug / something that systemd is likely to fix in
> the sense of this bug here?

I hope so, we'll see.

> After a review we can add this service to the systemd presets. Hopefully
> there
> won't be any side effects ... the package is not installed by default, I
> hope,
> so for users not needing this nothing should change.

Yes, but I don't think that adding it to the preset is the right way actually.
The udev rule is the right approach, just hits some corner cases...
Comment 9 Matthias Gerstner 2020-08-27 10:24:05 UTC
(In reply to fvogt@suse.com from comment #8)
> Yes, but I don't think that adding it to the preset is the right way actually.
> The udev rule is the right approach, just hits some corner cases...

Okay then I suggest we keep this bug open for review. And you can create a
split-off bug for systemd folks to address the separate issue of the `isolate`
behaviour.
Comment 10 Fabian Vogt 2020-08-27 10:32:16 UTC
(In reply to Matthias Gerstner from comment #9)
> (In reply to fvogt@suse.com from comment #8)
> > Yes, but I don't think that adding it to the preset is the right way actually.
> > The udev rule is the right approach, just hits some corner cases...
> 
> Okay then I suggest we keep this bug open for review. And you can create a
> split-off bug for systemd folks to address the separate issue of the
> `isolate`
> behaviour.

Done: bug 1175822
Comment 11 Matthias Gerstner 2020-09-10 13:31:46 UTC
I will look into this service now.
Comment 12 Matthias Gerstner 2020-09-18 12:55:42 UTC
The review brought some security issues to light, so let's move this bug into
SLE space and make it private for the time being.
Comment 13 Matthias Gerstner 2020-09-18 12:58:06 UTC
Internal CRD: 2020-12-17 preliminary
Comment 14 Matthias Gerstner 2020-09-18 13:00:47 UTC
This is an embargoed bug. This means that this information is not public.

Please do NOT:
- talk to other people about this unless they're involved in fixing the issue
- make this bug public
- submit this into OBS (e.g. fix Leap/Tumbleweed) until this bug becomes public (e.g. no EMBARGOED tag on the header)

Consult with security team if you think that the issue is public and the bug is still private (e.g. subject still contains "EMBARGOED"). Please do NOT make the bug public yourself.

Please be aware that the SUSE:SLE-15-SP3:GA codestream is available via OBS, so do NOT submit there before this is public.

These are the steps that are asked from you:
1, Your primary responsibility is to submit a fix for this issue. Here's a how-to for submitting packages for maintenance releases in IBS:
   https://confluence.suse.com/display/maintenance/How+to+Submit+Packages+or+Containers+to+Maintenance
   Apart from the GA codestreams mentioned above, you can submit to IBS anytime. This is private and allows us to start testing as soon as possible.
2, We also want to fix openSUSE if it's affected.
   $ is_maintained $PACKAGE
   will tell you if the package is inherited from SLES or if it is branched for openSUSE. There are two cases:
   - It's coming from SLES: The update will automatically be released for openSUSE. Nothing to do for you.
   - It's branched for openSUSE: You need to submit AFTER the bug became public, to the current openSUSE codestreams.
   For openSUSE Factory please submit to the devel project of you package AFTER the bug became public.

Security will then take the following steps:
- We wait for your submission and package them into an incident for QA testing. The QA tester might reach out to you if he finds issues with the update.
- Once the coordinated release date (CRD), the date this issue should become public, is reached (or for internal findings: once we're done testing), we remove the EMBARGOED tag from this bug and publish the updates.
- Only if the bug here is public you may submit to public repositories (OBS).

You can contact us at:

* IRC: irc.suse.de #security
* RocketChat: https://chat.suse.de/channel/security
* Email: security-team@suse.de
Comment 15 Matthias Gerstner 2020-09-18 13:03:35 UTC
Following is the security report that I just shared with the two active
developers of the FreeDesktop GitLab repository for spice-vdagent. I couldn't
find any more suitable security contact documented anywhere. Let's see what
they respond.

 # 1) Introduction
 
 The SUSE security team got a request to review the `spice-vdagentd` daemon.
 It got to our attention that this daemon can start indirectly via udev
 (`70-spice-vdagentd.rules`) even if the service was not explicitly activated by
 the Administrator and thus might introduce additional security attack
 surface in default installations.
 
 The `spice-vdagentd` is typically used in Qemu virtual machines to provide
 additional features like:
 
 - a shared clipboard between host and guest system to allow seamless
   copy/paste
 - file transfers from the host to the guest machine
 - sharing information about the virtual display size and monitor configuration
 - synchronizing audio device volume between host and guest
 
 `spice-vdagentd` is running as *root* as a socket activated systemd service.
 
 The following security report is based the spice-vdagent version 0.20.0,
 which is the latest upstream release available as of this writing.
 
 # 2) The `vdagentd` Communication Channels
 
 This section gives a short introduction about the communication channels used
 by `vdagentd` for readers that are not familiar with its design.
 
 ## a) The com.redhat.spice.0 Virtio Serial Device
 
 In Qemu virtual machine instances that have the spice protocol enabled a
 virtual serial device will be emulated, typically enabled by passing switches
 like these to qemu:
 
 ```
 -spice disable-ticketing,unix,addr=/path/to/spice_socket, -device virtio-serial-pci -device virtserialport,chardev=spicechannel0,name=com.redhat.spice.0 -chardev spicevmc,id=spicechannel0,name=vdagent
 ```
 
 Within the virtual machine `spice-vdagentd` talks to this serial device
 (`/dev/virtio-ports/com.redhat.spice.0`) to exchange information with the host
 machine and implement the spice features. The SPICE protocol is used on this
 communication channel.
 
 This serial device is of lesser importance in the context of this security
 report.
 
 ## b) The spice-vdagent UNIX Domain Socket
 
 The `spice-vdagentd` creates a listening UNIX domain socket of type
 `SOCK_STREAM` in `/run/spice-vdagentd/spice-vdagent-sock`. This socket path is
 accessible to all users in the system (file mode 0666). It is used by the
 counterpart of `spice-vdagentd`, the `spice-vdagent`, which runs in the
 context of a graphical user session with user privileges. This role will also
 be called the "user agent" for the rest of this document.
 
 The `spice-vdagent` e.g. informs the `spice-vdagentd` about changes in
 clipboard contents in the graphical VM session, which in turn forwards this
 information via the serial device to the host machine. Similarly requests from
 the host machine to e.g. initiate a file transfer will be forwarded from
 `spice-vdagentd` to the `spice-vdagent`, which will then store the file data
 in the context of the user session.
 
 Multiple user agents can connect to `spice-vdagentd` at the same time. At most
 one user agent may be present in the currently active session, however. Only
 a user agent running in the currently active session can access most of
 the features of `spice-vdagentd`. `spice-vdagentd` determines the session a
 user agent is part of by querying systemd facilities (e.g.
 `sd_seat_get_active()`, `sd_pid_get_session()`). The user agent (if any)
 associated with the currently active session will also be the target of any
 forwarded requests received from the host on the virtual serial device.
 
 # 3) Security Issues
 
 This report is accompanied by two Python scripts (`vdagent.py`,
 `create_vdagent_conns.py`) and a C++ source file (`socket_pid_attack.cxx`)
 which can be used to reproduce the findings that follow. These reproducers
 will be mentioned in the individual sections. All reproducers need to be
 executed in the context of a host system running a SPICE enabled Qemu virtual
 machine (see section 2.a for the required Qemu switches, or use something like
 livbirtd and its Qemu backend with appropriate settings).
 
 ## a) Memory DoS via Arbitrary Entries in `active_xfers` Hash Table
 
 The `spice-vdagentd` maintains a hash map named `active_xfers` that maps
 `task_ids` to UNIX domain socket connections they belong to. These `task_ids`
 refer to ongoing file transfers from the host to the virtual machine.
 
 An arbitrary client connected to `spice-vdagentd` via a UNIX domain socket can
 trigger an entry into this hash map, without the requirement that the client
 is associated with the currently active graphical session (function
 `do_agent_file_xfer_status`, specifically `vdagentd.c:1025`). There is no limit
 on the maximum amount of file transfers ongoing in parallel and there are no
 timeouts applied for a file transfer to be finished.
 
 Therefore any unprivileged local user with access to the
 `/run/spice-vdagentd/spice-vdagent-sock` socket path can perform a memory
 denial-of-service by entering a large amount of entries into this hash map.
 
 ### Impact
 
 - an unprivileged local attacker inside a virtual machine can cause large
   memory allocation in a daemon running as *root*. These memory allocations will
   persist as long as the UNIX domain socket connection remains intact. After a
   longer while, depending on the available system memory and swap space, the
   system might enter an out of memory situation, causing a denial-of-service for
   `spice-vdagentd` or even other processes in the system (the Linux kernel OOM
   killer is known to punish innocent victims at times).
 - after a large amount of entries have been made by an attacker this way and
   once the UNIX domain socket connection is terminated, the `spice-vdagentd`
   will remove all the hash map entries belonging to this connection (function
   `agent_disconnect`, specifically `vdagentd.c:955`). Each "cancelled" file
   transfer will be logged in the system (`vdagentd.c:910`, `vdagentd.c:342`).
   In my tests this also caused a high load in `systemd-journald` and high disk
   space consumption from the logs stored in `/var/log`.
 
 ### Suggested Fix
 
 Clients not belonging to the active session should not be allowed to cause
 data to be entered into the `active_xfers` hash map. A maximum amount of file
 transfers should be enforced that prevents any DoS situations (even a
 legitimate client from the active session could still cause the DoS).
 Furthermore, since file transfers are initiated by the host machine, only
 entries for actually existing file transfers should be allowed to clients.
 
 Additionally, a timeout could be implemented for file transfers (in relation
 to the file size) that prevents file transfers from getting stuck.
 
 ### Reproducer
 
 Using the supplied Python program the DoS can be performed this way:
 
 ```
 user $ ./vdagent.py --xfer-status-DoS
 ```
 
 The program will infinitely create new entries in the `active_xfers` hash map
 up until the maximum 64-bit (for `x86_64` architectures) integer value. Memory
 consumption will grow rather slowly but reliably. An exponential growth
 allocation scheme seems to be in place, because the observed memory
 consumption (as seen in `top`) only happens in jumps.
 
 ## b) Possible File Transfer DoS and Information Leak via `active_xfers` Hash Map
 
 The same basic problem as described in section 3.a can lead to a file transfer
 information leak. The file transfer protocol roughly works like this:
 
 - The host will send a `VD_AGENT_FILE_XFER_START` message that is forwarded to
   the user agent (function `do_client_file_xfer()`, specifically
   `vdagentd.c:376`). This message contains a `task_id` that identifies the file
   transfer process in future messages.
 - The `spice-vdagent` will check free disk space and allocate a file of the
   expected size in the file system. If all checks pass then it will reply with
   a `VDAGENTD_FILE_XFER_STATUS_CAN_SEND_DATA` message, which causes
   `spice-vdagentd` to associate the client connection with the ongoing file
   transfer.
 - The host will now start sending out chunks of the file data with
   `VDAGENTD_FILE_XFER_DATA` messages (processed in function
   `do_client_file_xfer()`, specifically `vdagentd.c:386`). `spice-vdagentd` will
   forward each chunk to the client connection stored in the `active_xfers`
   hash map.
 
 The host application (tested with `remote-viewer` from the virt-viewer
 package) chooses an incrementally growing `task_id` for file exchanges
 which starts counting at 1. Thus the `task_id` is predictable. Since any
 unauthenticated local client can replace the mapping of `task_id` to client
 connection by its own client connection, there is a possibility for an
 attacker to obtain parts of the transferred file data.
 
 The attacker needs to win a race condition here, because it needs to hit the
 time window after the legitimate client sends out the
 `VDAGENTD_FILE_XFER_STATUS_CAN_SEND_DATA` message and before the host starts
 sending out file chunks via `VDAGENTD_FILE_XFER_DATA`. If the attacker sends
 his own `VDAGENTD_FILE_XFER_STATUS_CAN_SEND_DATA` using the correct `task_id`
 during this time window, then he can obtain the complete file. At least for
 large file exchanges bigger parts of the file are feasible to be
 obtained, even when the initial parts of the file are transferred to the
 legitimate client.
 
 The more difficult part for an attacker will be to identify when such a file
 transfer will take place. The reproducer shows the basic attack technique.
 
 ### Impact
 
 File data from the host system can end up in full or in parts in the client
 connection of an illegitimate local user in the VM system. Exploitability will
 be difficult if there is not a suitable side channel with information about
 file transfers going on.
 
 In any case active file transfers from other users can also be interrupted
 (DoS aspect).
 
 ### Suggested Fix
 
 Basically the same as in section 3.a and additionally:
 
 - it should be made sure that existing entries in `active_xfers` never get
   overwritten before the transfer ends or is cancelled.
 - only the same client that originally received the `VD_AGENT_FILE_XFER_START`
   should be able to reply with `VDAGENTD_FILE_XFER_STATUS_CAN_SEND_DATA`.
 - `task_ids` could be randomized to become unpredictable (but this is not in
   the `spice-vdagentd` code, rather in the code of the tools running on the
   host).
 
 ### Reproducer
 
 This reproducer has the precondition that the victim's home directory is
 world-readable (or at least readable by the attacker). The script will wait
 (using the *inotify* API) for a new file to be created in the home directory
 (assuming that this is the initiation of a file transfer).
 
 For reproducing perform the following steps:
 
 1. Open a fresh `remote-viewer` connection for the test VM. This will reset
    the `task_id` used for file transfers to start to 1.
 2. Run the reproducer script in an attacker context as follows:
 
         # replace this path by the actual victim's home directory
         VICTIM=/home/victim
         attacker$ ./vdagent.py --send-xfer-status 1 --wait-for-file-create $VICTIM
 
 3. Log in as the *victim* user in a graphical session in the VM using the
    `remote-viewer` connection started in step 1.
 4. Drag-and-drop a larger file (I tested using an 8 megabyte text file) into
    the `remote-viewer` window to initiate file transfer.
 
 You should see that the attacker's `vdagent.py` script outputs large parts of
 the file transfer to the terminal. The original file transfer will indicate an
 error condition without specific details about the problem..
 
 Remember that for each subseqeuent attack attempt you will need to increment
 the `task_id` (`--send-xfer-status` parameter) or open a fresh `remote-viewer`
 window for the VM, to reset the `task_id` used on the host side.
 
 ## c) Possibility to Exhaust File Descriptors in `vdagentd`
 
 `spice-vdagentd` does not apply a limit to the amount of client connections
 that can be established via the UNIX domain socket in
 `/run/spice-vdagentd/spice-vdagent-sock`. Also existing connections aren't
 subject to a timeout or any kind of preconditions for them to stay alive.
 Thus it is easy to exhaust the file descriptor limit for the `spice-vdagentd`
 process (typically 1024 file descriptors by default, this limit is also
 imposed by system calls like `select()`).
 
 Any local user in the virtual machine can open around ~1020 connections to
 `spice-vdagentd` and simply keep them open without transmitting any data. The
 `spice-vdagentd` will then become unable to open further connections for
 legitimate clients or perform other tasks (like opening the serial device, see
 section 2.a, or invoking systemd library calls that require opening files).
 
 ### Impact
 
 By exhausting file descriptors in `spice-vdagentd` the following effects can
 be achieved:
 
 - The attack can prevent legitimate `spice-vdagent` instances from connecting
   to the `spice-vdagentd`. SPICE features won't be available to affected
   sessions.
 - The attack can cause `vdagentd` to exit on error conditions if tuned
   carefully. For example, an attacker can exhaust all file descriptors in
   `spice-vdagentd` except for one and then wait for a legitimate client from an
   active session to connect. This connection attempt will succeed, but the
   subsequent attempt to open the serial device (see section 2.a) will fail, and
   `spice-vdagentd` will exit. This will then also cause the involved
   `spice-vdagent` to exit, because the connection to the system daemon is
   lost.
 - `spice-vdagentd` will enter a 100 % CPU infinite loop, because it tries to
   `accept()` the new connection, which is impossible, but also doesn't close
   the listening socket or abort execution.
 - This attack vector makes security issue 3.d better exploitable, which will
   be explained there in more detail.
 
 ### Suggested Fix
 
 - `spice-vdagentd` should enforce a small upper limit of UNIX domain socket
   connections that are accepted and maintained in parallel. This will prevent
   file descriptor exhaustion for the process.
 - `spice-vdagentd` should perform some kind of credibility check on
   established connections: if the peer is not connected to any graphical
   session then the connection should be terminated again. Multiple connections
   from the same session should be terminated again. This already happens, but
   only if two agents for the same active session are encountered
   (`vdagentd.c:875`). This way local users should not be able to deny the SPICE
   service to other local users.
 
 ### Reproducer
 
 Using the supplied script `create_vdagent_conns.py` the maximum number of
 connections that `spice-vdagentd` can process, can be established. The result
 will be that no new user agents can register with the system daemon. A 100 %
 CPU loop will occur in `spice-vdagentd`.
 
 ## d) UNIX Doman Socket Peer PID Retrieved via `SO_PEERCRED` is Subject to Race Condition
 
 One major security property of `spice-vdagentd` is that it only allows those
 clients access to most of the SPICE features (like clipboard, file transfer)
 that are currently in an active session according to systemd (see also section
 2.b). It is possible for arbitrary local users (like *nobody*) to connect to
 `spice-vdagentd` but these connections should not be able to interact with the
 host machine, because they don't belong to the active session.
 
 The session check is performed after a new UNIX domain socket connection is
 established in `agent_connect()` in `vdagentd.c:937`. The check basically
 relies on these two source code lines:
 
 ```
 pid = vdagent_connection_get_peer_pid(VDAGENT_CONNECTION(conn), &err);
 agent_data->session = session_info_session_for_pid(session_info, pid);
 ```
 
 The peer's PID is obtained via glib's `g_socket_get_credentials` which boils
 down to the `SO_PEERCRED` socket option that is supported for UNIX domain
 sockets (see `man 7 socket`, `man 7 unix`, `struct ucred`). The man page says
 about this:
 
 > The returned credentials are those that were in effect at the time of the call
 > to connect(2) or socketpair(2).
 
 This means that there is a race condition between the point in time when a
 client performs the `connect()` call to establish a connection with
 `spice-vdagentd` and the time `spice-vdagentd` retrieves and checks the PID in
 its `agent_connect()` function. The PID in question can already have been
 replaced by an unrelated process. Therefore the session that `spice-vdagentd`
 associates with this PID might be a different one than the actual peer process
 belonged to, when the `connect()` system call was performed.
 
 An attack to exploit the race condition requires the following steps:
 
 1. an attacker can inherit a UNIX domain socket file descriptor to a child
 process that performs the `connect()` to `spice-vdagentd` and exits
 immediately again, thereby freeing the PID (let's call it the malicious PID)
 in the system as soon as the parent process performs a `wait()` on the exited
 child process. This malicious PID will now be associated in the kernel with
 the `SO_PEERCRED` data returned for the connected UNIX domain socket.
 2. now the attacker needs to perform a PID cycle in the system (i.e.
 create many useless child processes to cause the maximum PID - typically 32768 -
 to be reached in the system and new processes get assigned small PIDs
 again). When the PIDs assigned by the kernel are getting close to the
 malicious PID, the attacker needs to stop creating child processes and wait
 for unrelated processes from other users to come into existence.
 3. Once the malicious PID gets reassigned to an unrelated process and the
 `agent_connect()` function runs in `spice-vdagentd`, it will retrieve wrong
 session information for the existing connection. If the malicious PID gets
 reassigned to a process running in the active session, then the connection
 that the attacker uses will get access to the SPICE features and can
 communicate with the host, although the attacker would otherwise not have
 sufficient privileges to do so.
 
 The described race condition is very hard to hit under normal circumstances,
 because step 2., the PID cycle, is taking a long time and the
 `agent_connect()` function in `spice-vdagentd` is very likely to run before an
 unrelated process gets reassigned the malicious PID in question. When combined with
 the file descriptor exhaustion security issue described in section 3.c,
 however, then this attack will become way more feasible.
 
 This combined attack works like follows:
 
 - Exhaust all file descriptors in `spice-vdagentd` as described in section
   3.c.
 - Now perform the attack steps 1. and 2. as described previously. What
   happens now is that the attacker's UNIX domain socket `connect()` will succeed,
   because on kernel level this is still possible. `spice-vdagentd` won't be able
   to `accept()` this connection, though, because no more file descriptors are
   available to do so. The connection remains pending on the listening socket,
   however.
 - Now for step 3., once the attacker notices that the malicious PID got
   assigned to an unrelated process, he can stop the file descriptor exhaustion
   put into place previously, thus making it possible for `spice-vdagentd` to
   `accept()` the malicious connection pending in the kernel. Only now will the
   `agent_connect()` function run, and it will more reliably determine the
   wrong session for the connection.
 
 ### Impact
 
 1. A compromised local account with little privileges inside the virtual
   machine like *nobody* can try to become the "active agent" for
   `spice-vdagentd` for the graphical session of a legitimate local user. If
   successful then the attacker can access the host's clipboard contents or
   send malicious clipboard content to the host. The attacker can also retrieve
   file data from the host (compare section 2.b) or send invalid screen
   resolution and display information to the host.
 2. The combined attack using the file descriptor exhaustion and the
   `SO_PEERCRED` race condition is still not 100 % reliable but it can be
   repeated many times to increase chances of success. The only unpredictable
   ingredient is victim child processes appearing that get assigned the desired
   malicious PID and stay around for long enough for `spice-vdagentd` to pick up the
   wrong session information.
 3. If the victim's graphical session already runs a legitimate `spice-vdagent`
   then a successful attack will trigger an information leak protection logic
   in `vdagentd.c:874`. This has the effect of a denial-of-service, because
   neither the attacker nor the legitimate user will be able to use the SPICE
   features anymore.
 4. If the victim's graphical session is not running a `spice-vdagent` then the
   attacker can achieve all the effects described in 1.
 5. If 3. applies (the victim's is already running `spice-vdagent`) then the
   attacker could try to crash the currently running `spice-vdagentd` (see
   section 3.c for a possible attack vector). systemd should then restart the
   `spice-vdagentd` while the victim's `spice-vdagent` should exit but not be
   restarted. After this situation 4) applies.
 
 ### Suggested Fix
 
 - Once the file descriptor exhaustion (3.c) is fixed, this attack will be
   harder to perform successfully.
 - `spice-vdagentd` should not rely solely on the PID information to associate
   the peer with a running session. An additional sanity check involving the
   peer's UID should at least prevent that a connection from a different user
   gets access to the active session context.
 
 ### Reproducer
 
 This attack is quite complex and requires the combination of all supplied
 programs to succeed. All programs must be placed in the same directory,
 because they interact with each other. Perform the following steps:
 
 1. For reduced complexity it is a precondition that an active graphical session
   already exists in the VM, but no `spice-vdagent` is running in it. Therefore
   log in the "victim" user in a graphical session, kill any `spice-vdagent`
   that got started automatically and keep the session open.
 2. In the context of an "attacker" user run `create_vdagent_conns.py`:
 
         attacker$ ./create_vdagent_conns.py
          Established 1015 active connections to vdagentd.
          Waiting for Ctrl-C. Or press ENTER to close 5 sockets.
 
   Wait until the program displays the message seen above. Keep the
   program running in this state.
 3. Still in the context of the attacker compile and run the C++ program:
 
         attacker$ g++ -O2 socket_pid_attack.c -o socket_pid_attack
         attacker$ ./socket_pid_attack
          Target UDS PID = ????
          Cycled to PID ....
          [...]
          Closing in to target_pid ????: Got child PID ????
          Now waiting for ???? to get reassigned.
 
   This program creates a UNIX domain socket connected to `spice-vdagentd` but
   the `connect()` call will have been performed in a short-living child
   process, thus freeing the PID the kernel stores in `SO_PEERCRED`. The
   program then waits for the malicious PID to be assigned to an unrelated
   process before it continues. Keep the program running in this state.
 4. In the context of the "victim" user create a number of long running new child
    processes in the graphical session. For example something like this:
 
         victim$ for i in `seq 20`; do sleep 1h & done
 
    This simulates new child processes being created in the victim's context to
    replace the malicious PID the attacker is waiting for.
 5. If successful then you should see in the program from step 3. that
    something happened:
 
          PID ??? now exists, but can't read exe: Permission denied
          Running './vdagent.py --socket-fd 3 '
          Using existing connected socket file descriptor
 
    If this did not work right away then you need to repeat steps 3) and 4)
    (rather quickly) until it succeeds.
 
    In the case of success, the `socket_pid_attack` program will have replaced
    itself by the `vdagent.py` Python program, which needs to be present in the
    same directory. The Python program will block, trying to receive data on the
    connected UNIX domain socket, because `spice-vdagentd` can't accept the
    connection. 
    Now terminate the program still running from step 1. via Ctrl-C to free up
    the blocked file descriptors in `spice-vdagentd`. The malicious connection
    should now be accepted by `spice-vdagentd` and the attacker should have
    become the active agent for the victim's graphical session. The additional
    output of the program from step 3. should look similar to this:
 
    ```
    MessageType.VERSION arg1 = 0 arg2 = 0 bytes = 7
    b'302e32302e3000'
    sending resolution of 1024 768
    MessageType.GRAPHICS_DEVICE_INFO arg1 = 0 arg2 = 0 bytes = 4
    b'00000000'
    MessageType.CLIENT_DISCONNECTED arg1 = 0 arg2 = 0 bytes = 0
    MessageType.GRAPHICS_DEVICE_INFO arg1 = 0 arg2 = 0 bytes = 4
    b'00000000'
    MessageType.GRAPHICS_DEVICE_INFO arg1 = 0 arg2 = 0 bytes = 4
    b'00000000'
    MessageType.AUDIO_VOLUME_SYNC arg1 = 0 arg2 = 0 bytes = 7
    b'01eb0200000000'
    MessageType.AUDIO_VOLUME_SYNC arg1 = 0 arg2 = 0 bytes = 7
    b'00eb0200000000'
    MessageType.CLIPBOARD_RELEASE arg1 = 1 arg2 = 0 bytes = 0
    sending clipboard grab request for ClipboardType.SELECTION_PRIMARY
    ```
 
    If the attacker's connection is not considered to be part of the active
    session then the `AUDIO_VOLUME_SYNC` and `GRAPHICS_DEVICE_INFO` messages
    will not be received from the `spice-vdagentd`. This can happen if some
    other user in the system received the malicious PID or if the PID was not
    in existence for long enough. If this is the case repeat steps 3. to 5.
    until it succeeds.
 6. If the previous steps all succeeded then you should be able to see for
    example the clipboard content from the host when a SPICE capable viewer is
    used to connect to the virtual machine. The attacker can also send back
    "malicious clipboard content" to the host.
 
 # 4) Other Suggestions
 
 ## a) fchmod() instead of chmod() for UNIX domain socket
 
 Just a minor suggestion about the `chmod()` call in `vdagentd.c:1223`. If
 possible this should be replaced by a call to `fchmod()`, if the file
 descriptor can be obtained from the glib functions. If custom, unsafe paths
 are used for the UNIX domain socket then symlink attacks might become possible
 through the use of `chmod()`.
 
 # 5) CVE Assignments
 
 I think that each of the reported security issues 3.a, 3.b, 3.c and 3.d
 warrant CVE assignments.
 
 # 1173749: [IN_PROGRESS] AUDIT-0: EMBARGOED: spice-vdagent: spice-vdagentd.service can be implicitly started by default
 # Please enter the new comment. Lines starting with '#' will be ignored
 # lines starting with ' #' will be kept
 #
Comment 16 Matthias Gerstner 2020-10-16 07:30:36 UTC
Upstream assigned a couple of CVEs and sent me some patches for review.
There's no publication date known yet.

I will create sub-bugs for each CVE for tracking.
Comment 17 Matthias Gerstner 2020-10-27 09:41:31 UTC
Created attachment 843022 [details]
final version of security patches
Comment 18 Matthias Gerstner 2020-10-27 09:43:48 UTC
CRD: 2020-11-03

I will send a prenotification to the linux-distros mailing list for major distributions to be able to fix their packages before general publication. The CRD above is the designated publication date.

@brogers: can you please prepare internal IBS submissions using the patches found in attachment 843022 [details]? Thank you.
Comment 19 Bruce Rogers 2020-10-28 12:26:22 UTC
(In reply to Matthias Gerstner from comment #18)
> CRD: 2020-11-03
> 
> I will send a prenotification to the linux-distros mailing list for major
> distributions to be able to fix their packages before general publication.
> The CRD above is the designated publication date.
> 
> @brogers: can you please prepare internal IBS submissions using the patches
> found in attachment 843022 [details]? Thank you.

Working on it. v0.20 based spice-vdagent package is prepared and basically ready to submit when embargo lifts. v0.19 based spice-vdagent package is being worked on.
Comment 22 Bruce Rogers 2020-11-03 19:08:50 UTC
I never saw a time on the Embaroged Date (today), but I see that the upstream vdagent project git repo has already included these patches. I assume that means the Embargo is lifted now, but I will wait for the security team to indicate so.

I've got the openSUSE side of things ready to submit and am evaluating older releases to see exactly what is needed. Quite a bit is different for those older releases so it is taking some time.

I do not see the reproducer code attached to this bug report. It may prove helpful in verifying the issue and fix for older releases. Could someone who has it provide it here?
Comment 23 Matthias Gerstner 2020-11-04 08:43:32 UTC
(In reply to brogers@suse.com from comment #22)
> I never saw a time on the Embaroged Date (today), but I see that the upstream vdagent project git repo has already included these patches. I assume that means the Embargo is lifted now, but I will wait for the security team to indicate so.

Yes, thank you for being prudent. We usually don't track exact embargo lifting
times since most of the time some upstream person is responsible for actually
publishing the information (and in different ways, too). Therefore we wait
until we get a notification from upstream or for some other sign of
publication before we lift the embargo on our end.

I can confirm that the patches are now public in the upstream repository. I
will therefore lift the embargo from our related bugs.

> I've got the openSUSE side of things ready to submit and am evaluating older
> releases to see exactly what is needed. Quite a bit is different for those
> older releases so it is taking some time.

You can submit the openSUSE updates now. Thank you for covering the
maintenance and backports. If you need help with backporting or reviewing
patches just say so.

> I do not see the reproducer code attached to this bug report. It may prove
> helpful in verifying the issue and fix for older releases. Could someone who
> has it provide it here?

I will attach the tarball to this bug in a jiffy.
Comment 24 Matthias Gerstner 2020-11-04 08:45:25 UTC
Created attachment 843289 [details]
tarball containing the reproducers for the issues described in the report in comment 15
Comment 25 Matthias Gerstner 2020-11-04 10:54:04 UTC
I published the security report now also on oss-sec [1].

[1]: https://www.openwall.com/lists/oss-security/2020/11/04/1
Comment 26 Swamp Workflow Management 2020-11-10 20:14:57 UTC
SUSE-SU-2020:3268-1: An update that solves four vulnerabilities and has one errata is now available.

Category: security (important)
Bug References: 1173749,1177780,1177781,1177782,1177783
CVE References: CVE-2020-25650,CVE-2020-25651,CVE-2020-25652,CVE-2020-25653
JIRA References: 
Sources used:
SUSE Linux Enterprise Module for Desktop Applications 15-SP2 (src):    spice-vdagent-0.19.0-3.3.1

NOTE: This line indicates an update has been released for the listed product(s). At times this might be only a partial fix. If you have questions please reach out to maintenance coordination.
Comment 27 Swamp Workflow Management 2020-11-11 11:17:14 UTC
openSUSE-SU-2020:1898-1: An update that solves four vulnerabilities and has one errata is now available.

Category: security (important)
Bug References: 1173749,1177780,1177781,1177782,1177783
CVE References: CVE-2020-25650,CVE-2020-25651,CVE-2020-25652,CVE-2020-25653
JIRA References: 
Sources used:
openSUSE Leap 15.2 (src):    spice-vdagent-0.19.0-lp152.2.3.1
Comment 28 Matthias Gerstner 2020-11-18 10:08:22 UTC
Removing the dependency to the VUL-0 bugs. They are now tracked by reactive security. The AUDIT is complete. Closing as FIXED.
Comment 30 Swamp Workflow Management 2021-08-05 14:18:36 UTC
openSUSE-SU-2021:2614-1: An update that solves four vulnerabilities and has one errata is now available.

Category: security (important)
Bug References: 1173749,1177780,1177781,1177782,1177783
CVE References: CVE-2020-25650,CVE-2020-25651,CVE-2020-25652,CVE-2020-25653
JIRA References: 
Sources used:
openSUSE Leap 15.3 (src):    spice-vdagent-0.21.0-3.3.1
Comment 31 Swamp Workflow Management 2021-08-05 14:26:44 UTC
SUSE-SU-2021:2614-1: An update that solves four vulnerabilities and has one errata is now available.

Category: security (important)
Bug References: 1173749,1177780,1177781,1177782,1177783
CVE References: CVE-2020-25650,CVE-2020-25651,CVE-2020-25652,CVE-2020-25653
JIRA References: 
Sources used:
SUSE Linux Enterprise Module for Desktop Applications 15-SP3 (src):    spice-vdagent-0.21.0-3.3.1

NOTE: This line indicates an update has been released for the listed product(s). At times this might be only a partial fix. If you have questions please reach out to maintenance coordination.