Android SELinux安全机制与权限管理那些事

您所在的位置:网站首页 perfdump官方 Android SELinux安全机制与权限管理那些事

Android SELinux安全机制与权限管理那些事

#Android SELinux安全机制与权限管理那些事| 来源: 网络整理| 查看: 265

文章目录 前言权限管理系统应用特权应用历史漏洞广播的保护机制CVE-2020-0391 SELinuxDAC&MACSE Android安全标签策略规则常用命令 历史漏洞 总结

前言

在 Android 漏洞挖掘和安全研究过程中,不可避免地会涉及到跟 Android SELinux 安全机制打交道,比如当你手握一个 System 应用的路径穿越的漏洞的时候想去覆写其他应用沙箱的可执行文件的时候,SELinux 极大可能成为你成功路上最大的拦路虎……

通过 ps -efZ 命令可以查看 Android 系统当前运行的进程信息和 SELinux 标签信息: 在这里插入图片描述 可以看到 Android 系统在 SELiunx 上对应用标签划分了 系统应用(system_app)、特权应用(priv_app)、平台签名应用(platform_app)、不可信应用(untrusted_app)等。

同时,Android 的权限管理机制又对权限做了以下几类划分(具体可参见Android官方文档): 在这里插入图片描述 可以看到,权限管控这里又出现了 system 应用或 privileged 应用才能使用的权限,那么这里的 system 应用或 privileged 应用跟 SELinux 机制的 系统应用(system_app)、特权应用(priv_app)是否有区别?下面逐一来学习下。

【补充】可以使用adb shell dumpsys package permision |grep -i prot命令来获取权限等级。

权限管理

先来看下 Android 权限管控机制里的 system 应用或 privileged 应用是如何区分和定义的。

先附上两篇不错的参考文章,以表敬意:

Android 权限的一些细节;“系统应用”与CVE-2020-0391”。 系统应用

第1类 System App:特殊的 Uid

追溯 AOSP 源码,可以看到 Android 对 system app 的判断逻辑为:具有 ApplicationInfo.FLAG_SYSTEM 标记的,被视为 System app。

//frameworks/base/core/java/android/content/pm/ApplicationInfo.java public boolean isSystemApp() { return (flags & ApplicationInfo.FLAG_SYSTEM) != 0; }

而 FLAG_SYSTEM 标记由 PackageManagerService 的构造函数中赋予:

//frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID, ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); mSettings.addSharedUserLPw("android.uid.phone", RADIO_UID, ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); mSettings.addSharedUserLPw("android.uid.log", LOG_UID, ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); mSettings.addSharedUserLPw("android.uid.nfc", NFC_UID, ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); mSettings.addSharedUserLPw("android.uid.bluetooth", BLUETOOTH_UID, ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); mSettings.addSharedUserLPw("android.uid.shell", SHELL_UID, ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); mSettings.addSharedUserLPw("android.uid.se", SE_UID, ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); mSettings.addSharedUserLPw("android.uid.networkstack", NETWORKSTACK_UID, ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); mSettings.addSharedUserLPw("android.uid.uwb", UWB_UID, ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);

可以看到:

android.uid.system android.uid.phone android.uid.log android.uid.nfc android.uid.bluetooth android.uid.shell android.uid.se android.uid.networkstack android.uid.uwb

等这类具备特殊 shared uid 的 app 都被赋予了 ApplicationInfo.FLAG_SYSTEM 标志,这是第一类 System App。

第2类 System App:特殊的路径

在 InitAppsHelper 下可以看到以下扫描 System app 路径的函数:

//frameworks/base/services/core/java/com/android/server/pm/InitAppsHelper.java private void scanSystemDirs(PackageParser2 packageParser, ExecutorService executorService) { File frameworkDir = new File(Environment.getRootDirectory(), "framework"); // Collect vendor/product/system_ext overlay packages. (Do this before scanning // any apps.) // For security and version matching reason, only consider overlay packages if they // reside in the right directory. for (int i = mDirsToScanAsSystem.size() - 1; i >= 0; i--) { final ScanPartition partition = mDirsToScanAsSystem.get(i); if (partition.getOverlayFolder() == null) { continue; } scanDirTracedLI(partition.getOverlayFolder(), /* frameworkSplits= */ null, mSystemParseFlags, mSystemScanFlags | partition.scanFlag, packageParser, executorService); } scanDirTracedLI(frameworkDir, null, mSystemParseFlags, mSystemScanFlags | SCAN_NO_DEX | SCAN_AS_PRIVILEGED, packageParser, executorService); if (!mPm.mPackages.containsKey("android")) { throw new IllegalStateException( "Failed to load frameworks package; check log for warnings"); } for (int i = 0, size = mDirsToScanAsSystem.size(); i scanDirTracedLI(partition.getPrivAppFolder(), /* frameworkSplits= */ null, mSystemParseFlags, mSystemScanFlags | SCAN_AS_PRIVILEGED | partition.scanFlag, packageParser, executorService); } scanDirTracedLI(partition.getAppFolder(), /* frameworkSplits= */ null, mSystemParseFlags, mSystemScanFlags | partition.scanFlag, packageParser, executorService); } }

