Friday, April 7, 2017

Pandavirtualization: Exploiting cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Xen hypervisor

Posted by Jann Horn, Project Zero

On 2017-03-14, I reported a bug to Xen's security team that permits an attacker with control over cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel of a paravirtualized x86-64 Xen guest to break out of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 hypervisor and gain full control over cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 machine's physical memory. The Xen Project publicly released an advisory and a patch for this issue 2017-04-04.

To demonstrate cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 impact of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 issue, I created an exploit that, when executed in one 64-bit PV guest with root privileges, will execute a shell command as root in all ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r 64-bit PV guests (including dom0) on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same physical machine.

Background

access_ok()

On x86-64, Xen PV guests share cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 virtual address space with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 hypervisor. The coarse memory layout looks as follows:


Xen allows cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 guest kernel to perform hypercalls, which are essentially normal system calls from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 guest kernel to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 hypervisor using cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 System V AMD64 ABI. They are performed using cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 syscall instruction, with up to six arguments passed in registers. Like normal syscalls, Xen hypercalls often take guest pointers as arguments. Because cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 hypervisor shares its address space, it makes sense for guests to simply pass in guest-virtual pointers.

Like any kernel, Xen has to ensure that guest-virtual pointers don't actually point to hypervisor-owned memory before dereferencing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m. It does this using userspace accessors that are similar to those in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Linux kernel; for example:

  • access_ok(addr, size) for checking whecá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r a guest-supplied virtual memory range is safe to access - in ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r words, it checks that accessing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 memory range will not modify hypervisor memory
  • __copy_to_guest(hnd, ptr, nr) for copying nr bytes from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 hypervisor address ptr to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 guest address hnd without checking whecá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r hnd is safe
  • copy_to_guest(hnd, ptr, nr) for copying nr bytes from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 hypervisor address ptr to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 guest address hnd if hnd is safe

In cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Linux kernel, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 macro access_ok() checks whecá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 whole memory range from addr to addr+size-1 is safe to access, using any memory access pattern. However, Xen's access_ok() doesn't guarantee that much:

/*
* Valid if in +ve half of 48-bit address space, or above Xen-reserved area.
* This is also valid for range checks (addr, addr+size). As long as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365
* start address is outside cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Xen-reserved area cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n we will access a
* non-canonical address (and thus fault) before ever reaching VIRT_START.
*/
#define __addr_ok(addr) \
   (((unsigned long)(addr) < (1UL<<47)) || \
    ((unsigned long)(addr) >= HYPERVISOR_VIRT_END))

#define access_ok(addr, size) \
   (__addr_ok(addr) || is_compat_arg_xlat_range(addr, size))

Xen normally only checks that addr points into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 userspace area or cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel area without checking size. If cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 actual guest memory access starts roughly at addr, proceeds linearly without skipping gigantic amounts of memory and bails out as soon as a guest memory access fails, only checking addr is sufficient because of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 large range of non-canonical addresses, which serve as a large guard area. However, if a hypercall wants to access a guest buffer starting at a 64-bit offset, it needs to ensure that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 access_ok() check is performed using cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 correct offset - checking cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 whole userspace buffer is unsafe!

Xen provides wrappers around access_ok() for accessing arrays in guest memory. If you want to check whecá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r it's safe to access an array starting at element 0, you can use guest_handle_okay(hnd, nr). However, if you want to check whecá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r it's safe to access an array starting at a different element, you need to use guest_handle_subrange_okay(hnd, first, last).

When I saw cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 definition of access_ok(), cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 lack of security guarantees actually provided by access_ok() seemed racá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r unintuitive to me, so I started searching for its callers, wondering whecá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r anyone might be using it in an unsafe way.

Hypercall Preemption

When e.g. a scheduler tick happens, Xen needs to be able to quickly switch from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 currently executing vCPU to anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r VM's vCPU. However, simply interrupting cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 execution of a hypercall won't work (e.g. because cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 hypercall could be holding a spinlock), so Xen (like ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r operating systems) needs some mechanism to delay cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 vCPU switch until it's safe to do so.

In Xen, hypercalls are preempted using voluntary preemption: Any long-running hypercall code is expected to regularly call hypercall_preempt_check() to check whecá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 scheduler wants to schedule to anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r vCPU. If this happens, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 hypercall code exits to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 guest, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365reby signalling to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 scheduler that it's safe to preempt cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 currently-running task, after adjusting cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 hypercall arguments (in guest registers or guest memory) so that as soon as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 current vCPU is scheduled again, it will re-enter cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 hypercall and perform cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 remaining work. Hypercalls don't distinguish between normal hypercall entry and hypercall re-entry after preemption.

