Android保护机制及利用技巧总结

admin 2025年1月15日14:43:28评论22 views字数 5298阅读17分39秒阅读模式
一、前  言

二、真机内核利用适配

三、SELinux

四、CFI保护

五、BTI保护

六、PAC保护

七、MTE保护

八、AARCH64 JOP

九、总  结

前  言
本文总结了在Android上利用漏洞时遇到的一些新的保护机制以及在真机上的内核漏洞利用和调试技巧。虽然Android底层为Linux内核,但是相比较下Android内核更加难利用,主要体现在真机不能实时调试,可能开启了BTI保护、PAC保护和CFI保护,同时在近年新出的一些手机,如Pixel 10开启了内存标记访问保护Memory Tagging Extension(MTE)。本文还将介绍MTE保护在用户态时的一个特殊的绕过方法,通过探讨这些新的保护机制及其应对策略,我们希望能够帮助读者更好地理解当前Android安全环境,并为未来的漏洞研究提供新的思路和技术手段。

真机内核利用适配
对于一个真机内核,在编写漏洞利用程序期间可以编译一个版本一样的Linux内核用qemu模拟运行,便于掌握数据的处理过程。还可以使用Android模拟器,目前高版本的Android模拟器无法在x86/x64架构下模拟AARCH64的镜像,可以在AARCH64架构下的主机,如树莓派等下面运行模拟器。在模拟的内核中利用成功后,就是如何将其移植到真机上的问题。虽然真机不能实时调试,但是可以通过查看/sys/fs/pstore/目录下的日志文件以及dmesg来获取内核最后崩溃时的寄存器值。根据寄存器信息来定位漏洞利用程序中需要适配的位置。

Android保护机制及利用技巧总结

SELinux
SELinux是一个强制访问控制安全机制,它提供了一种灵活的、细粒度的访问控制策略,用于提高Linux系统的安全性。Android上默认开启了SeLinux,因此某些漏洞利用方法在编译的Linux内核中能够使用但是在Android上测试却失效了。

01
SELinux原理

SELinux实际上是对系统中所有的关键函数注册了HOOK

Android保护机制及利用技巧总结

这些HOOK函数会在函数中被调用,它们一般以security_开头。

Android保护机制及利用技巧总结

如果SELinux没有开启,这些security_函数默认返回0让程序继续程序,如果开启了则跳转到HOOK函数执行。

Android保护机制及利用技巧总结

这些HOOK函数根据SELinux配置的规则对参数进行审计,以此来让一个函数执行或者拒绝。

02
SELinux绕过

当开启SELinux时,改写modprobe_path或者core_pattern后不能触发提权脚本的执行,这是因为我们指向的脚本不在SELinux规则中规定的可执行路径。为了绕过SELinux的检查,我们查看审计函数的代码,avc_has_perm函数的子调用链为avc_has_perm->avc_has_perm_noaudit

Android保护机制及利用技巧总结

如果avc_has_perm_noaudit函数审计出当前的操作是被禁止的,那么调用avc_denied函数。

Android保护机制及利用技巧总结

avc_denied函数来看,如果selinux_enforcing全局变量为0,则仍然可以使得avc_denied返回0,进而让selinux_函数放行,因此可以利用漏洞改写selinux_enforcing这个全局变量来绕过SELinux。

在高版本Linux中,判断方式采用了函数,实际上判断的是state->enforce

Android保护机制及利用技巧总结

state指针指向的仍然是一个全局变量结构体。

Android保护机制及利用技巧总结

因此可以修改selinux_state.enforce变量。

CFI保护
CFI保护是Android内核中引入的,目的是保护函数指针,如果函数指针被篡改为任意地址,会被检测出来然后终止执行。开启了CFI保护的内核如下所示,会有很多以.cfi结尾的函数。

Android保护机制及利用技巧总结

还存在着不带.cfi结尾的同名函数。

Android保护机制及利用技巧总结

不带.cfi的函数中只会有一条B跳转指令,不会再有其他任何人指令。实际上这些函数是一张类似于PLT跳转表的东西,我们可以把它命名为CFI表。

01
函数指针检查

CFI的检测实际上就是对每一个函数指针调用的位置进行了插桩,判断函数指针是否在CFI表中,如下是CFI的桩代码:

Android保护机制及利用技巧总结

如果函数指针发生了篡改,则将进入_cfi_slowpath函数,_cfi_slowpath函数调用_cfi_check进行检查。

Android保护机制及利用技巧总结

_cfi_check根据_cfi_slowpath函数的第一个参数传入的MAGIC值,会再一次的判断函数指针是否能够通过检查。

Android保护机制及利用技巧总结

如果函数指针与预期值不等,则调用__cfi_check_fail函数让内核崩溃。