对于安装到 mDirsToScanAsSystem 里面的应用会设置 msystemScanFlags(这个标记仅在 PMS 内部使用,实际上会对应上 ApplicationInfo.FLAG_SYSTEM):

private final int mSystemScanFlags; static final int SCAN_AS_SYSTEM = 1 //这里的systemPartitions正是下文的scanPartitions.addAll(mSystemPartitions); mPm = pm; …… mDirsToScanAsSystem = getSystemScanPartitions(); …… } …… private List getSystemScanPartitions() { final List scanPartitions = new ArrayList(); scanPartitions.addAll(mSystemPartitions); scanPartitions.addAll(getApexScanPartitions()); Slog.d(TAG, "Directories scanned as system partitions: " + scanPartitions); return scanPartitions; }

不得不说往下追溯 mSystemPartitions 复杂到让人有点犯迷糊了,此处直接放结果吧:

//frameworks/base/core/java/android/content/pm/PackagePartitions.java /** * The list of all system partitions that may contain packages in ascending order of * specificity (the more generic, the earlier in the list a partition appears). */ private static final ArrayList SYSTEM_PARTITIONS = new ArrayList(Arrays.asList( new SystemPartition(Environment.getRootDirectory(), PARTITION_SYSTEM, Partition.PARTITION_NAME_SYSTEM, true /* containsPrivApp */, false /* containsOverlay */), new SystemPartition(Environment.getVendorDirectory(), PARTITION_VENDOR, Partition.PARTITION_NAME_VENDOR, true /* containsPrivApp */, true /* containsOverlay */), new SystemPartition(Environment.getOdmDirectory(), PARTITION_ODM, Partition.PARTITION_NAME_ODM, true /* containsPrivApp */, true /* containsOverlay */), new SystemPartition(Environment.getOemDirectory(), PARTITION_OEM, Partition.PARTITION_NAME_OEM, false /* containsPrivApp */, true /* containsOverlay */), new SystemPartition(Environment.getProductDirectory(), PARTITION_PRODUCT, Partition.PARTITION_NAME_PRODUCT, true /* containsPrivApp */, true /* containsOverlay */), new SystemPartition(Environment.getSystemExtDirectory(), PARTITION_SYSTEM_EXT, Partition.PARTITION_NAME_SYSTEM_EXT, true /* containsPrivApp */, true /* containsOverlay */)));

因此可以得出,以下路径下的应用均被 PMS 视为 System App:

/system/framework /system/app /system/priv-app /system_ext/app /system_ext/priv-app /system_ext/overlay /odm/app /odm/priv-app /odm/overlay /oem/app /oem/overlay /vendor/app /vendor/priv-app /vendor/overlay /product/app /product/priv-app /product/overlay 特权应用

Privileged app,我们称之为 特权 app,主要原因是此类特权 app 可以使用 protectionLevel 为 signatureOrSystem 或 signature|privileged 的权限。实际上上面已经解释过这两个 protectionLevel 的关系,signature|privileged 已替代了 signatureOrSystem:

//frameworks/base/core/java/android/content/pm/PermissionInfo.java public static int fixProtectionLevel(int level) { if (level == PROTECTION_SIGNATURE_OR_SYSTEM) { level = PROTECTION_SIGNATURE | PROTECTION_FLAG_PRIVILEGED; } …… return level; }

回归到特权应用的定义:从 ApplicationInfo 的 isPrivilegedApp() 可以看出特权 app 是具有 ApplicationInfo.PRIVATE_FLAG_PRIVILEGED 标志的一类 app。

//frameworks/base/core/java/android/content/pm/ApplicationInfo.java public boolean isPrivilegedApp() { return (privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0; }

那么哪些应用会具备 ApplicationInfo.PRIVATE_FLAG_PRIVILEGED 标志?同样有两类。

