GitHub Advisoryadvisory · Go

GHSA-ggp9-c99x-54gp KubeVirt's Improper TLS Certificate Management Handling Allows API Identity Spoofing

MEDIUM Published 2025-11-06 Modified 2025-11-27 Official page ↗
Aliases
CVE-2025-64434
Ecosystem
Go
Packages
kubevirt.io/kubevirt
Severity
Moderate
CVSS vector
CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:N/I:N/A:H
Weaknesses
CWE-287
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 Due to improper TLS certificate management, a compromised `virt-handler` could impersonate `virt-api` by using its own TLS credentials, allowing it to initiate privileged operations against another `virt-handler`. Details _Give all details on the vulnerability. Pointing to the incriminated source code is very helpful for the maintainer._ Because of improper TLS certificate management, a compromised `virt-handler` instance can reuse its TLS bundle to impersonate `virt-api`, enabling unauthorized access to VM lifecycle operations on other `virt-handler` nodes. The `virt-api` component acts as a sub-resource server, and it proxies API VM lifecycle requests to `virt-handler` instances. The communication between `virt-api` and `virt-handler` instances is secured using mTLS. The former acts as a client while the latter as the server. The client certificate used by `virt-api` is defined in the source code as follows and have the following properties: ```go //pkg/virt-api/api.go const ( ... defaultCAConfigMapName = "kubevirt-ca" ... defaultHandlerCertFilePath = "/etc/virt-handler/clientcertificates/tls.crt" defaultHandlerKeyFilePath = "/etc/virt-handler/clientcertificates/tls.key" ) ``` ```bash verify virt-api's certificate properties from the docker container in which it is deployed using Minikube admin@minikube:~$ openssl x509 -text -in \ $(CID=$(docker ps --filter 'Name=virt-api' --format '{{.ID}}' | head -n 1) && \ docker inspect $CID | grep "clientcertificates:ro" | cut -d ":" -f1 | \ tr -d '"[:space:]')/tls.crt | \ grep -e "Subject:" -e "Issuer:" -e "Serial" Serial Number: 127940157512425330 (0x1c688e539091f72) Issuer: CN = kubevirt.io@1747579138 Subject: CN = kubevirt.io:system:client:virt-handler ``` The `virt-handler` component verifies the signature of client certificates using a self-signed root CA. This latter is generated by `virt-operator` when the KubeVirt stack is deployed and it is stored within a ConfigMap in the `kubevirt` namespace. This configmap is used as a trust anchor by all `virt-handler` instances to verify client certificates. ```bash inspect the self-signed root CA used to sign virt-api and virt-handler's certificates admin@minikube:~$ kubectl -n kubevirt get configmap kubevirt-ca -o jsonpath='{.data.ca-bundle}' | openssl x509 -text | grep -e "Subject:" -e "Issuer:" -e "Serial" Serial Number: 319368675363923930 (0x46ea01e3f7427da) Issuer: CN=kubevirt.io@1747579138 Subject: CN=kubevirt.io@1747579138 ``` The `kubevirt-ca` is also used to sign the server certificate which is used by a `virt-handler` instance: ```bash admin@minikube:~$ openssl x509 -text -in \ $(CID=$(docker ps --filter 'Name=virt-handler' --format '{{.ID}}' | head -n 1) && \ docker inspect $CID | grep "servercertificates:ro" | cut -d ":" -f1 | \ tr -d '"[:space:]')/tls.crt | \ grep -e "Subject:" -e "Issuer:" -e "Serial" the virt-handler's server ceriticate is issued by the same root CA Serial Number: 7584450293644921758 (0x6941615ba1500b9e) Issuer: CN = kubevirt.io@1747579138 Subject: CN = kubevirt.io:system:node:virt-handler ``` In addition to the validity of the signature, the `virt-handler` component also verifies the CN field of the presented certificate: ```go //pkg/util/tls/tls.go func SetupTLSForVirtHandlerServer(caManager ClientCAManager, certManager certificate.Manager, externallyManaged bool, clusterConfig *virtconfig.ClusterConfig) *tls.Config { // #nosec cause: InsecureSkipVerify: true // resolution: Neither the client nor the server should validate anything itself, `VerifyPeerCertificate` is still executed //... // XXX: We need to verify the cert ourselves because we don't have DNS or IP on the certs at the moment VerifyPeerCertificate: func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error { return verifyPeerCert(rawCerts, externallyManaged, certPool, x509.ExtKeyUsageClientAuth, "client") }, //... } func verifyPeerCert(rawCerts [][]byte, externallyManaged bool, certPool *x509.CertPool, usage x509.ExtKeyUsage, commonName string) error { //... rawPeer, rawIntermediates := rawCerts[0], rawCerts[1:] c, err := x509.ParseCertificate(rawPeer) //... fullCommonName := fmt.Sprintf("kubevirt.io:system:%s:virt-handler", commonName) if !externallyManaged && c.Subject.CommonName != fullCommonName { return fmt.Errorf("common name is invalid, expected %s, but got %s", fullCommonName, c.Subject.CommonName) } //... ``` The above code illustrates that client certificates accepted be KubeVirt should have as CN `kubevirt.io:system:client:virt-handler` which is the same as the CN present in the `virt-api`'s certificate. However, the latter is not the only component in the KubeVirt stack which can communicate with a `virt-handler` instance. In addition to the extension API server, any other `virt-handler` can communicate with it. This happens in the context of VM migration operations. When a VM is migrated from one node to another, the `virt-handler`s on both nodes are going to use structures called `ProxyManager` to communicate back and forth on the state of the migration. ```go //pkg/virt-handler/migration-proxy/migration-proxy.go func NewMigrationProxyManager(serverTLSConfig *tls.Config, clientTLSConfig *tls.Config, config *virtconfig.ClusterConfig) ProxyManager { return &migrationProxyManager{ sourceProxies: make(map[string][]*migrationProxy), targetProxies: make(map[string][]*migrationProxy), serverTLSConfig: serverTLSConfig, clientTLSConfig: clientTLSConfig, config: config, } } ``` This communication follows a classical client-server model, where the `virt-handler` on the migration source node acts as a client and the `virt-handler` on the migration destination node acts as a server. This communication is also secured using mTLS. The server certificate presented by the `virt-handler` acting as a migration destination node is the same as the one which is used for the communication between the same `virt-handler` and the `virt-api` in the context of VM lifecycle operations (`CN=kubevirt.io:system:node:virt-handler`). However, the client certificate which is used by a `virt-handler` instance has the same CN as the client certificate used by `virt-api`. ```bash admin@minikube:~$ openssl x509 -text -in $(CID=$(docker ps --filter 'Name=virt-handler' --format '{{.ID}}' | head -n 1) && docker inspect $CID | grep "clientcertificates:ro" | cut -d ":" -f1 | tr -d '"[:space:]')/tls.crt | grep -e "Subject:" -e "Issuer:" -e "Serial" Serial Number: 2951695854686290384 (0x28f687bdb791c1d0) Issuer: CN = kubevirt.io@1747579138 Subject: CN = kubevirt.io:system:client:virt-handler ``` Although the migration procedure, where two separate `virt-handler` instances coordinate the transfer of a VM's state, is not directly tied to the communication between `virt-api` and `virt-handler` during VM lifecycle management, there is a critical overlap in the TLS authentication mechanism. Specifically, the client certificate used by both `virt-handler` and `virt-api` shares the same CN field, despite the use of different, randomly allocated ports, for the two types of communication. PoC _Complete instructions, including specific configuration details, to reproduce the vulnerability._ To illustrate the vulnerability, a Minikube cluster has been deployed with two nodes (`minikube` and `minikube-m02`) thus, with two `virt-handler` instances alongside a vmi running on one of the nodes. It is considered that an attacker has obtained access to the client certificate bundle used by the `virt-handler` instance running on the compromised node (`minikube`) while the virtual machine is running on the other node (`minikube-m02`). Thus, they can interact with the sub-resource API exposed by the other `virt-handler` instance and control the lifecycle of the VMs running on the other node: ```yaml the deployed VMI on the non-compromi…

Related

Alias · CVE (1)

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

Weakness · CWE (1)

  • CWE-287 Improper Authentication weakness

References