使能和测试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架构甚至同一架构下不同版本的使能方式可能存在差别,具体可参考下表:

 (表格来源: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。

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

 第四步:再次安装运行测试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

  • 0
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
编译树莓派4b的arm64内核需要以下几个步骤: 1. 安装必要的工具 首先,需要安装一些必要的工具,比如gcc、make等。可以使用以下命令进行安装: ``` sudo apt-get update sudo apt-get install git bc bison flex libssl-dev make libc6-dev libncurses5-dev ``` 2. 下载内核源代码 树莓派的内核源代码可以从官方github仓库中获取。可以使用以下命令进行下载: ``` git clone --depth=1 https://github.com/raspberrypi/linux.git -b rpi-5.10.y ``` 这里我们选择了5.10.y分支作为示例,具体版本可以根据自己的需要选择。 3. 配置内核参数 进入内核源代码目录,并进行内核参数配置: ``` cd linux KERNEL=kernel8 make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- bcm2711_defconfig ``` 其中,KERNEL变量指定了内核的名称,这里我们编译的是64位内核,因此为kernel8。 4. 编译内核 使用以下命令进行内核编译: ``` make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- Image.gz modules dtbs ``` 其中,Image.gz为内核镜像文件,modules为内核模块,dtbs为设备树文件。编译完成后,这些文件会被放置在arch/arm64/boot和arch/arm64/boot/dts目录下。 5. 安装内核 将生成的内核文件复制到/boot目录下: ``` sudo cp arch/arm64/boot/Image.gz /boot/$KERNEL.img sudo cp arch/arm64/boot/dts/broadcom/*.dtb /boot/ sudo cp arch/arm64/boot/dts/overlays/*.dtb* /boot/overlays/ sudo cp arch/arm64/boot/dts/overlays/README /boot/overlays/ sudo reboot ``` 6. 配置启动 修改/boot/config.txt文件,添加以下内容: ``` kernel=$KERNEL.img ``` 这样,在下一次重启时,系统会启动新编译的内核。可以使用以下命令查看当前正在使用的内核版本: ``` uname -a ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

车联网安全杂货铺

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值