Exploit-DB #41973
Description: Xen 64bit PV Guest - pagetable use-after-type-change Breakout
Type: local
Platform: linux
Date: 2017-05-08
Author: Google Security Research

Exploit:
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: [PII_REDACTED]   CONTEXT: hypervisor (d1v2)
(XEN) rax: [PII_REDACTED]   rbx: [PII_REDACTED]   rcx: [PII_REDACTED]
(XEN) rdx: [PII_REDACTED]   rsi: 00007ffe98b5e248   rdi: [PII_REDACTED]
(XEN) rbp: [PII_REDACTED]   rsp: ffff8301abb37f30   r8:  [PII_REDACTED]
(XEN) r9: [PII_REDACTED]00001b   r10: [PII_REDACTED]   r11: [PII_REDACTED]
(