PXN与PAN机制攻防

由来与演进

Posted by Weizhou on October 7, 2021

前言

PXN(Privilege Execute Never)PAN(Privilege Access Never)是ARM在v7架构开始引入的安全机制。与之对应的Intel使用相同机制的名称为SMEP(Supervisor Mode Execution Prevention)和SMAP(Supervisor Mode Access Prevention)。实际上是在Intel在2014年Intel Broadwell微架构中引入SMAP与SMEP后ARM随后引入的类似机制。PXN(SMEP)与PAN(SMAP)同是芯片的硬件特性,需要对应的操作系统进行适配和使能。Windows在Win10以后引入并使能了该能力。本文主要从安全攻防角度阐述当前针对PXN和PAN的攻防由来和进展。

正文

PXN与PAN的硬件寄存器配置

PXN和PAN的实际功能和设计思想是使得运行在Privileged Mode下的代码即内核态程序无法随意的执行/访问用户态的代码/数据。
如下图所示为ARM CP15寄存器对页表项的说明:
cp15_reg.jpg
操作系统内核可通过配置上述寄存器中的PXN位完成对系统内核访问用户数据或代码的权限控制。

PXN与PAN的引入

PXN和PAN的引入初衷实际上是为了解决ret2usr的攻击方式。ret2usr攻击方式如下图所示:
ret2usr.PNG
ret2usr主要用于在入侵到系统获取到可以执行任意程序的能力后,实现提权攻击的场景。使得攻击者本应在用户态执行的代码可被内核以特权的权限执行
ret2usr并不是能够单独使用的一种攻击手段,需要与内核中的其他漏洞配合使用。例如当内核代码中存在栈溢出漏洞时,攻击者就可将栈上的return指针覆写成用户态中准备的攻击代码地址。或者当内核存在heap overflow的时候攻击者可通过修改堆上的函数指针变量,使其指向用户态的恶意代码,当内核达到某种条件后进行回调操作。 如上图所示针对上述攻击现有的KERNEXECkGuardPXNPANUDEREF机制和进行防护。ret2usr攻击的核心思路是利用操作系统中用户态和内核态的代码和数据没有完全隔离的条件,实现在内核态执行用户态代码的行为进而实现提权的效果。

使能PAN后数据拷贝

用户态会通过系统调用与内核进行服务的请求,当向内核传入的参数是用户态指针时,会将指针所指向的数据从用户态搬运到内核态并进行后续计算,这个过程由copy_from_user函数完成;当系统调用完成输出一个指针数据时需要调用copy_to_user函数向用户态进行搬运。当系统使能PAM机制后就会有读者好奇,这部分的工作是否会影响。答案是肯定的,以copy_to_user函数为例,最终会调用与架构相关的raw_copy_to_user函数:

raw_copy_to_user(void __user *to, const void *from, unsigned long n)
{
#ifndef CONFIG_UACCESS_WITH_MEMCPY
    unsigned int __ua_flags;
    __ua_flags = uaccess_save_and_enable();
    n = arm_copy_to_user(to, from, n);
    uaccess_restore(__ua_flags);
    return n;
#else
    return arm_copy_to_user(to, from, n);
#endif
}

如上图所示,当使能了CONFIG_UACCESS_WITH_MEMCPY宏后,首先会调用uaccess_save_and_enable函数暂时关闭PAN功能,然后调用架构相关的拷贝函数arm_copy_to_user进行数据拷贝,最后再调用uaccess_restore函数恢复PAN属性的配置。

PXN与PAN绕过技术1——ret2dir

该技术由哥伦比亚大学在2014年提出,其利用原理是,linux内核在设计的时候,为了提高内存的操作效率,在用户空间映射内存的时候,内核也相应地在内核的低端内存区地址映射一段影子内存称之为physmap。攻击示意图如下所示:
ret2dir.PNG
同样,攻击者也可以将用户空间的攻击代码映射到内核的低端内存可执行区或者将特定数据进行喷射到内核的低端内存,进行内存布局,然后利用发现的漏洞,让内核执行攻击代码,从而达到提权的作用。这项技术在32位arm设备上有60%以上的成功率,而在64位arm中有96%的成功率。通过RET2DIR和JOP方式的结合,可以使得UAF这类漏洞的利用代码比较稳定,而且成功率较高。
keen_team在cve-2015-3636的漏洞利用中使用这两个技术后,一时间RET2DIR成为漏洞利用的“倚天剑”。
不过在2016年七月google在android PIXEL(内核3.18-16.04)版本以后封杀了RET2DIR的攻击方式。
medi.jpg