This hypercall re-entry mechanism is used in Xen because Xen does not have one hypervisor stack per vCPU; it only has one hypervisor stack per physical core. This means that while ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r operating systems (e.g. Linux) can simply leave cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 state of an interrupted syscall on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel stack, Xen can't do that as easily.

This design means that for some hypercalls, to allow cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m to properly resume cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir work, additional data is stored in guest memory that could potentially be manipulated by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 guest to attack cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 hypervisor.

memory_exchange()

The hypercall HYPERVISOR_memory_op(XENMEM_exchange, arg) invokes cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 function memory_exchange(arg) in xen/common/memory.c. This function allows a guest to "trade in" a list of physical pages that are currently assigned to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 guest in exchange for new physical pages with different restrictions on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir physical contiguity. This is useful for guests that want to perform DMA because DMA requires physically contiguous buffers.

The hypercall takes a struct xen_memory_exchange as argument, which is defined as follows:

struct xen_memory_reservation {
   /* [...] */
   XEN_GUEST_HANDLE(xen_pfn_t) extent_start; /* in: physical page list */

   /* Number of extents, and size/alignment of each (2^extent_order pages). */
   xen_ulong_t    nr_extents;
   unsigned int   extent_order;

   /* XENMEMF flags. */
   unsigned int   mem_flags;

   /*
    * Domain whose reservation is being changed.
    * Unprivileged domains can specify only DOMID_SELF.
    */
   domid_t        domid;
};

struct xen_memory_exchange {
   /*
    * [IN] Details of memory extents to be exchanged (GMFN bases).
    * Note that @in.address_bits is ignored and unused.
    */
   struct xen_memory_reservation in;

   /*
    * [IN/OUT] Details of new memory extents.
    * We require that:
    *  1. @in.domid == @out.domid
    *  2. @in.nr_extents  << @in.extent_order ==
    *     @out.nr_extents << @out.extent_order
    *  3. @in.extent_start and @out.extent_start lists must not overlap
    *  4. @out.extent_start lists GPFN bases to be populated
    *  5. @out.extent_start is overwritten with allocated GMFN bases
    */
   struct xen_memory_reservation out;

   /*
    * [OUT] Number of input extents that were successfully exchanged:
    *  1. The first @nr_exchanged input extents were successfully
    *     deallocated.
    *  2. The corresponding first entries in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 output extent list correctly
    *     indicate cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 GMFNs that were successfully exchanged.
    *  3. All ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r input and output extents are untouched.
    *  4. If not all input exents are exchanged cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 return code of this
    *     command will be non-zero.
    *  5. THIS FIELD MUST BE INITIALISED TO ZERO BY THE CALLER!
    */
   xen_ulong_t nr_exchanged;
};

The fields that are relevant for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bug are in.extent_start, in.nr_extents, out.extent_start, out.nr_extents and nr_exchanged.

nr_exchanged is documented as always being initialized to zero by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 guest - this is because it is not only used to return a result value, but also for hypercall preemption. When memory_exchange() is preempted, it stores its progress in nr_exchanged, and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 next execution of memory_exchange() uses cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 value of nr_exchanged to decide at which point in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 input arrays in.extent_start and out.extent_start it should resume.

Originally, memory_exchange() did not check cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 userspace array pointers at all before accessing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m with __copy_from_guest_offset() and __copy_to_guest_offset(), which do not perform any checks cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365mselves - so by supplying hypervisor pointers, it was possible to cause Xen to read from and write to hypervisor memory - a pretty severe bug. This was discovered in 2012 (XSA-29, CVE-2012-5513) and fixed as follows (https://xenbits.xen.org/xsa/xsa29-4.1.patch):

diff --git a/xen/common/memory.c b/xen/common/memory.c
index 4e7c234..59379d3 100644
--- a/xen/common/memory.c
+++ b/xen/common/memory.c
@@ -289,6 +289,13 @@ static long memory_exchange(XEN_GUEST_HANDLE(xen_memory_exchange_t) arg)
        goto fail_early;
    }