Android保护机制及利用技巧总结

02
函数指针多值的处理

某些函数指针可能有多个指向的目标,因此不能对函数指针进行固定值比较,CFI采用了运算的方式将指针值限定在一个范围内。

Android保护机制及利用技巧总结

即只能在CFI表中的single_step_handler附近。

Android保护机制及利用技巧总结

显然,在编译时,生成的这张CFI表中函数的排列顺序是精心计算安排的,把一个函数指针所有可能的指向地址排列成相邻的。

03
CFI绕过的可能思路

对于ARM架构,目前无法绕过CFI,因为ARM架构的指令是对齐且定长的,不能在CFI表中跳转到错位的地址进而构造出ROP gadget。如果是在x86架构下,对于函数指针多值的CFI检查,由于指针值限定在CFI表的一个范围区间,可以在区间内寻找是否有合适的gadget能够控制执行流。

04
CFI例题

在GeekCon的ksocket pixel3题目中,实现了一个自定义的socket,我们可以通过UAF控制这个socket对象的结构。由于开启了CFI,我们不能去控制函数指针。

Android保护机制及利用技巧总结

我们观察到,在close时触发的avss_release函数中有以下的链表unlink操作:

Android保护机制及利用技巧总结

我们可以把unlink用来做任意地址写,由于两个数据都必须为合法的内存指针,因此不能直接写数据。但是可以用错位的思路,CPU为小端,因此指针的最低一个字节存放在最前面,我们每次只需要保证指针的最低一个字节被写入到目标地址即可。令*(v3 + 112) = addr, *(v3 + 104) = bss | byte,则可以在addr处写上一个字节byte。其中bss为bss的地址,用于保证两个数据都为合法的内存指针不会崩溃。在实现了任意地址写以后,改写selinux_enforcing为0关闭selinux,改写modprobe_path为提权脚本。然后触发modprobe_path的执行。

BTI保护
在 AArch64(ARMv8-A 架构的 64 位模式)中,BTI 指令用于验证间接跳转的目标是否有效。它的主要作用是确保程序控制流只能跳转到预期的代码位置(即合法的分支目标)。即BLR/BR Rn寄存器跳转指令跳转的目标位置的第一条指令必须为BTI否则函数无法继续向下执行。

Android保护机制及利用技巧总结

PAC保护
01
PAC原理

PAC(Pointer Authentication) 技术,用于验证和保护返回地址及其他指针数据的完整性。ARMv8.3-A 新引入了三类指令:

  • PAC* 类指令可以向指针中生成和插入 PAC。比如,PACIA X8,X9 可以在寄存器X8中以 X9 为上下文,APIAKey为密钥,为指针计算PAC,并且将结果写回到 X8 中。
  • AUT* 类指令可以验证一个指针的 PAC。如果PAC是合法的,将会还原原始的指针。否则,将会在指针的扩展位中将会被写入错误码,在指针被间接引用时,会触发错误。比如,AUTIA X8,X9 可以以 X9 为上下文,验证 X8 寄存器中的指针。当验证成功时会将指针写回 X8,失败时则写回一个错误码。
  • XPAC* 类指令可以移除一个指针的 PAC 并且在不验证指针有效性的前提下恢复指针的原始值。PAC的加密生成算法不同的硬件有不同的实现。

在Android中,开启了PAC保护的函数如图所示,PACIASP指令会基于当前的栈指针(SP)、私有密钥(APIAKey)以及返回地址生成认证码,认证码被嵌入到给定的函数返回地址中,在函数返回时,使用对应的 AUTIASP 指令对返回地址进行验证。如果地址合法且未被篡改,验证成功;否则,程序会触发异常(SIGILL 或其他非法指令异常)。

Android保护机制及利用技巧总结

02
PAC绕过

PAC绕过是困难的,PAC的密钥通过特定的系统寄存器存储和操作。内核态使用的密钥是APIXKey_EL1,用户态使用的密钥是APIXKey_EL0,因此在用户态计算出的PAC值不能给内核态使用。内核态下可以操作访问APIXKey_EL1APIXKey_EL0等寄存器修改或者读取密钥。

Android保护机制及利用技巧总结

因此有一种可能的情形就是在内核态中某个gadget可以将用户态的APIXKey_EL0修改成与内核态一样的数值,那么就可以在用户态执行PAC指令计算PAC值然后填入ROP链。

MTE保护
01
MTE原理

MTE (Memory Tagging Extension)是ARMv8.5-A 架构引入的一项硬件支持的内存安全技术,旨在检测和防止内存相关的错误和漏洞,例如越界访问和使用已释放内存(Use-After-Free, UAF)。MTE 的基本原理:

Android保护机制及利用技巧总结

  • IRG (Insert Random Tag) 指令为指针Xn生成一个随机tag,使用Xm作为种子,将结果保存至Xd中。
  • STG (Store Allocation Tag) 指令将tag应用至内存中,生效的长度取决于颗粒度,一般为16字节。
  • LDR (Load Register) 使用带有tag的指针读取内存。
Android保护机制及利用技巧总结

如图,IRG指令执行后,X0比X8在高位多了一个TAG值。

Android保护机制及利用技巧总结

STG指令执行后,以后访问这段内存需要带上正确的TAG值的指针才能访问,否则指令会执行错误。

Android保护机制及利用技巧总结

02
MTE应用

在堆分配器中,malloc后,通过对申请的堆地址打上标签返回,free后对堆地址重新打标签。这样就能阻止UAF这类的漏洞,因为free后指针重新打了标签,导致UAF残留的指针无效,通过UAF的指针访问内存时就会崩溃。不同的堆分配器在mallocfree时有着不同的处理内存标签的方式。有关内存分配器处理MTE标签的分析可以参考文章GeekCon的文章填补盾牌的裂缝:堆分配器中的MTE。

03
MTE爆破

如果给系统调用直接传一个带有错误TAG的指针,会发生什么?如图,假设buf指向的内存已经被free导致重新打标签,现在传给Sys_write的是一个无效的指针。

Android保护机制及利用技巧总结

单步进入会触发内核的Error EL1h

Android保护机制及利用技巧总结

错误会被el0t_64_sync函数捕捉处理。

Android保护机制及利用技巧总结

异常处理会调用el0_svc函数,并不会退出程序。

Android保护机制及利用技巧总结

异常处理完成后,调用ret_to_user返回到了用户态。

Android保护机制及利用技巧总结

可见,当一个不正确的MTE指针进入系统调用,系统调用执行不成功,同时进程不会崩溃;我们可以利用这种特性来对TAG值进行爆破。一般的,我们在用户态利用UAF漏洞时,在已知指针值但是不知道TAG,我们可以用这样的方法爆破。

Android保护机制及利用技巧总结

上述代码来源于我在GeekCon Shanghai 2024上解出的MTE题的EXP。

AARCH64 JOP
在AARCH64中,RET指令不会从栈里弹出返回地址进行返回,RET指令直接跳转到X30寄存器指向的地址;而BLR指令在跳入新函数时,会将返回地址赋值给X30寄存器。由于这个特性,我们在搜索一些gadgets指令时,无需考虑BLR后面的代码。

在做GeekCon的kSysRace赛题时,我们控制了一个地方的函数指针,能够调用任意一个函数,以及X0执行的内容可控:

Android保护机制及利用技巧总结

让其先跳入下面的代码:

Android保护机制及利用技巧总结

在这段代码中,我们的目的是控制X19指向X0,因为X0是我们可控的,我们不用担心BLR X8返回执行后面,因为我们可以再调用一次BLR来将X30覆盖。我们控制X8,让其先跳入下面的代码:

Android保护机制及利用技巧总结

在这段代码中,由于X19可控,我们可以调用3个参数的任意函数了,自始至终,我们的栈没有发生过调整,由于漏洞发生的位置栈尾部是这样的:

Android保护机制及利用技巧总结

栈尾部跟我们的gadgets一模一样,这意味着我们的gadgets在执行到RET时可以直接返回到漏洞发生的函数的上层,栈平衡了。也就是我们能够执行任意的一个函数,控制3个参数,同时栈能够恢复,可以让程序继续保持正常的运行状态。这样我们就可以进行多次的任意函数调用。

总  结
本文我们介绍了众多在Android AARCH64上所使用的保护机制以及特性,劫持程序流程变得越来越困难,在没有开启程序流保护的情况下,使用JOP去实现任意代码执行;当程序流保护机制开启时,可以转变思路,通过劫持一些数据结构体,利用程序中自带的linkunlink等操作去实现一个地址写或者读,本文还介绍了MTE保护机制的一种特殊情况下的爆破。

参考链接
1. <a ""="" class="weapp_text_link js_weapp_entry" href="">AVSS 2024 Final Writeup

2. <a ""="" class="weapp_text_link js_weapp_entry" href="">填补盾牌的裂缝:堆分配器中的MTE

【版权说明】

本作品著作权归ha1vk所有

未经作者同意,不得转载

Android保护机制及利用技巧总结
ha1vk

二进制安全研究员,BlackHat USA 2022 WASM演讲者,专注IOT、硬件、内核等方面的研究。

原文始发于微信公众号(奇安信天工实验室):Android保护机制及利用技巧总结

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年1月15日14:43:28
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   Android保护机制及利用技巧总结https://cn-sec.com/archives/3631920.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息