PXN与PAN绕过技术2——利用VDSO

由于linux内核是个庞大且复杂的系统,一定还有这样的“福地”,让我们可以在内核中找到,空间任意执行我们的ShellCode。
近年攻击者把目标放在了内核和用户空间共享的代码空间-VDSO(Virtual Dynamic Shared Object,虚拟动态共享对象)
VDSO是内核为了减少内核与用户空间频繁切换,提高系统调用效率而提出的机制。特别是gettimeofday这种对时间精度要求特别高的系统调用,需要尽可能地减少用户空间到内核空间堆栈切换的开销。
vdso.jpg
可以通过cat /proc/self/maps命令来查看用户态vdso映射情况:
map.jpg
由于该段用户空间和内核空间是一一映射的, 如果我们能在这段空间中布置我们的提权代码,我们可以让内核来执行该段代码,从而提权。
不过事情没有想象那么容易,该段空间是只读和可执行的。通过阅读内核的代码,我们可以发现,如果我们可以把这段映射区改为可读可写可执行就可以了。

int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp) {
    ret = _install_special_mapping(mm,
                                   vdso_base,
                                   vdso_text_len,
                                   VM_READ|VM_EXEC|VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC,
                                   &vdso_spec[1]);
    ...
}

可以通过利用内核中现成的代码来完成页表属性的修改。我们发现可以通过内核导出函数set_memory_rw来打开内核页表的读写权限。set_memory_rw函数的定义如下:

int set_memory_rw(unsigned long virt, int numpages)

其中virt为起始虚拟地址,可以设置为_text的虚拟地址,numpages为页表的数量。
假设我们可以修改vdso映射区的读写权限,那我们的攻击路径可以是这样:
也可以通过读取/proc/selft/maps虚拟文件内容的方式获得用户态的vdso映射地址。我们的攻击路径可以进一步再完善为:

  1. 获取VDSO的映射地址。
    在高版本的glibc中,我们可以通过以下代码获得VDSO的地址:
    unsigned long sysinfo_ehdr = getauxval(AT_SYSINFO_EHDR);
    if (!sysinfo_ehdr) {
     printf("AT_SYSINFO_EHDR is not present!\n");
     return 0;
    }
    
  2. 利用内核漏洞的执行set_memory_rw函数,修改VDSO映射区的读写权限。
  3. 在VDSO布置shellcode。
  4. 调用shellcode提权。

总结

从有关PXN和PAN机制的现有攻防两端技术可以总结出如下思考:

  • 操作系统设计时处于对性能的考虑并没有将用户态和内核态完全的分离,即最初的内核态代码是可以随意访问用户态数据,执行用户态代码的。从而引入了ret2usr的漏洞。
  • 伴随芯片厂商PXN(SMEP)PAN(SMAP)机制的引入和操作系统对应的适配,ret2usr攻击的影响得到了大幅度的削弱。但用户态和内核态之间的隔离关系没有得到改变。
  • 攻击者后续利用操作系统中隐式映射的physmap区域实现了ret2dir攻击足以表明操作系统在设计之初为了性能是给用户态与内核态之间预留一些机制以实现高效的通信。这也成为了攻击者新的兴趣方向。
  • ret2dir攻击被缓解后,由相继提出了利用VDSO实现的攻击手段,可以说明在进行性能提升设计时的确会不自觉的为“固若金汤”的内核开后门。
  • 给我们新的提示:任何新的性能提升设计可能都会伴随着新攻击路径的引入,在追求性能提升的同时要注意安全设计。
  • 性能vs安全是横亘不变的话题。