使能和测试ARM64内核PAN机制

PAN机制简介

内核PAN机制(Privileged Access Never)阻止内核态程序直接访问用户态的数据,只能通过内核提供的固定接口copy_from_user,copy_to_user与用户空间交换数据。使用这一机制的原因主要是因为在某些攻击场景下,黑客通过控制用户态数据来执行漏洞利用,比如towelroot的提权POC[1]。

从实现技术上看,PAN通过修改ttbr0_el1寄存器的值(因为ttbr0_el1寄存器保存着一级页表的地址),使内核无法完成对用户态地址的寻址,从而无法操作用户态地址[2]。另外,使能PAN的内核选项CONFIG_ARM64_SW_TTBR0_PAN的字面意思也清晰地表达了PAN的实现原理——通过“switch ttbr0”来实现PAN。

ARM64使能PAN机制

在ARM64(armv8)上通过启用内核配置CONFIG_ARM64_SW_TTBR0_PAN可以启用PAN功能。需要注意的是,不同CPU架构甚至同一架构下不同版本的使能方式可能存在差别,具体可参考下表:

使能和测试ARM64内核PAN机制_第1张图片

 (表格来源:Exploit Methods/Userspace data usage - Linux Kernel Security Subsystem)

如何测试PAN是否生效

PAN机制的测试不同于KASLR,KASLR的测试方法十分简单,通过多次启动系统对比内核符号的地址即可判断KASLR是否生效,但PAN机制没办法通过系统环境直接判断是否生效。我们可以通过编写内核模块的方式测试PAN,具体的测试步骤如下:

第一步:编写内核模块,在内核模块中使用memcpy直接访问用户空间数据。

#define BUFSIZE  100
static int kmem = 20;
module_param(kmem,int,0660);
static struct proc_dir_entry *entry;
 
static ssize_t panwrite(struct file *file, const char __user *ubuf, size_t count, loff_t *ppos) 
{
	int count, len, tmp;
	char buf[BUFSIZE];

	if(*ppos > 0 || count > BUFSIZE)
		return -EFAULT;

    memcpy(buf, ubuf, count);
	count = sscanf(buf,"%d",&tmp);
	if(count != 1)
		return -EFAULT;

	kmem = tmp; 
	len = strlen(buf);
	*ppos = len;
	return len;
}
 
 
static const struct proc_ops panops = 
{
	.proc_read = panread,
	.proc_write = panwrite,
};
 
static int pan_init(void)
{
	entry = proc_create("pandev", 0660, NULL, &panops);
	printk(KERN_ALERT "hello...\n");
	return 0;
}
 
static void pan_cleanup(void)
{
	proc_remove(entry);
	printk(KERN_WARNING "bye ...\n");
}
 
module_init(pan_init);
module_exit(pan_cleanup);

用户态测试程序如下,主要逻辑是通过buf向内核传递参数,修改/proc/pandev的值:

char buf[100];
int fd = open("/proc/pandev", O_RDWR);
read(fd, buf, 100);
puts(buf);
 
lseek(fd, 0 , SEEK_SET);
write(fd, "33", 5);
	
lseek(fd, 0 , SEEK_SET);
read(fd, buf, 100);
puts(buf);

第二步:编译模块,在未启用PAN的系统上安装模块并测试。

adas:/home# insmod lkm_hello.ko 
[   31.181093] hello...
adas:/home# cat /proc/pandev 
kmem = 20
xcu-s32g274a:/home# ./uspace 
kmem = 20

kmem = 33
adas:/home# cat /proc/pandev
kmem = 33

根据测试结果可知,在未开启PAN的系统上,内核通过memcpy的方式可以直接访问用户态的内存数据,下一步我们将用同样的测试case验证使能后的情况。

第三步:在内核配置中使能PAN。

使能和测试ARM64内核PAN机制_第2张图片

 在内核配置中开启CONFIG_ARM64_SW_TTBR0_PAN之后重新编译内核,烧录并启动系统,确认/proc/config.gz文件中CONFIG_ARM64_SW_TTBR0_PAN是否使能。

使能和测试ARM64内核PAN机制_第3张图片

 第四步:再次安装运行测试case。

