在我们控制了内核上下文的PC寄存器之后,我们接下来肯定是通过执行代码来获得设备的root权限,这才是我们一直的最终的目的。这里最简单的方法是重写当前任务(task)的addr_limit到0,于是达到内核空间的任意读写的目的,然后在我们重写一些内核空间的重要数据结构之后提权。
对于没有PXN存在的设备上,事情就很简单了。我们只需要将close函数指针设置为一个用户空间的虚拟地址,然后将一段用来将当前任务的addr_limit设置为0的shellcode放在这个位置。
对于那些开启了PXN的设备,ret2usr攻击就没什么用了。于是我们需要使用ROP来达到我们的目的。然而为了设计一种稳定的利用方式,我们用到内核JOP(Jump-Oriented Programming,面向跳转的编程)来重写当前任务的addr_limit到0,而不是使用ROP。
1)在JOP中用到的寄存器
在JOP过程中,一些寄存器可能会改变当前的值。如果我们不关心,他们原来的值其实是可能会丢失的。在我们的root过程中,我们在悬垂的套接字文件描述符上调用close函数,然后进入到在用户空间放置的JOP链。在整个JOP链的执行过程中,我们只改变了r0,r1,r2,r3,r4和f5的值,改变其他寄存器的值将会在以后导致一些难以预料的内核崩溃。一些关键寄存器的值需要保持不变
2)保持SP的值
我们使用JOP而不是ROP的主要原因是,在ROP里我们经常需要stack pivot,也就意味将内核空间的栈pivot到用户空间里,这样的行为将会导致在exploit的一段时间中sp值损坏。寄存器SP在整个内核的执行当中是至关重要的,在任何时间选择损坏整个值都是不推荐的。
3)避免栈中的数据损坏
我们需要避免在JOP中使用这样的片段(gadget)
str x2, [sp, 0x20] # 32位
str x2, [x29, 0x20] # 64位
寄存器在64位linux内核中存储了SP的值。这些片段将会损坏栈上的数据,导致影响到将来内核的执行流程。这样的行为会导致不确定性,我们应该避免。
4)利用core片段
我们的JOP链主要有两个任务。第一个任务是在内核中泄露出SP的值,然后我们可以从SP值得到当前任务的task_struct的地址。第二个任务是重写当前task_struct中的addr_limit,这将会导致内核空间的任意地址读写。我们需要找到的核心片段大概如下:
str x1, [x0, 0x14]
ldr x1, [x2, 0x10]
blr x1
对这个例子而言,为了达到泄露的目的,x0寄存器的值应该为用户空间的一个虚拟地址,然后我们读取到x1寄存器的值,也就是内核空间的SP值。然后我们可以通过跳转到由x2寄存器指向的正确返回地址来回到JOP,而其值则也应该是用户空间的地址。
为了达到重写的目的,x1寄存器的值应该为0,然后x0的值应该是和addr_limit的地址相关的。然后我们回到原来的返回地址。
总体来说,有两步,泄露和重写。我们试图在各个开启了PXN的设备的启动镜像(boot image)中找到这样的片段。
5)泄露小技巧
A.在64位android设备上,寄存器x29像往常一样存储了内核SP的值。于是这样的指令在得到内核栈地址的时候可以被使用。
mov x0, sp # 32位
mov x0, x29 # 64位
B.对于64位设备,内核虚拟地址的高32位空间保持不变。于是我们在攻击中泄露内核栈地址的低32位就已经可以了。
6)重写小技巧
当问题来到这些市场上的android设备的一些ROM的时候,有一个镜像中的片段我们可以用来利用他达到内核的任意地址写,然后事情就解决了。否则我们就有两种选择。
A.直接法
mov x1, 0
str x1, [addr_limit]
B.间接法
mov x1, [user_space_address] #用户空间地址
str x1, [addr_limit]
或者有时候我们也可以使用
str w1, [addr-limit]
然后写两次来把addr_limit设置到0。
在这篇论文中,我们展示了CVE-2015-3636的细节,然后展示了Keen Team是怎样通过他来达到在市面上大多数android大于4.3的设备上进行通用root的。我们使用这样的利用技术也可以root64为设备,这在世界上还是首例。另外,通过确定的一些android内核JOP技巧,PXN可以完全被可靠地绕过。
References
1. V. P. Kemerlis, M. Polychronakis, and A. D. Keromytis.
ret2dir: Rethinking kernel isolation. USENIX Security
Symposium, 2014.
2. Jon Oberheide, Dan Rosenberg. Stackjacking Your
Way to grsecurity/PaX Bypass. INFILTRATE 2011.
3. https://www.kernel.org/doc/Documentation/vm/slub.txt.
4. Vasileios P. Kemerlis, Georgios Portokalidis, and Angelos
D. Keromytis. kGuard: Lightweight Kernel Protection
against Return-to-user Attacks. USENIX Security Symposium,
2012.
5. Marco Prandini and Marco Ramilli. Return-oriented
programming. Security and Privacy, IEEE, 2012.
6. Tyler Bletsch, Xuxian Jiang, Vince W. Freeh, Zhenkai
Liang. Jump-Oriented Programming: A New Class of CodeReuse
Attack. Proceedings of the 6th ACM Symposium
on Information, Computer and Communications Security.
ACM, 2011