Hervé Godquin

Aller au contenu | Aller au menu | Aller à la recherche

[CVE-2019-6974][CVE-2019-7221] : Kernel - kvm : 2 UAF à corriger et combat pour le patching part.1

Bonjour à toutes et à tous,

Ce soir je viens vous parler du RHSA-2019:0818 (https://access.redhat.com/errata/RHSA-2019:0818) qui corrige, entre autre, deux vulnérabilités de types Use-After-Free à savoir : CVE-2019-6974 et CVE-2019-7221. Ces deux vulnérabilités sont assez intéressantes, et assez simples, à étudier car elles font l'objet toutes les deux de publications par projectzero.

Vous allez me dire : "tu triches du coup, tu reprends le travail d'autres !" : oui pour le coup j'avoue, mais cela fait parti d'un ensemble, nous allons voir ici l'exemple de deux vulnérabilités assez bien documentées mais je vous prépare un prochain article montrant comment il est difficile de faire prendre en compte la menace d'une vulnérabilité, notamment dans le cadre d'une campagne de patching.

La CVE-2019-7221

La description générale :

A use-after-free vulnerability was found in the way the Linux kernel's KVM hypervisor emulates a preemption timer for L2 guests when nested (=1) virtualization is enabled. This high resolution timer(hrtimer) runs when a L2 guest is active. After VM exit, the sync_vmcs12() timer object is stopped. The use-after-free occurs if the timer object is freed before calling sync_vmcs12() routine. A guest user/process could use this flaw to crash the host kernel resulting in a denial of service or, potentially, gain privileged access to a system.

La description sur projectzero est disponible ici : https://bugs.chromium.org/p/project-zero/issues/detail?id=1760

La CVE-2019-6974

La description générale :

A use-after-free vulnerability was found in the way the Linux kernel's KVM hypervisor implements its device control API. While creating a device via kvm_ioctl_create_device(), the device holds a reference to a VM object, later this reference is transferred to the caller's file descriptor table. If such file descriptor was to be closed, reference count to the VM object could become zero, potentially leading to a use-after-free issue. A user/process could use this flaw to crash the guest VM resulting in a denial of service issue or, potentially, gain privileged access to a system.

On va un peu plus d'attarder sur cette vulnérabilité car elle vous permttra d'appréhender mon prochain article. Qui sera un peu plus long je vous l'assure.

Comme expliquer dans l'article de projectzero le code posant problème est le suivant :

kvm_ioctl_create_device() contains the following code:

dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev)
return -ENOMEM;

dev->ops = ops;
dev->kvm = kvm;

mutex_lock(&kvm->lock);
ret = ops->create(dev, cd->type);
if (ret < 0) {
mutex_unlock(&kvm->lock);
kfree(dev);
return ret;
}
list_add(&dev->vm_node, &kvm->devices);
mutex_unlock(&kvm->lock);

if (ops->init)
ops->init(dev);

ret = anon_inode_getfd(ops->name, &kvm_device_fops, dev, O_RDWR | O_CLOEXEC);
if (ret < 0) {
mutex_lock(&kvm->lock);
list_del(&dev->vm_node);
mutex_unlock(&kvm->lock);
ops->destroy(dev);
return ret;
}

kvm_get_kvm(kvm);
cd->fd = ret;
 

De même on nous explique que :

This code:

1. creates a device that holds a reference to the VM object (with a borrowed
reference, the VM's refcount has not been bumped yet)
2. initializes the device
3. transfers the reference to the device to the caller's file descriptor table
4. calls kvm_get_kvm() to turn the borrowed reference to the VM into a real
reference

The ownership transfer in step 3 must not happen before the reference to the VM
becomes a proper, non-borrowed reference, which only happens in step 4.
After step 3, an attacker can close the file descriptor and drop the borrowed
reference, which can cause the refcount of the kvm object to drop to zero.

Oui je sais jusque là je me suis vraiment déchiré niveau éditorial :). Mais voilà on va rentrer dans le dur.

Alors le problème se situe donc quelque part par ici :

if (ops->init)

ops->init(dev);

ret = anon_inode_getfd(ops->name, &kvm_device_fops, dev, O_RDWR | O_CLOEXEC);

if (ret < 0) {

mutex_lock(&kvm->lock);

list_del(&dev->vm_node);

mutex_unlock(&kvm->lock);

Et ici :

return ret;

}

kvm_get_kvm(kvm);

cd->fd = ret;

return 0;

}

On peut donc constater qu'effectivement il manque quelques petites choses, et parfois des petites choses en trop :

Dans la première partie de code que je présente à aucun moment kvm_get_kvm() n'est appelé, il n'est appelé qu'à la fin du processus. De même aucun appel n'est fait vers kvm_put_kvm() fonction définit comme suit :

void kvm_put_kvm(struct kvm *kvm)
  {
  if (refcount_dec_and_test(&kvm->users_count))
  kvm_destroy_vm(kvm);
  }
 

EXPORT_SYMBOL_GPL(kvm_put_kvm);

Voilà cette vulnérabilité est assez "simple" à appréhender et à comprendre, d'ailleurs vous pouvez retrouver un exemple d'exploitation sur projectzero : https://bugs.chromium.org/p/project-zero/issues/detail?id=1765

Patching

Je ne vais pas vous présenter le patch vous pouvez le trouver vous même, mais vous parler de patching, en soit ce genre de vulnérabilité est sympathique, en effet, elle est simple à décrire, un PoC d'exploitation est présent. C'est publié par des personnes connues et tout le monde est d'accord sur l'impact, convaincre un responsable de production ou un resposable métier de pacher est bien plus facile avec ce genre de vulnérabilité, mais dans un prochain article à paraître (paru ici : https://www.herve-godquin.fr/index.php?post/2019/04/30/Etude-de-la-vuln%C3%A9rabilit%C3%A9-CVE-2019-10125) très bientôt vous verrez que ce n'est pas nécessairement le cas tout le temps.

Conclusion

Patchez !

Ajouter un commentaire

Le code HTML est affiché comme du texte et les adresses web sont automatiquement transformées.

La discussion continue ailleurs

URL de rétrolien : https://www.herve-godquin.fr/index.php?trackback/6

Fil des commentaires de ce billet