ASA-2019-00084 – Linux: Use-after-free usando temporizador de preempção vmx emulado


For the English version of this alert, click here.

Allele Security Alert

ASA-2019-00084

Identificador(es)

ASA-2019-00084, CVE-2019-7221

Título

Use-after-free usando temporizador de preempção vmx emulado

Fabricante(s)

The Linux foundation

Produto(s)

Linux

Versão(ões) afetada(s)

Desconhecido

Versão(ões) corrigida(s)

Linux kernel com o seguinte commit:

KVM: nVMX: unconditionally cancel preemption timer in free_nested (CVE-2019-7221)
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=ecec76885bcfe3294685dc363fd1273df0d5d65f

Linux kernel 4.20.8
Linux kernel 4.19.21
Linux kernel 4.14.99
Linux kernel 4.9.156

Prova de conceito

Desconhecido

Descrição

Uma vulnerabilidade de use-after-free foi encontrada na maneira como o hypervisor KVM do kernel do Linux emula um temporizador de preempção para convidados L2 quando a virtualização aninhada (=1) é ativada. Este timer de alta resolução (hrtimer) é executado quando um convidado L2 está ativo. Após a saída da VM, o objeto do cronômetro sync_vmcs12() é interrompido. O use-after-free ocorre se o objeto timer for liberado antes de chamar a rotina sync_vmcs12(). Um usuário convidado/processo poderia usar essa falha para travar o kernel do host, resultando em uma negação de serviço ou, potencialmente, obter acesso privilegiado a um sistema.

Detalhes técnicos

O KVM emula temporizadores de preempção de VMX para convidados de L2 usando um timer de alta resolução chamado preemption_timer. O temporizador é armazenado na estrutura nested_vmx que é um membro de vcpu_vmx.

Se um hypervisor L1 usar esse recurso para um convidado L2, o timer será iniciado logo antes de entrar no convidado L2 em nested_vmx_run->enter_vmx_non_root_mode->prepare_vmcs02->vmx_start_preemption_timer:

static void vmx_start_preemption_timer(struct kvm_vcpu *vcpu)
{
u64 preemption_timeout = get_vmcs12(vcpu)->vmx_preemption_timer_value;
struct vcpu_vmx *vmx = to_vmx(vcpu);
… 
preemption_timeout <<= VMX_MISC_EMULATED_PREEMPTION_TIMER_RATE; preemption_timeout *= 1000000; do_div(preemption_timeout, vcpu->arch.virtual_tsc_khz);
hrtimer_start(&vmx->nested.preemption_timer,
ns_to_ktime(preemption_timeout), HRTIMER_MODE_REL);
}

Para garantir que o timer seja executado somente enquanto L2 estiver ativo, o KVM chamará hrtimer_cancel em sync_vmc12, que é chamado após uma saída da VM L2:

static void sync_vmcs12(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12)
{
...
if (nested_cpu_has_preemption_timer(vmcs12)) {
if (vmcs12->vm_exit_controls &
VM_EXIT_SAVE_VMX_PREEMPTION_TIMER)
vmcs12->vmx_preemption_timer_value =
vmx_get_preemption_timer_value(vcpu);
hrtimer_cancel(&to_vmx(vcpu)->nested.preemption_timer);
}

Não há locking no local para garantir que a estrutura kvm_vcpu, que armazena preemption_timer, não seja liberada enquanto o cronômetro estiver ativo. Isso significa que um atacante que pode fazer uma chamada para vmx_free_vcpu sem chamar sync_vmcs12 pode acionar um altamente explorável use-after-free (ponteiro de função controlada + primeiro argumento apontando para dados controlados pelo atacante).

Acredito que há várias maneiras de fazer isso, mas uma abordagem possível é acionar uma falha na entrada da VM em enter_vmx_non_root_mode após a chamada para o timer vmx_start_preemption:

O KVM reimplementa a maioria das verificações de entrada de VM no software e as chama antes de enter_vmx_non_root_mode. No entanto, falhas de entrada devido a um endereço de carregamento MSR inválido acontecem depois que prepare_vmcs02 invoca vmx_start_preemption_timer:

if (prepare_vmcs02(vcpu, vmcs12, from_vmentry ? exit_qual : &dummy_exit_qual))
goto fail;

if (from_vmentry) {
nested_get_vmcs12_pages(vcpu);

r = EXIT_REASON_MSR_LOAD_FAIL;
*exit_qual = nested_vmx_load_msr(vcpu,
vmcs12->vm_entry_msr_load_addr,
vmcs12->vm_entry_msr_load_count);
if (*exit_qual)
goto fail;

Créditos

Felix Wilhelm (Google Project Zero)

Referência(s)

Issue 1760: KVM: use-after-free using emulated vmx preemption timer
https://bugs.chromium.org/p/project-zero/issues/detail?id=1760

Bug 1671904 (CVE-2019-7221) – CVE-2019-7221 Kernel: KVM: nVMX: use-after-free of the hrtimer for emulation of the preemption timer
https://bugzilla.redhat.com/show_bug.cgi?id=1671904

KVM: nVMX: unconditionally cancel preemption timer in free_nested (CVE-2019-7221)
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=ecec76885bcfe3294685dc363fd1273df0d5d65f

ChangeLog-4.20.8
https://cdn.kernel.org/pub/linux/kernel/v4.x/ChangeLog-4.20.8

ChangeLog-4.19.21
https://cdn.kernel.org/pub/linux/kernel/v4.x/ChangeLog-4.19.21

ChangeLog-4.14.99
https://cdn.kernel.org/pub/linux/kernel/v4.x/ChangeLog-4.14.99

ChangeLog-4.9.156
https://cdn.kernel.org/pub/linux/kernel/v4.x/ChangeLog-4.9.156

CVE-2019-7221
https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-7221

CVE-2019-7221
https://nvd.nist.gov/vuln/detail/CVE-2019-7221

Se encontrou algum erro neste alerta ou deseja uma análise compreensiva, entre em contato.

Última modificação: 19 fevereiro 2019

Não somos responsáveis por qualquer perda de dados, corrupção de dispositivos ou qualquer outro tipo de problema devido ao uso de qualquer informação mencionada em nossos alertas de segurança.