首页
社区
课程
招聘
[原创]p0six_spawn untether部分分析
发表于: 2016-4-20 13:33 12507

[原创]p0six_spawn untether部分分析

2016-4-20 13:33
12507
感谢@jerryxjtu 对p0six_spawn的分析,查了一下untether部分相关的资料,按照自己的理解做了一下整理和分析。同时也有很多困惑,希望对这方面有了解的能指点一下
XNU
1.使用mach_msg with ool descriptor,可以在内核空间创造一片堆内存,他描述了mach消息所要附加的数据的地址、大小,以及如何处理数据的指令。 
查看xnu的源码,可以看到它的send_msg大致执行流程:

mach_msg-->  
mach_msg_trap-->
mach_msg_overwrite_trap-->  
ipc_kmsg_copyin_body-->  
ipc_kmsg_copyin_ool_descriptor-->  
copyin  
这里把用户空间的descriptor拷贝到了内核空间,当数据的大小在4096字节一下,也会在内核空间中拷贝一份。也就是说,我们可以通过它来控制内核的堆内存的创建;同样的,通过receivemsg,我们可以控制内核去释放这片堆内存。

2.setrlimit可以设置进程所拥有的最大的fd的数量,在OS X下已经测试过,默认的最大值是256;使用setrlimit调高rlim_cur之后,可以通过dup2重定向文件描述符,来使用当前的最大文件描述符号,来触发fdalloc,fdalloc会重新分配一片堆内存,用来存储fd;每个fd的size为(sizeof(struct file*) + sizeof(char)),即5个字节。重要的一点是,fdalloc中,会重新为进程对应的filedesc结构分配内存空间,重要的fd_ofileflags也将被重定位。xnu源码中的调用流程为:

dup2-->    
fdalloc-->  
MALLOC_ZONE  
3.posix_spawn即fork+exec组合的变体,posix_spawn的好处在于,它没有因为fork创建子进程而造成额外的资源消耗,而是在子进程调用exec前挂起父进程并且父子进程共享虚拟地址空间,也不会因为执行exec时发生死锁问题造成父子进程的互相等待。另外,和本文相关的是,执行posix_spawn的时候,可以指定子进程要执行的一些文件描述符操作例如打开、关闭、复制等,而这些文件操作都是在内核中进行的。 
执行文件操作时,文件操作的数据被从用户空间复制到了内核空间。 
相关源码在kern_exec.c-->posix_spawn函数。

Vuln
1.posix_spawn在执行文件操作的时候,仅仅检验了文件操作的数量的上下限,但是并没有检验是否有这么多个完整的文件操作数据。这也就是说,如果向内核中写入了不完整的File Actions,通过读取文件操作数据的最后面的内容,有可能会使一部分处在缓冲区之外的数据暴露出来。这个漏洞为后续的运行时修改文件描述符提供了条件。

2.PSFA_INHERIT这个文件操作在内存中发生了写操作,具体的写的内容是向fd_ofilflags[fd]这个byte和0x20做按位或操作。但是问题是fd的值在进行这个文件操作之前已经做过了一次校验。这里可以通过文件锁来制造一个百分百成功率的竞态条件来在校验和写入两个操作之间,结合1,使用另一个内核线程,改变File Action缓冲区后面的那片内存中的数据(需要进行堆风水来控制堆内存分配)。这时候fd的取值可以是任意值,也就是说,我们可以将任何的和fd_ofileflags地址的相对fd偏移位置的地址中的值和0x20做按位或操作。

3.Azimuth的vm_map_copy_t自我重定位技术,详细介绍可以看一下iOS6 kernel Security : A Hacker's Guide这个ppt。结合2中的相对写操作,以及堆风水,可以得到一个内核指针,并计算出ofile_flags的内核地址。也就是说,可以在内核的任意位置进行写操作了。

Exploit
梳理一下攻击流程: 
1.使用OOL mach_msg创建堆内存,来填充kalloc.1536,使后续能分配到连续的堆内存; 
2.触发setrlimit+dup2,使fd_ofileflags被重定位到新分配的堆内存中; 
3.执行posix_spawn, 执行文件操作FA1 + close A + open B,其中FA1和部分的close A是利用Vuln中的1所分配,剩余部分则是使用OOL mach_msg所分配。这里我思考了一下为什么vm_map_copy_t这种结构的header部分不会影响到File Action的解析。一个_psfa_action结构是1040字节,其中大部分是表示打开操作的文件路径psfao_path,对于open操作,可能是header部分被放在了前一个_psfa_action的psfao_path的"\0"结束符后面,对于其他操作,header部分被放在了无用的psfao_path区域。posix_spawn的loop1中,执行到close A时,解开了A的文件锁,open B等待B文件锁被某个线程释放; 
4.另一个线程中则是负责创建内核堆中的部分close A以及整个的close B。它占有了B文件锁,一直在等待A文件锁释放,A释放之后,同样使用OOL mach_msg receive msg 以及 send msg, 就可以重新在这一片堆内存中写入数据;重新写入的数据为PSFA_DUP2和PSFA_INHERIT, 通过PSFA_DUP2可以实现任意写入fd的值,通过PSFA_INHERIT实现了数据的写入; 
5.写入的地点是第一个mach_msg所分配的vm_map_copy_t的size地址与fd_ofileflags的相对位置,使size的值增大了,这样的话使用receivemsg就可以dump出后面的vm_map_copy_t区域以及filedesc区域。dump出来的内容会包含内核指针以及fd_ofileflags。只要把内核dump出来之后,我们就能实现内核内存的任意写操作。

Doubts
通过查的资料和阅读untether的反编译代码的一些关键函数,基本可以猜测攻击流程是如上所述。但是从反编译代码中还是有很多不理解的地方: 
1.untether中使用setrlimit提高进程能持有的最大文件描述符数量到了12000,并且创建了5000个pipe(10000个文件描述符),不知道这样有什么意义; 
2.没有发现堆风水; 
3.fileActions的布局和想象中的数据结构不太一样;

感觉Pseudocode有时候比起ARM更容易让人困惑,比如这里的SendHeapLayouts函数,把两个内存中连续的不同的数据结构当成了一个数组...这方面类似的分析还是没啥经验。

[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

收藏
免费 0
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回
//