+    if ( !guest_handle_okay(exch.in.extent_start, exch.in.nr_extents) ||
+         !guest_handle_okay(exch.out.extent_start, exch.out.nr_extents) )
+    {
+        rc = -EFAULT;
+        goto fail_early;
+    }
+
    /* Only privileged guests can allocate multi-page contiguous extents. */
    if ( !multipage_allocation_permitted(current->domain,
                                         exch.in.extent_order) ||

The bug

As can be seen in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following code snippet, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 64-bit resumption offset nr_exchanged, which can be controlled by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 guest because of Xen's hypercall resumption scheme, can be used by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 guest to choose an offset from out.extent_start at which cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 hypervisor should write:

static long memory_exchange(XEN_GUEST_HANDLE_PARAM(xen_memory_exchange_t) arg)
{
   [...]

   /* Various sanity checks. */
   [...]

   if ( !guest_handle_okay(exch.in.extent_start, exch.in.nr_extents) ||
        !guest_handle_okay(exch.out.extent_start, exch.out.nr_extents) )
   {
       rc = -EFAULT;
       goto fail_early;
   }

   [...]

   for ( i = (exch.nr_exchanged >> in_chunk_order);
         i < (exch.in.nr_extents >> in_chunk_order);
         i++ )
   {
       [...]
       /* Assign each output page to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 domain. */
       for ( j = 0; (page = page_list_remove_head(&out_chunk_list)); ++j )
       {
           [...]
           if ( !paging_mode_translate(d) )
           {
               [...]
               if ( __copy_to_guest_offset(exch.out.extent_start,
                                           (i << out_chunk_order) + j,
                                           &mfn, 1) )
                   rc = -EFAULT;
           }
       }
       [...]
   }
   [...]
}

However, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 guest_handle_okay() check only checks whecá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r it would be safe to access cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 guest array exch.out.extent_start starting at offset 0; guest_handle_subrange_okay() would have been correct. This means that an attacker can write an 8-byte value to an arbitrary address in hypervisor memory by choosing:

  • exch.in.extent_order and exch.out.extent_order as 0 (exchanging page-sized blocks of physical memory for new page-sized blocks)
  • exch.out.extent_start and exch.nr_exchanged so that exch.out.extent_start points to userspace memory while exch.out.extent_start+8*exch.nr_exchanged points to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 target address in hypervisor memory, with exch.out.extent_start close to NULL; this can be calculated as exch.out.extent_start=target_addr%8, exch.nr_exchanged=target_addr/8.
  • exch.in.nr_extents and exch.out.nr_extents as exch.nr_exchanged+1
  • exch.in.extent_start as input_buffer-8*exch.nr_exchanged (where input_buffer is a legitimate guest kernel pointer to a physical page number that is currently owned by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 guest). This is guaranteed to always point to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 guest userspace range (and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365refore pass cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 access_ok() check) because exch.out.extent_start roughly points to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 start of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 userspace address range and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 hypervisor and guest kernel address ranges togecá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r are only as big as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 userspace address range.

The value that is written to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 attacker-controlled address is a physical page number (physical address divided by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 page size).

Exploiting cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bug: Gaining pagetable control

Especially on a busy system, controlling cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 page numbers that are written by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel might be difficult. Therefore, for reliable exploitation, it makes sense to treat cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bug as a primitive that permits repeatedly writing 8-byte values at controlled addresses, with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 most significant bits being zeroes (because of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 limited amount of physical memory) and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 least significant bits being more or less random. For my exploit, I decided to treat this primitive as one that writes an essentially random byte followed by seven bytes of garbage.

It turns out that for an x86-64 PV guest, such a primitive is sufficient for reliably exploiting cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 hypervisor for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following reasons:

  • x86-64 PV guests know cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 real physical page numbers of all pages cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y can access
  • x86-64 PV guests can map live pagetables (from all four paging levels) belonging to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir domain as readonly; Xen only prevents mapping cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m as writable
  • Xen maps all physical memory as writable at 0xffff830000000000 (in ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r words, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 hypervisor can write to any physical page, independent of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 protections using which it is mapped in ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r places, by writing to physical_address+0xffff830000000000).

The goal of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 attack is to point an entry in a live level 3 pagetable (which I'll call "victim pagetable") to a page to which cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 guest has write access (which I'll call "fake pagetable"). This means that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 attacker has to write an 8-byte value, containing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 physical page number of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 fake pagetable and some flags, into an entry in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 victim pagetable, and ensure that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following 8-byte pagetable entry stays disabled (e.g. by setting cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 first byte of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following entry to zero). Essentially, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 attacker has to write 9 controlled bytes followed by 7 bytes that don't matter.

Because cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 physical page numbers of all relevant pages and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 address of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 writable mapping of all physical memory are known to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 guest, figuring out where to write and what value to write is easy, so cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 only remaining problem is how to use cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 primitive to actually write data.

Because cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 attacker wants to use cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 primitive to write to a readable page, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 "write one random byte followed by 7 bytes of garbage" primitive can easily be converted to a "write one controlled byte followed by 7 bytes of garbage" primitive by repeatedly writing a random byte and reading it back until cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 value is right. Then, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 "write one controlled byte followed by 7 bytes of garbage" primitive can be converted to a "write controlled data followed by 7 bytes of garbage" primitive by writing bytes to consecutive addresses - and that's exactly cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 primitive needed for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 attack.

At this point, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 attacker can control a live pagetable, which allows cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 attacker to map arbitrary physical memory into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 guest's virtual address space. This means that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 attacker can reliably read from and write to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 memory, both code and data, of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 hypervisor and all ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r VMs on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 system.

Running shell commands in ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r VMs

At this point, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 attacker has full control over cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 machine, equivalent to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 privilege level of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 hypervisor, and can easily steal secrets by searching through physical memory; and a realistic attacker probably wouldn't want to inject code into VMs, considering how much more detectable that makes an attack.

But running an arbitrary shell command in ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r VMs makes cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 severity more obvious (and it looks cooler), so for fun, I decided to continue my exploit so that it injects a shell command into all ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r 64-bit PV domains.

As a first step, I wanted to reliably gain code execution in hypervisor context. Given cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ability to read and write physical memory, one relatively OS- (or hypervisor-)independent way to call an arbitrary address with kernel/hypervisor privileges is to locate cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Interrupt Descriptor Table using cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 unprivileged SIDT instruction, write an IDT entry with DPL 3 and raise cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 interrupt. (Intel's upcoming Cannon Lake CPUs are apparently going to support User-Mode Instruction Prevention (UMIP), which will finally make SIDT a privileged instruction.) Xen supports SMEP and SMAP, so it isn't possible to just point cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 IDT entry at guest memory, but using cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ability to write pagetable entries, it is possible to map a guest-owned page with hypervisor-context shellcode as non-user-accessible, which allows it to run despite SMEP.

Then, in hypervisor context, it is possible to hook cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 syscall entry point by reading and writing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 IA32_LSTAR MSR. The syscall entry point is used both for syscalls from guest userspace and for hypercalls from guest kernels. By mapping an attacker-controlled page into guest-user-accessible memory, changing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 register state and invoking sysret, it is possible to divert cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 execution of guest userspace code to arbitrary guest user shellcode, independent of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 hypervisor or cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 guest operating system.

My exploit injects shellcode into all guest userspace processes that is invoked on every write() syscall. Whenever cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 shellcode runs, it checks whecá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r it is running with root privileges and whecá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r a lockfile doesn't exist in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 guest's filesystem yet. If cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se conditions are fulfilled, it uses cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 clone() syscall to create a child process that runs an arbitrary shell command.

(Note: My exploit doesn't clean up after itself on purpose, so when cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 attacking domain is shut down later, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 hooked entry point will quickly cause cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 hypervisor to crash.)

Here is a screenshot of a successful attack against Qubes OS 3.2, which uses Xen as its hypervisor. The exploit is executed in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 unprivileged domain "test124"; cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 screenshot shows that it injects code into dom0 and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 firewallvm:



Conclusion

I believe that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 root cause of this issue were cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 weak security guarantees made by access_ok(). The current version of access_ok() was committed in 2005, two years after cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 first public release of Xen and long before cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 first XSA was released. It seems like old code tends to contain relatively straightforward weaknesses more often than newer code because it was committed with less scrutiny regarding security issues, and such old code is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n often left alone.

When security-relevant code is optimized based on assumptions, care must be taken to reliably prevent those assumptions from being violated. access_ok() actually used to check whecá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 whole range overlaps hypervisor memory, which would have prevented this bug from occurring. Unfortunately, in 2005, a commit with "x86_64 fixes/cleanups" was made that changed cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 behavior of access_ok() on x86_64 to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 current one. As far as I can tell, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 only reason this didn't immediately make cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 MEMOP_increase_reservation and MEMOP_decrease_reservation hypercalls vulnerable is that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 nr_extents argument of do_dom_mem_op() was only 32 bits wide - a relatively brittle defense.

While cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re have been several Xen vulnerabilities that only affected PV guests because cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 issues were in code that is unnecessary when dealing with HVM guests, I believe that this isn't one of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m. Accessing guest virtual memory is much more straightforward for PV guests than for HVM guests: For PV guests, raw_copy_from_guest() calls copy_from_user(), which basically just does a bounds check followed by a memcpy with pagefault fixup - cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same thing normal operating system kernels do when accessing userspace memory. For HVM guests, raw_copy_from_guest() calls copy_from_user_hvm(), which has to do a page-wise copy (because cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 memory area might be physically non-contiguous and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 hypervisor doesn't have a contiguous virtual mapping of it) with guest pagetable walks (to translate guest virtual addresses to guest physical addresses) and guest frame lookups for every page, including reference counting, mapping guest pages into hypervisor memory and various checks to e.g. prevent HVM guests from writing to readonly grant mappings. So for HVM, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 complexity of handling guest memory accesses is actually higher than for PV.

For security researchers, I think that a lesson from this is that paravirtualization is not much harder to understand than normal kernels. If you've audited kernel code before, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 hypercall entry path (lstar_enter and int80_direct_trap in xen/arch/x86/x86_64/entry.S) and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 basic design of hypercall handlers (for x86 PV: listed in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pv_hypercall_table in xen/arch/x86/pv/hypercall.c) should look more or less like normal syscalls.