长亭百川云 - 文章详情

三星RKP内核完整性保护程序分析

kernsec

56

2024-07-13

1 功能分析

  本文主要以三星s6与s20二进制为样本进行分析。S6在2016年发布,这个版本由于有符号存在,可以大大降低逆向工程分析的难度,对于s20二进制只做部分参考分析。

1.1 Cred保护

  内核Struct cred数据结构保存了进程的权限凭证如uid/gid、capability等,是内核漏洞攻击程序进行权限提升必须要更改的数据结构,因此对cred数据结构的保护至关重要。三星rkp在el2限制了cred在el1为只读,当内核对cred进行正常写操作时,通过rkp接口调用el2层函数对其进行写操作。但rkp除了对cred做只读保护外, 还在cred数据结构引入了两个字段bp_task和bp_pgd对cred做完整性校验以及struct task_security_struct结构加入bp_cred字段,防止被其他进程篡改。

struct cred {

1.1.1 反向task_struct指针保护

  当内核需要对cred进行创建和更新时,bp_task和bp_pgd就需要同步更新。

  对于bp_task,内核在prepare_ro_creds函数里通过调用如下rkp接口:

drivers/uh/kdp.c

  对应的el2层函数操作为:

通过rkp_assign_creds对cred结构体的CRED_BP_TASK_OFFSET偏移进行赋值。

1.1.2 反向pgd指针保护

  对于bp_pgd,内核提供kdp_assign_pgd进行操作。

void kdp_assign_pgd(struct task_struct *p)

  对应的el2层函数操作为:

通过rkp_pgd_assign对cred结构体的CRED_BP_PGD_OFFSET偏移进行赋值。

1.1.3 task_security_struct指针保护

security/selinux/hooks.c

  内核调用cred_init_security对init进程进行初始化,后续子进程将会继承struct task_security_struct指针。当cred需要更改时同样使用prepare_ro_creds进行处理。

drivers/uh/kdp.c

 对应的el2函数接口为:

通过rkp_assign_secptr对cred结构体的CRED_SECURITY_OFFSET偏移进行赋值。

Rkp加入这三个指针保护的目的是做完整性检查,在LSM框架里调用hook钩子之前加入判断语句:

security/security.c

 cmp_sec_integrity用来验证cred数据结构的bp_task和bp_pgd指针是否被篡改。

1.2 Namespace保护

   Rkp对Namespace的保护目前仅局限于mount namespace,对其保护的方式为验证nsproxy->mnt_ns->root字段是否被篡改,同时还对mount挂载点进行了只读保护,不能挂载新的分区以及二进制程序必须从可信的mount点启动。

 首先在vfsmount和mount数据结构中都加入了互相指向的backup指针:

include/linux/mount.h

  内核通过调用kdp_mnt_alloc_vfsmount请求el2进行指针设置。

int kdp_mnt_alloc_vfsmount(struct mount *mnt)

  对应的el2函数操作为:

El2通过rkp_init_ns对mount结构的BPMNT_VFSMNT_OFFSET偏移进行赋值。

当LSM框架的hook钩子执行时,会调用cmp_ns_integrity进行指针完整性检查:

static unsigned int cmp_ns_integrity(void)

  当内核挂载一个新的文件系统时,调用kdp_do_new_mount->kdp_populate_sb来对指定的白名单分区做只读保护:

static void kdp_populate_sb(char *mount_point, struct vfsmount *mnt)

    Rkp对以下的super_block结构体做了只读保护:

static struct super_block *rootfs_sb __kdp_ro = NULL;

 对应的分区名白名单为:

#define KDP_MOUNT_SYSTEM "/system"

  当内核通过execve执行一个新的二进制程序时,将会调用invalid_drive函数来判断二进制程序是否从以上白名单分区中启动:

int invalid_drive(struct linux_binprm *bprm)

    kdp_check_path_mismatch忽略了以下白名单程序:

/com.android.runtime

    kdp_check_sb_mismatch检查是否来自以上白名单分区。

static int kdp_check_sb_mismatch(struct super_block *sb)

1.3 二进制程序启动权限限制

    Rkp在内核execve执行一个二进制程序时,对每个二进制在el2层做了一个标记,用于后续进行权限检查。

SYSCALL_DEFINE3(execve,

    EL2对应的MARK_PPT操作为:

__int64 __fastcall rkp_mark_ppt(__int64 a1)

    kdp_restrict_fork先判断二进制是否在白名单内:

/system/bin/patchoat

  如果cred_kdp->type的第2个bit被置位, 则强制二进制程序的uid为2000。

2 自身防护差距分析

    Rkp在自身防护上有一些优势,可以大大增加逆向工程分析的难度。

2.1 字符串混淆

    Rkp对输出的字符串进行了哈希计算,大大增加了逆向过程的难度。

if ( !(unsigned int)sub_80001400(v6) || !(unsigned int)sub_80001400(v6 + *(unsigned int *)CRED_FLAGS_OFFSET - 1) )

2.2 rkp cmd限定使用次数

   Rkp对特定的cmd使用次数进行了限制,比如一些初始化函数只需执行一次, 这样可以防止rop/jop对其进行后续重用。

_int64 __fastcall rkp_main(unsigned __int64 a1, unsigned __int8 *a2)

    [2]处在rkp_main执行完后对byte_422A8变量设置为1, 当下次rkp_main再次被调用时在[1]处会被执行检查, rkp_policy_violation函数打印”Multiple INIT calls”并panic系统。

2.3 stack canary栈溢出保护

  三星s20使用的高通qhee平台使用了stack canary栈溢出保护机制。

  在一些关键函数的开头插入如下代码:

qword_80094A48地址保存的是rkp启动时产生的一个随机值,用作stack canary,保存在x8寄存器中。

  在函数的末尾插入如下代码:

 重新加载qword_80094A48值到x9寄存器,然后与之前保存的x8寄存器值进行比较,如果发生改变则跳转到loc_8000442DC去执行。

2.4 gadget去除

  三星、苹果在发布的binary程序中,将黑客经常用到的gadget优化掉, 防止rop/jop利用。

相关推荐
关注或联系我们
添加百川云公众号,移动管理云安全产品
咨询热线:
4000-327-707
百川公众号
百川公众号
百川云客服
百川云客服

Copyright ©2024 北京长亭科技有限公司
icon
京ICP备 2024055124号-2