第1类 Privileged App:特殊的 Uid

前面介绍 System App 的时候已经看到了,PMS 给部分特殊 Shared uid 赋予 System Flag 的同时也赋予了特权 Flag:

//frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID, ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); mSettings.addSharedUserLPw("android.uid.phone", RADIO_UID, ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); mSettings.addSharedUserLPw("android.uid.log", LOG_UID, ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); mSettings.addSharedUserLPw("android.uid.nfc", NFC_UID, ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); mSettings.addSharedUserLPw("android.uid.bluetooth", BLUETOOTH_UID, ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); mSettings.addSharedUserLPw("android.uid.shell", SHELL_UID, ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); mSettings.addSharedUserLPw("android.uid.se", SE_UID, ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); mSettings.addSharedUserLPw("android.uid.networkstack", NETWORKSTACK_UID, ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); mSettings.addSharedUserLPw("android.uid.uwb", UWB_UID, ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);

故在 PMS 中,以下 Uid 的应用既属于 System App,又属于 Privileged App:

/system/framework /system/app /system/priv-app /system_ext/app /system_ext/priv-app /system_ext/overlay /odm/app /odm/priv-app /odm/overlay /oem/app /oem/overlay /vendor/app /vendor/priv-app /vendor/overlay /product/app /product/priv-app /product/overlay

第2类 Privileged App:特殊的路径

不翻源码了,直接看 官方说明文档 吧: 在这里插入图片描述 简而言之,以下路径的应用被视为特权应用:

/system/framework /system/priv-app /system_ext/priv-app /odm/priv-app /vendor/priv-app /product/priv-app

注意,并非特权应用就可以申请并默认拥有所有 signature|privileged 权限,特权应用想使用某些系统特许权限,需要把白名单添加到 privapp-permissions-platform.xml 文件中(当然也可以单独建立一个文件,例如 com.android.systemui.xml 就是 SystemU I的特权白名单文件)。

关于特权白名单的更多信息也可参考:Android系统开发的特权白名单。

AOSP 包含可根据需要自定义的许可名单实现, 对于包含在 AOSP 中的应用,其权限已在 /etc/permissions/privapp-permissions-platform.xml 中列入许可名单。如果有不应授予的权限,请修改 XML,用 “deny-permission” 标记代替 “permission” 标记。示例:

...

最后,简单总结下系统应用和特权应用的区分:

Privileged app 一定也属于 System app,即 System app = Normal System app + Privileged app。当我们说起 system app 时,通常指的是前面提到的特定 uid 和特定目录中的 app,包含了普通的 system app 和 特权 app;而当我们说起有访问 System 权限或者 privileged 权限的 app 时,通常指特权 app;平台签名应用指的是应用的签名和 Android 包一样的应用,和是否是系统应用、特权应用无关。 历史漏洞

要介绍的同样源于 “系统应用”与CVE-2020-0391” 讲述的关于上述 “系统应用” 划分机制出现的历史漏洞。

广播的保护机制

Android 广播机制中有些广播并不是所有应用都可以随意发送的,比如可以在 frameworks/base/core/res/AndroidManifest.xml 看到 AOSP 定义的受保护的广播: 在这里插入图片描述 问题1:哪些应用可以发送这些受保护的广播?

看 AMS 的源码 可以知道有 7 个特殊 Uid 的应用和 isPersistent=true 的应用可以发送,否则将抛出安全异常: 在这里插入图片描述 问题2:那哪些应用可以定义受保护的广播?

在常规应用想定义受保护的广播是不被允许的: 在这里插入图片描述 但是看源码发现 “com.android.providers.telephony” 或 “com.android.systemui” 等应用却可以定义: 在这里插入图片描述 引用大佬的分析,ScanPackageUtils.java 文件中的 applyPolicy 函数中 if ((scanFlags & SCAN_AS_SYSTEM) != 0) 对立分支存在注释:“Non system apps cannot mark any broadcast as protected”: 在这里插入图片描述 即 system app 无法定义受保护广播,那么反过来就是 system app 能将广播标志为受保护的状态。

CVE-2020-0391

先看下美国国家漏洞库的相关描述: 在这里插入图片描述

有点狠,由于未强制执行的受保护广播,可能会以系统身份执行任意命令……

此漏洞于 2020年9月谷歌安全公告 发布,受影响版本仅限于 Android 9 和 Android 10: 在这里插入图片描述 Android 9 以前的版本不受影响,那肯定是 “改出来的” 漏洞……

