最近在研究内核提权时,突然发现以往修改current_thread_info()->addr_limit,以实现任意内核地址读写的方法不能用了,于是研究了下这部分代码。
以往的利用流程是用漏洞把current_thread_info()->addr_limit扩展为0xffffffffffffffff或是其它比用户空间更大的值后,再用pipe的读写组合从内核空间读写任意地址。
读写任意地址所用的内核函数是copy_from_user以及copy_to_user, 现在以copy_from_user为例子进行说明:
copy_from_user的第一个校验就是access_ok,这里校验了user传来的地址是否超出了addr_limit,把addr_limit扩展为0xffffffffffffffff就是为了绕过这个校验。
接下来进入arch_copy_from_user,代码如下:
ENTRY(arch_copy_from_user)
uaccess_enable_not_uao x3, x4, x5
add end, x0, x2
#include "copy_template.S"
uaccess_disable_not_uao x3, x4
mov x0, #0 // Nothing to copy
ret
ENDPROC(__arch_copy_from_user)
这里遇到一个开关,uaccess_enable_not_uao:
.macro uaccess_enable_not_uao, tmp1, tmp2, tmp3
uaccess_ttbr0_enable \tmp1, \tmp2, \tmp3
alternative_if ARM64_ALT_PAN_NOT_UAO
SET_PSTATE_PAN(0)
alternative_else_nop_endif
.endm
其作用是打开内核空间访问用户空间的限制,如果不打开它,内核访问用户地址是会进入异常的。
这个实现方式网上有很多资料,简单说就是有两个方式进行限制。
一个是软件的,把用户空间的页表指向空页表,uaccess_ttbr0_enable \tmp1, \tmp2, \tmp3这句就是为了恢复这个页表,这种方式对应的配置项是CONFIG_ARM64_SW_TTBR0_PAN
另一个是硬件的,SET_PSTATE_PAN(0)就是从硬件上关闭这个限制,对应的配置项是CONFIG_ARM64_PAN
不过这个限制显然不会对漏洞利用造成阻碍。
再进入copy_template.S
这里可以看到实际进行拷贝的源码:
tbz tmp2, #0, 1f
ldrb1 tmp1w, src, #1
strb1 tmp1w, dst, #1
1:
tbz tmp2, #1, 2f
ldrh1 tmp1w, src, #2
strh1 tmp1w, dst, #2
2:
tbz tmp2, #2, 3f
ldr1 tmp1w, src, #4
str1 tmp1w, dst, #4
3:
tbz tmp2, #3, .LSrcAligned
ldr1 tmp1, src, #8
str1 tmp1, dst, #8
ldrx和strx分别是从源地址提取数据,以及发送数据到目的地址,注意它们都使用了宏,后缀有个“1”,以ldr1和str1为例:
.macro ldr1 ptr, regB, val
uao_user_alternative 9998f, ldr, ldtr, \ptr, \regB, \val
.endm
.macro str1 ptr, regB, val str \ptr, [\regB], \val .endm ....... .section .fixup,"ax" .align 2
9998: sub x0, end, dst // bytes not copied
ret
.previous
str1就是str,这意味着发送数据到目的地址没有任何限制,而ldr1却成了ldtr,这将使这个load指令成为一个unprivileged操作,无法访问内核空间地址,因此我们故意从pipe传入的非法内核地址,到了这里就会发生异常并被跳转到9998进行修复,无法提取数据。
copy_to_user的流程也是类似的,只是ldr1 str1的宏定义反过来了而已。
这样,原理就揭示出来了,这个功能也是一个配置项CONFIG_ARM64_UAO控制的,UAO即'User Access Override',是在armv8.2中引入的硬件特性,那为什么以前这个保护没有生效呢?推测原因有两点,一是手机的芯片不是armv8.2的核心,二是虽然是armv8.2的核心,但却没有打开CONFIG_ARM64_UAO配置项。
为此又拿了几款手机来确认:
1.pixel3
骁龙845----Kryo 385-----ARMv8.2-A
CONFIG_ARM64_UAO是打开的,这是我的测试手机,关闭CONFIG_ARM64_UAO刷机,顺利绕过了这个保护。
2.mi10
骁龙865----Kryo 585-----ARMv8.2-A
CONFIG_ARM64_UAO是打开的,看来也得刷机了
3.honer-v30 mate30-pro p40
华为手机比较多,一下找了3款,其中p40是麒麟990-----A76+A55-----ARMv8.2-A
这几款最新的手机,CONFIG_ARM64_UAO竟然全是关闭的!
4.vivo y50
骁龙665----Kryo 260-----ARMv8-A
这个倒是打开了CONFIG_ARM64_UAO,但cpu架构不是ARMv8.2-A的,因此是不起作用的。