首页
社区
课程
招聘
[原创]android kernel exploit第二弹
发表于: 2020-3-27 15:10 5673

[原创]android kernel exploit第二弹

2020-3-27 15:10
5673

这个漏洞是对类中某指针函数的覆盖,从而在调用该指针函数时劫持,从而执行任意代码

结构体在commandhandler.h文件中,如下:

首先看下源码的问题,漏洞出在write_to_vuln_device函数:

这个函数会使用copy_from_user从buf中读取CommandHandler大小的字节地址到handler的地址中,而这个handler其实也是个CommandHandler结构体,且其内部函数指针指向的函数功能只是返回0。

那利用思路就比较明显了,如果我们传入的buf的头四个字节是自己设定好的地址的话,那么我们就可以在handler的runHandler函数被调用时,就可以将程序流劫持了。

那是在哪里触发的呢?程序中有这样的一段代码:

可以看到当我们在对该模块(即cmd_handler)调用write,ioctl函数时,该模块就会将这两个函数替换成自己的漏洞函数了,也就是说我们可以先调用write函数,使其触发write_to_vuln_device去覆盖runhandler地址(这里说一点能覆盖的原因是结构体的首地址和第一项内容的起始地址相同,且大小相同),然后再调用unlocked_ioctl触发调用runHandler(kernel 2.6.36 中已经完全删除了struct file_operations 中的ioctl 函数指针,取而代之的是unlocked_ioctl)

看下exp的核心部分,这里的getsymbol函数即打开/proc/kallsyms去获取相应字符串的地址而已:

调试过程如下,省略部分步骤,分析一同写在里面了。
先挂起gdb,然后执行./build_and_run.sh程序,注意由于我们exp需要commit_creds和prepare_kernel_cred地址,所以可以用adb shell进去echo 0 > /proc/sys/kernel/kptr_restrict, 同时关闭随机化echo 0 > /proc/sys/kernel/randomize_va_space

在几个重要函数下断点,并来到write_to_vuln_device,此时对应着exp是执行到了write这一句

可以发现程序用来覆盖的值应该是0x8335,也就是run_handler()的函数地址,但是现在无法查看

步进到copy_from_user,查看填入地址和其内容发现确实是handler和runhandler,且此时from地址的值就是0x00008335

来到device_ioctl,此时对应着exp应该是调用了ioctl函数所以触发到这里,查看runhandler地址已被覆盖为0x00008335,同时查看传入的cmd为0x1337正好对应着RUN_COMMAND_HANDLER的值,所以接下来会触发handler.runhandler()

步进到handler.runHandler()后查看下其汇编函数,发现pc即将跳转到寄存器r3指向的地址,查看下r3的值,发现就是我们劫持填入的地址,这里直接看0x8335的内容识别不了,但可以根据在exp中打印出我们所写的提权函数run_handler的地址,发现也是0x8335,因此可以得知跳转到了我们的函数。(当然也可以尝试下反汇编,这里我就不试了)

最终利用成功如下

这题的漏洞是栈溢出,但是要绕过canary,这题提供了一种绕过canary的方法。

原理是由于proc接口就像提供了一个文件的接口,因此可以对proc目录下的打开的文件描述符调用lseek函数,从而导致传递给csaw_read函数的off参数可以是非零值。通过这个属性我们可以在进程中dump出canary的值后将它填入具体位置绕过。利用如下,具体的偏移值(如canary和ret的距离)可以通过动态调试确定:

这题很迷。。。调到后面才发现结构体不一样。。。但还是学到些东西的

简单说下原理:在Linux上,系统上的每个线程都在内核内存中分配了相应的内核堆栈。 x86上的Linux内核堆栈的大小为4096或8192字节。尽管此大小似乎很小,无法包含完整的调用链和相关的本地堆栈变量,但实际上内核调用链相对较浅,当使用高效的分配器(如SLUB)时,不鼓励内核函数滥用带有大局部堆栈变量的宝贵空间。可用。堆栈与thread_info结构共享4k / 8k的总大小,该结构包含有关当前线程的一些元数据,如include/linux/sched.h中所示:

根据题目的提示的信息,我们可以对应到linux v2.6.39.4的版本
然后在x86下的thread_info的结构是这样的:

由于内核内存空间有限,当内核中的函数需要超过4k / 8k的堆栈空间或长调用链超出可用堆栈空间时,那么就会发生堆栈溢出,并且如果thread_info结构或超出其的关键内存损坏,则常会导致内核崩溃。但是如果可以对齐其结构体,并且存在实际可以控制写入堆栈及其以外的数据的情况,则可能存在可利用的条件。
这里的话,可以将restart_block作为一个攻击切入点, restart_block是每个线程的结构,用于跟踪信息和参数以重新启动系统调用。如果在sigaction 中指定了SA_RESTART,则被信号中断的系统调用可以中止并返回EINTR,也可以自行重启。在include/linux/thread_info.h中,restart_block的定义如下:

需要关注的是第一个函数指针,先来看下是否可以触发这个指针,在kernel/signal.c中发现可以通过restart_syscall函数来调用restart_block的fn指向的函数

而在arch/x86/kernel/syscall_table_32.S中定义了restart_syscall的系统调用号:

也就是说我们可以通过syscall(0)就能触发到这个fn指向的函数了,那如果可以破坏thread_info的restart_block成员中的函数指针,将其指向我们控制下的用户空间中的函数,那么就可以劫持程序流了。

以上就是大致的原理部分,程序本身是一个加密解密模块,这不是我们关注的重点,重点在于我们是否能够在内核空间将使用地址覆盖到thread_info,直接分配大空间内存在这里无法实现,所以考虑不断分配空间的方式,即找寻递归函数,在decrypt函数我们看到了一个decrypt自身的递归调用,比较明显的是round的值代表了控制解密的轮数,在这里也就是分配栈帧的次数,如果我们可以控制这个round为一个合适的较大值,那么我们就可以覆盖到thread_info了

而这个round值在key_write函数中被赋值,可以看到从copy_from_user获取用户的输入,然后按照格式化字符串的填入key和rounds。

填充rounds和触发的函数如下, 就是打开相应模块然后触发各自实现的读入函数(读入函数这里就不放了,类似于第一个案例的实现):

当然有点无语的是具体shellcode填充,给了x86架构的exp,然而放在androidkernel的题库里。。。shellcode就是类似于jop的思路

当我费心改成arm架构的机器码挂上去调试的时候,却突然发现不同架构的thread_info的结构体都不一样 :)

在linux之前的内核中,还没有lx的辅助调试选项,所以查看thread_info的方式要稍麻烦些
首先我们需要根据栈地址拿到thread_info的地址,根据上文thread_union结构体可知可以通过程序的局部变量的地址(&retval)获得内核栈的地址。又因为thread_info 位于内核栈顶部而且是 8k(或者 4k )对齐的,所以利用 栈地址 & (~(THREAD_SIZE - 1)) 就可以计算出 thread_info 的地址。

而THREAD_SIZE的定义在thread_info.h中, 下面是arm架构的THREAD_SIZE定义,THREAD_SIZE_ORDER和PAGE_SIZE根据架构有所不同,这里由于是arm 32位,所以这里的 THREAD_SIZE = 4096 * 2 = 0x2000

就是在调试时才发现原来thread_info的结构体也不一样,猝。。。

又是一道在x86下的kernel exploit :) 不知为何又出现在了这个库里,又是给了一个x86的exp,绝望了,打算再去巩固下驱动和内核的知识再来分析了。。。


[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 4
支持
分享
最新回复 (5)
雪    币: 14530
活跃值: (17548)
能力值: ( LV12,RANK:290 )
在线值:
发帖
回帖
粉丝
2
mark,楼主辛苦了
问个新手问题,有些安卓的app并没有直接提供apk文件,而是要求用户在play store里下载,该如何提取出apk文件呢??虚拟机里能直接使用play store么??(我的手机不能用play store)
最后于 2020-3-27 16:13 被pureGavin编辑 ,原因: 错字
2020-3-27 15:38
0
雪    币: 1420
活跃值: (2171)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
3
pureGavin mark,楼主辛苦了问个新手问题,有些安卓的app并没有直接提供apk文件,而是要求用户在play store里下载,该如何提取出apk文件呢??虚拟机里能直接使用play store么??(我的手机 ...
一般来讲干这个 不得 搭梯子么? 我记得好像有个专门的apk网站 。收录了绝大多数的apk文件
2020-3-28 09:29
0
雪    币: 14530
活跃值: (17548)
能力值: ( LV12,RANK:290 )
在线值:
发帖
回帖
粉丝
4
青丝梦 一般来讲干这个 不得 搭梯子么? 我记得好像有个专门的apk网站 。收录了绝大多数的apk文件
梯子的事儿好说,主要是play store用不了APK文件下载不了。。。
2020-3-28 12:22
0
雪    币: 1692
活跃值: (342)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
5
pureGavin 梯子的事儿好说,主要是play store用不了APK文件下载不了。。。
额,我不太确定你的意思,如果是说知道apk文件名称,且要求在play store里下载,那一般上www.apkmirror.com,sameapk.com,apkpure.com这些网站可以找到,但另一种情况来说apk可能还存在,那需要执行下面三条命令,将安装的所有应用包列出来,
adb shell pm list packages
adb shell pm path com.xxx.yyy
adb pull 上一步得到的路径  ./
2020-4-1 10:17
0
雪    币: 14530
活跃值: (17548)
能力值: ( LV12,RANK:290 )
在线值:
发帖
回帖
粉丝
6
Dawuge 额,我不太确定你的意思,如果是说知道apk文件名称,且要求在play store里下载,那一般上www.apkmirror.com,sameapk.com,apkpure.com这些网站可以找到,但另 ...
明白了
2020-4-1 10:57
0
游客
登录 | 注册 方可回帖
返回
//