直接看 Google 的 补丁代码: 在这里插入图片描述 可以看到,清除保护广播定义的代码跑到了 if ((scanFlags & SCAN_AS_PRIVILEGED) == 0) 的条件下了?

也就是说,只有特权应用能定义保护广播了,普通系统应用并不行。然而从上面的章节可以知道,特权应用的范围比系统应用小很多。那么那些原先由非特权的系统应用定义受保护的广播怎么办?还能生效且受系统保护吗?答案当然是不能……

漏洞修改方案是把错误分支代码改回去: 在这里插入图片描述 上文提到该漏洞的影响是任意命令执行,那么是哪个失效的“受保护”广播的杰作?

答案是紧随其后 2020年10月谷歌安全公告 公布的 CVE-2020-11164,但是它归属于闭源组件…… 在这里插入图片描述 看不到修改的代码,那就引用大佬的分析吧。

漏洞出现在一个名为 Perfdump 的系统应用上,路径是/system/app/Perfdump/Perfdump.apk,显然这不是一个特权应用,所以它定义的保护广播会失效,那么它定义了哪些没被保护的保护广播呢?其中之一是:

一看这名字觉得不得了,不会是一个执行代码的功能吧,猜对了,真的是这样。而且这个应用还是 system uid,也就是说,普通应用可以发一个广播,然后以 system 的身份执行命令。

POC 程序如下:

Intent intent = new Intent("android.perfdump.action.EXT_EXEC_SHELL"); intent.setClassName("com.qualcomm.qti.perfdump", "com.qualcomm.qti.perfdump.StaticReceiver"); intent.putExtra("callerPackageName", "com.test"); intent.putExtra("shellCommand", ); sendBroadcast(intent); SELinux

本章节的参考文章:

Google官方安全特性描述:SELinux;SELinux在Android中的应用;Android 用户组权限,SELinux心得总结;Android 8.1 安全机制 — SEAndroid & SELinux;Android 11魔形女系列漏洞分析;

SELinux (Security Enhanced Linux) 是 Linux 下的安全控制机制,为进程访问系统资源提供了访问控制(access control)策略。它由美国国家安全局(NSA)发起,发布于2000年12月(代码采用 GPL 许可发布)。并在 Linux Kernel 2.6 版本后,直接整合搭建在 Linux Security Module(LSM) 基础上,目前已经成为最受欢迎,使用最广泛的安全方案。而 SEAndroid 是 Google 在 Android 4.4 上正式推出的一套以 SELinux 为基础于核心的系统安全机制。

DAC&MAC

在 SELinux 出现之前,Linux 基于用户身份/用户组的 DAC(Discretionary Access Control)作为访问控制策略:即每个进程都有所属的 UID,每个文件都有所属的 UID/GID 以及文件模式(读写执行等), 一个进程是否可以访问某个文件就是基于 UID/GID/文件模式 来管理的(详情参见Android 用户组权限,SELinux心得总结)。换句话说,只要某个资源序属于该用于或该用户组,则该用户对该资源具有绝对控制权力。

DAC 的核心思想很简单,就是:进程理论上所拥有的权限与执行它的用户的权限相同。比如,以 root 用户启动 Browser,那么 Browser 就有 root 用户的权限,在 Linux 系统上能干任何事情。但这也就导致了 Linux DAC 明显的缺陷:Root 用户“无法无天”,几乎可以做任意事情。一旦入侵者拿到 root 权限,即已经完全掌控了系统。另外每一个进程默认都拿到对应这个用户的所有权限,可以改动/删除这个用户的所有文件资源,明显这个难以防止恶意软件。

所以在 DAC 之外设计了一个新的安全模型 MAC(Mandatory Access control,强制性访问控制),即系统针对每一项访问都进行严格的限制,具体的限制策略由开发者给出。SELinux 采用的就是这种细粒度的 MAC(Mandatory Access Control),对于 DAC 而言,资源的权限是由每个用户自己控制的,而 MAC 则将所有的权限收拢,由一个统一的管理者(SELinux) 统一来分配所有的资源权限,如果访问者没有事先分配到某个资源的权限,则不会允许访问。这样即使是 root 用户也要受到安全策略的约束。

MAC 的处世哲学非常简单:即任何进程想在 SELinux 系统中干任何事情,都必须先在安全策略配置文件中赋予权限。凡是没有出现在安全策略配置文件中的权限,进程就没有该权限。

SE Android