adas:/home# insmod lkm_hello.ko
adas:/home# [   39.387532] hello...
adas:/home# ./uspace 
[   90.541095] Unable to handle kernel access to user memory outside uaccess routines at virtual address 0000005589d52f30
[   90.647970] printk: console [ttyLF0]: printing thread stopped
[   90.649269] Mem abort info:
[   90.656813]   ESR = 0x96000005
[   90.660425]   EC = 0x25: DABT (current EL), IL = 32 bits
[   90.665952]   SET = 0, FnV = 0
[   90.669112]   EA = 0, S1PTW = 0
[   90.672367] Data abort info:
[   90.675341]   ISV = 0, ISS = 0x00000005
[   90.679289]   CM = 0, WnR = 0
[   90.682334] user pgtable: 4k pages, 39-bit VAs, pgdp=0000000082da7000
[   90.689038] [0000005589d52f30] pgd=00000000970de003, p4d=00000000970de003, pud=00000000970de003, pmd=0000000097020003, pte=00e80000974bdf43
[   90.701908] Internal error: Oops: 96000005 [#1] PREEMPT_RT SMP
[   90.707895] Modules linked in: lkm_hello(O) pfeng(O)
[   90.712994] CPU: 3 PID: 1850 Comm: sh Tainted: G           O      5.10.41-rt42+g5c4c385db992 #1
[   90.721912] Hardware name: XXXXXX (DT)
[   90.726731] pstate: 80400005 (Nzcv daif +PAN -UAO -TCO BTYPE=--)
[   90.732887] pc : __memcpy+0x94/0x180
[   90.736564] lr : mywrite+0x64/0xc4 [lkm_hello]
[   90.741125] sp : ffffffc01422bcf0
[   90.744516] x29: ffffffc01422bcf0 x28: ffffff800ad6ea00 
[   90.749962] x27: 0000000000000000 x26: 0000000000000000 
[   90.755406] x25: 0000000000000000 x24: 0000000000000000 
[   90.760851] x23: 0000000000000000 x22: ffffffc01422be30 
[   90.766297] x21: 0000005589d52f30 x20: 0000000000000004 
[   90.771742] x19: ffffffc01422be30 x18: ffffffc010958ec8 
[   90.777187] x17: 0000000000000000 x16: 0000000000000000 
[   90.782630] x15: 0000000000000020 x14: 0000000000000000 
[   90.788074] x13: 0000000000000000 x12: fffffffffffe7687 
[   90.793517] x11: ffffffc010958ee0 x10: ffffffc010960c10 
[   90.798962] x9 : ffffffc01422bcf0 x8 : ffffffc010958ea0 
[   90.804407] x7 : ffffffc01422bb70 x6 : ffffffc01422bd2c 
[   90.809851] x5 : ffffff806fc487b8 x4 : 0000000000000000 
[   90.815297] x3 : 0000000000000027 x2 : 0000000000000004 
[   90.820742] x1 : 0000005589d52f30 x0 : ffffffc01422bd2c 
[   90.826188] Call trace:
[   90.828693]  __memcpy+0x94/0x180
[   90.832005]  proc_reg_write+0xa8/0xec
[   90.835765]  vfs_write+0xf0/0x2b0
[   90.839164]  ksys_write+0x6c/0x100
[   90.842649]  __arm64_sys_write+0x20/0x30
[   90.846667]  el0_svc_common.constprop.0+0x78/0x1a0
[   90.851582]  do_el0_svc+0x24/0x90
[   90.854978]  el0_svc+0x14/0x20
[   90.858112]  el0_sync_handler+0x1a4/0x1b0
[   90.862219]  el0_sync+0x184/0x1c0
[   90.865625] Code: 36180062 f8408423 f80084c3 36100062 (b8404423) 
[   90.871875] ---[ end trace 0000000000000002 ]---

内核模块安装成功,但是运行用户态程序之后出现崩溃,根据日志“Unable to handle kernel access to user memory outside uaccess routines at virtual address 0000005589d52f30”等信息,我们可以确认PAN机制阻止了内核对用户态内存数据的直接访问。

参考文献

[1].towelroot提权POC

https://github.com/geekben/towelroot/blob/master/towelroot.c

[2].ARM64 PAN的实现

 git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux ttbr0-pan

你可能感兴趣的:(系统安全,arm,linux,安全,操作系统,安全性测试)