Android 是基于 Linux 实现的,但由于 Android 系统有着独特的用户空间,因此 SELinux 不能完全适用于 Android 系统。为此 NSA 针对 Android 系统,在 SElinux 基础上开发了 SEAndroid。

Android 系统是同时支持 DAC 与 SELinux 的,也就是说,在一个进程访问某个资源时,会按照如下规则进行权限控制:

首先根据 DAC 规则,检查进程权限,是否具有对应资源的读写/执行权限, 如果没有则拒绝执行;如果 DAC 规则检查通过,则执行 SELinux 安全规则的检查,如果不通过,则拒绝访问。 安全标签

在 SELinux 机制下,Android 中所有的对象 (进程/文件/socket/property) 都打上了标签 (label),进程访问对象时,SELinux 根据事先配置好的安全策略 (security policy) 判断访问者是否有权限。

一个标签通常有如下的形式:

user:role:type:mls_level

这样一个标签也通常被成为 Security Context,例如 SEAndroid 中,进程的 Security Context 可通过 ps -efZ 命令查看: 在这里插入图片描述 查看文件的安全上下文的命令则是 ls -lZ: 在这里插入图片描述 以进程 keychain 的标签 u:r:system_app:s0 为例,解释下标签的各个组成含义:

u 为 user 的意思,SEAndroid 中定义了一个 SELinux 用户,值为 ur 为 role 的意思,一个 u 可以属于多个 role,不同的 role 具有不同的权限。system_app 代表该进程所属的 Domain 为 system_app,对进程来说, Type 就是 Domain,system_app 需要什么权限,都需要通过 allow 语句在 te 文件中进行说明。s0 是 SELinux 为了满足军用和教育行业而设计的 Multi-Level Security(MLS)机制有关。简单点说,MLS 将系统的进程和文件进行了分级,不同级别的资源需要对应级别的进程才能访问。

在 Android 中,通常不用关心 user/role/msl_level,因为 user 一般只有 u, role 对于进程来说是 r,对其他对象是 object_r,而 msl_level 是 s0。需要重点关注的是 type,它用来标识对象的类型,其决定了该对象的所具备的能力。因此 Android中 的 SELinux 又称为基于 Type Enforcement Access Control(简称 TEAC,一般用 TE 表示)的安全机制。

引用一张表格,概括下对于 Android 进程的 SElinux Type: 在这里插入图片描述

策略规则

上文已经提到,SEAndroid 安全机制主要是使用对象安全上下文中的类型来定义安全策略,这种安全策略就称 Type Enforcement,简称 TE。在 Android 中,所有的 SELinux 策略文件都以 te 结尾。Android 的 SELinux 配置在 Android 源码中有两个目录:

/system/sepolicy /device///sepolicy

其中 /system/sepolicy 主要是 Android 原生已有的 SELinux 文件,包括所有 SELinux 标签以及策略文件 .te 的定义,一般不做修改。 在这里插入图片描述

所有以 .te 为后缀的文件经过编译之后,就会生成一个 sepolicy 文件。这个 sepolicy 文件会打包在 ROM 中,并且保存在设备上的根目录下: 在这里插入图片描述

根据 SELinux 规范,完整的 SELinux 策略规则语句格式为:

allow domains types:classes permissions; - Domain - 一个进程或一组进程的标签。也称为域类型,因为它只是指进程的类型。 - Type - 一个对象(例如,文件、套接字)或一组对象的标签。 - Class - 要访问的对象(例如,文件、套接字)的类型。 - Permission - 要执行的操作(例如,读取、写入)。 = allow : 允许主体对客体进行操作 = neverallow :拒绝主体对客体进行操作 = dontaudit : 表示不记录某条违反规则的决策信息 = auditallow :记录某项决策信息,通常 SElinux 只记录失败的信息,应用这条规则后会记录成功的决策信息。

使用政策规则时将遵循的结构示例:

语句: allow appdomain app_data_file:file rw_file_perms; 这表示所有应用域都可以读取和写入带有 app_data_file 标签的文件 常用命令

SELinux 可以在各种模式下实现:

强制模式(Enforcing):强制执行并记录 SELinux 安全策略。宽容模式(Permissive):仅记录但不强制执行 SELinux 安全政策。

默认状态都是强制模式,相应的在 Android 系统安全测试中,经常使用的几条 SELinux 相关命令如下:

命令作用adb shell getenforce查看当前手机的 SeLinux 是出于强制模式还是宽容模式adb shell setenforce 0将当前手机的 SeLinux 设置为宽容模式(需要root)adb shell setenforce 1将当前手机的 SeLinux 设置为强制模式

在强制模式下,非法操作会被阻止,并且尝试进行的所有违规行为都会被内核记录到 dmesg(内核日志) 和 logcat。我们可以通过以下命令来查看 SELinux 的相关安全日志:

adb shell logcat|grep avc adb shell dmesg |grep avc

比如捕获到的一条日志如下:

avc: denied { write } for pid=4663 comm="om.zte.engineer" name="brightness" dev="sysfs" ino=9451 scontext=u:r:radio:s0 tcontext=u:object_r:sysfs:s0 tclass=file permissive=0

上面日志反映的信息为:om.zte.engineer 这个进程,安全上下文为 “u:r:radio:s0”,想访问安全上下文为 “u:object_r:sysfs:s0” 的 sysfs 文件并执行写操作时,被 SELinux 访问控制策略拒绝访问了。

总结来说,SELinux 日志的整体格式为:

avc: denied {被拒绝的操作} for pid=进程pid comm=“进程名” scontext=u:r:源类型:s0 tcontext=u:r:目标类型:s0 tclass=访问类型 permissive=0

那么如何解决上述访问限制? 分析过程:

缺少什么权限: write 权限;谁缺少权限:scontext=u:r:radio:s0;对哪个文件缺少权限: tcontext=u:object_r:sysfs:s0;什么类型的文件:tclass=file;

所以解决方法是在:radio.te 中加入

allow radio sysfs:file write;

关于 te 文件具体的修改和编译方式,此处不讨论,请参见 Google官方安全特性描述:SELinux。

历史漏洞

回到本文学习 SELinux 的初心,本人在一次漏洞挖掘过程中发现一个可以获得以某 Uid=1000 的 System 应用的身份对系统任意文件进行读写的漏洞,本来想着美滋滋地以 Android APP代码执行历史漏洞与攻击面分析 所述的覆盖其他应用的动态执行文件(比如 /data/data/com.xxx.xxx 沙箱下的 so、dex、jar、apk 等,或者 odex 和 vdex 文件也行),就能十分容易地拿下一个任意代码执行漏洞。然而想象很美好,现实很骨感……SELinux 直接切断了我这种想法。

【补充】/data/dalvik-cache 路径下拥有大量各个进程动态加载文件 vdex、odex 文件(安全上下文为 u:object_r:dalvikcache_data_file:s0),然而 Android 已经限制了 app 标签的进程写 dalvikcache_data_file,即使是 system_app 也不行。

Android 的 SELinux 机制并不允许 system_app 标签的进程访问普通应用的沙箱文件(Type 为 app_data_file): 在这里插入图片描述 所以即使通过比如路径穿越的漏洞拿到 System 权限去读写系统文件,也是无法影响到普通应用的沙箱数据的,除非你读写的同样是 System 权限的应用(其文件 Type 则为 system_app_data_file)。

然而,历史上谷歌在 2021 年 Android 11 上出现过一个关于 SELinux 配置的低级错误……这就是CVE-2021-0691: 在这里插入图片描述 从美国国家漏洞库 NVD 对该漏洞的描述中不难看出漏洞正是允许了 system_app 去读写其他普通应用的沙箱数据……来看下公开的补丁信息中代码的前后差异: 在这里插入图片描述 没错,Google 居然允许了 system_app 写 apk_data_file 类型的文件……这“粗心大意”的操作难不成是“善心大发”,想给漏洞挖掘者留点缺口?这也警示了 Android 开发者,在给自己的系统增加 SELinux 安全策略的过程中,要考虑好其合理性和安全性,不能给自己“挖坑”。

京东安全实验室曾经发现的 Android 11 “魔性女”漏洞,正是基于此漏洞实现了绕过其他应用程序的沙箱访问限制,具体可参见京东官方文档:《魔形女漏洞白皮书》。

总结

本文介绍了 Android 的权限类型划分,并引出和解释了 System 应用 和 Privileged app 的划分,同时介绍了广播保护机制及其历史漏洞。最后介绍了 Android SELinux 机制及其相关历史漏洞。

以上安全机制是 Android 漏洞挖掘过程中一定会遇到的“限制”和“阻碍”,了解清楚其机制才能减少不必要的踩坑并寻找正确的挖洞和利用方向。事实上,从本文提及的 Google 的历史漏洞也不难看出,Google 的工程师们在 AOSP 源码的配置文件或安全机制中也会出现一些低级的错误,阅读源码的过程中如果细心点且敢于质疑,可能也能挖到意想不到的漏洞……



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3