Skip to content

5x01 - SELinux on Android

SELinux (Security-Enhanced Linux) 是 Android 强制访问控制 (MAC) 的核心。

SELinux 的目标不是"阻止一切 bug",而是把 bug 的影响范围收窄:即使某个进程出现漏洞,进程也只能在其 domain 授权的范围内访问资源。

1. 运行模式

  • Permissive: 仅记录违规行为,不拦截(通常用于开发调试)。
  • Enforcing: 强制拦截违规行为(生产环境默认)。

研究时经常需要明确系统处于哪种模式,因为 permissive 环境下的行为与生产环境可能完全不同。

2. 核心概念

  • Domain (域): 进程的安全上下文(如 u:r:untrusted_app:s0)。
  • Type (类型): 资源的安全上下文(如 u:object_r:app_data_file:s0)。
  • Policy (策略): 定义了哪个 Domain 可以对哪个 Type 执行什么操作。

补充几个在 Android 上很常见的概念:

  • attribute:一组 type/domain 的集合,用于批量授权
  • file context:路径到 type 的映射(文件/目录最终是什么标签)
  • transition:进程执行某个入口后 domain 切换(例如某服务从 init 启动后进入特定域)

3. SELinux 策略文件结构

Android SELinux 策略由多个文件组成,理解其结构是分析和绕过的基础。

3.1 策略文件位置

AOSP 源码中的策略目录

system/sepolicy/
├── public/          # 公开 API,可被 vendor 引用
├── private/         # 系统私有策略
├── vendor/          # vendor 可定制部分
├── prebuilts/       # 预编译策略
└── Android.bp       # 构建配置

设备上的策略文件

/sys/fs/selinux/policy           # 当前加载的二进制策略
/sepolicy                        # boot image 中的策略(旧版本)
/vendor/etc/selinux/             # vendor 策略
/system/etc/selinux/             # system 策略
/odm/etc/selinux/                # ODM 策略

3.2 关键策略文件类型

文件类型扩展名用途
Type Enforcement.te定义 domain/type 及 allow 规则
File Contextsfile_contexts路径到 type 的映射
Property Contextsproperty_contexts系统属性到 type 的映射
Service Contextsservice_contextsBinder 服务到 type 的映射
Seapp Contextsseapp_contexts应用进程 domain 分配规则

3.3 策略规则语法

allow 规则

allow source_domain target_type:target_class permissions;

# 示例:允许 untrusted_app 读取 app_data_file 类型的文件
allow untrusted_app app_data_file:file { read open getattr };

type_transition 规则

type_transition source_domain target_type:class new_type;

# 示例:init 执行 /system/bin/app_process 后切换到 zygote domain
type_transition init zygote_exec:process zygote;

neverallow 规则

neverallow source_domain target_type:class permissions;

# 示例:禁止 untrusted_app 访问 kernel 文件
neverallow untrusted_app kernel:file *;

3.4 从设备提取策略

bash
# 提取二进制策略
adb pull /sys/fs/selinux/policy sepolicy.bin

# 提取 file_contexts
adb pull /system/etc/selinux/plat_file_contexts
adb pull /vendor/etc/selinux/vendor_file_contexts

# 提取 property_contexts
adb pull /system/etc/selinux/plat_property_contexts

4. 策略审计工具

分析 SELinux 策略需要专用工具,以下是常用的审计工具。

4.1 sesearch

sesearch 用于搜索策略中的规则。

bash
# 安装(Ubuntu/Debian)
sudo apt install setools

# 搜索所有允许 untrusted_app 的规则
sesearch -A -s untrusted_app sepolicy.bin

# 搜索特定权限
sesearch -A -s untrusted_app -t app_data_file -c file sepolicy.bin

# 搜索 type_transition 规则
sesearch -T -s init sepolicy.bin

# 搜索 neverallow 规则
sesearch --neverallow sepolicy.bin

输出示例

allow untrusted_app app_data_file:file { append create getattr ioctl ... };
allow untrusted_app app_data_file:dir { add_name create getattr ... };

4.2 seinfo

seinfo 用于查看策略的元信息。

bash
# 查看所有 type
seinfo -t sepolicy.bin

# 查看所有 attribute
seinfo -a sepolicy.bin

# 查看特定 type 的 attribute
seinfo -t untrusted_app -x sepolicy.bin

# 查看所有 class
seinfo -c sepolicy.bin

# 统计信息
seinfo sepolicy.bin

输出示例

Types: 1247    Attributes: 89
Classes: 83    Permissions: 267
Booleans: 0    Sensitivities: 1
Categories: 1024

4.3 audit2allow

audit2allow 从 AVC denial 日志生成 allow 规则。

bash
# 从 dmesg 生成规则
adb shell dmesg | audit2allow -p sepolicy.bin

# 从 logcat 生成规则
adb logcat -d | grep 'avc: denied' | audit2allow -p sepolicy.bin

# 输出可直接使用的 .te 文件
adb shell dmesg | audit2allow -p sepolicy.bin -o fix.te

# 生成 reference policy 模块
adb shell dmesg | audit2allow -p sepolicy.bin -R

注意audit2allow 生成的规则可能过于宽松,应人工审核后再使用。

4.4 其他实用工具

sepolicy-analyze (AOSP)

bash
# 检查 neverallow 违规
sepolicy-analyze sepolicy.bin neverallow -n neverallow_rules

# 查找可达路径
sepolicy-analyze sepolicy.bin permissive

# 查找 attribute 成员
sepolicy-analyze sepolicy.bin attribute domain

apol (SETools GUI)

bash
# 图形化策略分析
apol sepolicy.bin

5. 安全审计

  • Neverallow: 策略中绝对禁止出现的规则,用于防止策略过于宽松。
  • MLS/MCS: 通过安全级别/类别(Android 常见的是 MCS categories)来做额外隔离;具体落地依设备与厂商策略而定。

6. AVC Denial 排查方法

最常见的现场问题是遇到 avc: denied

6.1 收集信息

  • adb logcat | grep -i 'avc: denied'
  • 若存在内核侧日志:adb shell dmesg | grep -i avc

典型日志会包含:

  • scontext(来源 domain)
  • tcontext(目标 type)
  • tclass(对象类别:file/dir/socket/process 等)
  • perm(尝试的操作:read/write/execute/connectto 等)

6.2 判断性质

  • 若是系统组件正常功能被拦截:说明策略缺规则或上下文打标错误
  • 若是低权限进程试图访问高敏感资源:可能是攻击尝试或漏洞链路

6.3 常见根因

  • 文件/目录标签不对(file_contexts 配置或打包问题)
  • 服务进程 domain 不对(init 启动配置或 transition 不生效)
  • 访问路径不对(实际访问了不同的文件/设备节点)

7. SELinux Bypass 技术

7.1 策略漏洞利用

过于宽松的 allow 规则

vendor 定制策略经常出现过于宽松的规则:

# 危险:允许 shell 访问所有 vendor 文件
allow shell vendor_file:file *;

# 危险:允许 app 访问设备节点
allow untrusted_app device:chr_file { read write };

审计方法

bash
# 查找 wildcard 权限
sesearch -A sepolicy.bin | grep '\*'

# 查找敏感 type 的访问者
sesearch -A -t kernel_data_file sepolicy.bin
sesearch -A -t proc_kmsg sepolicy.bin

attribute 滥用

当一个 domain 被加入过于宽泛的 attribute 时,会继承大量权限:

bash
# 查看 domain 所属的 attribute
seinfo -t vendor_init -x sepolicy.bin

# 查看 attribute 拥有的权限
sesearch -A -s coredomain sepolicy.bin | wc -l

7.2 内核模块注入绕过

SELinux 策略在内核空间执行,如果攻击者能加载内核模块,可以直接绕过 SELinux。

攻击原理

  1. 利用内核漏洞获取内核代码执行能力
  2. 修改 selinux_enforcing 变量(设为 0)
  3. 或 hook avc_has_perm 函数使其永远返回成功
  4. 或直接修改进程的 security credentials

内核符号定位

c
// selinux_enforcing 控制 enforcing/permissive 模式
// 位置:security/selinux/selinuxfs.c
extern int selinux_enforcing;

// avc_has_perm 是权限检查的核心函数
// 位置:security/selinux/avc.c
int avc_has_perm(struct selinux_state *state,
                 u32 ssid, u32 tsid,
                 u16 tclass, u32 requested,
                 struct common_audit_data *auditdata);

防御检测

bash
# 检查 enforcing 状态是否被篡改
adb shell cat /sys/fs/selinux/enforce

# 检查内核完整性(如果有 dm-verity)
adb shell getprop ro.boot.verifiedbootstate

7.3 逻辑错误利用

SELinux 策略逻辑错误可能导致意外的权限泄露。

常见逻辑错误类型

  1. type_transition 链断裂:进程未能切换到预期的受限 domain
  2. 条件规则误用:booleans 配置错误导致规则生效/失效异常
  3. domain 继承问题:fork 后子进程继承了不应有的权限
  4. MCS 分类错误:不同安全级别的进程能相互访问

检测方法

bash
# 查找可能的 transition 问题
sesearch -T sepolicy.bin | grep -v 'type_transition'

# 检查 permissive domain(不应存在于生产环境)
seinfo -t sepolicy.bin | xargs -I {} sesearch -A -s {} -p permissive sepolicy.bin

8. CVE-2025-0078

幻觉.

9. Android Rooting 框架的 SELinux 绕过

现代 Android root 工具需要处理 SELinux,以下是常见框架的绕过策略。

9.1 Magisk 的 SELinux 处理

Magisk 采用"最小修改"策略,不完全禁用 SELinux:

核心机制

  1. MagiskSU:运行在独立的 u:r:magisk:s0 domain
  2. 策略注入:动态 patch 策略添加必要规则
  3. Zygisk:hook zygote 但保持原有安全上下文

策略 patch 示例

# Magisk 注入的规则
allow magisk * * *;  # magisk domain 拥有完整权限
allow su * * *;      # su 进程权限
type_transition zygote magisk_file:process magisk;

检测 Magisk 策略修改

bash
# 对比原始策略和当前策略
sesearch -A -s magisk sepolicy.bin  # 如果有输出说明被修改
seinfo -t magisk sepolicy.bin       # 检查是否存在 magisk type

9.2 KernelSU 的 SELinux 处理

KernelSU 在内核层面实现,对 SELinux 的处理更底层:

核心机制

  1. 内核 hook:直接 hook avc_has_perm 检查
  2. 条件绕过:仅对授权进程绕过 SELinux 检查
  3. 最小化修改:不修改策略文件本身

内核实现概念

c
// KernelSU hook 逻辑(简化)
int ksu_avc_has_perm_hook(u32 ssid, u32 tsid, u16 tclass, 
                          u32 requested, struct common_audit_data *ad)
{
    // 检查是否是授权的 root 进程
    if (ksu_is_allow_su()) {
        return 0;  // 直接允许
    }
    // 否则调用原始函数
    return original_avc_has_perm(ssid, tsid, tclass, requested, ad);
}

9.3 APatch 的方案

APatch 结合了 Magisk 和 KernelSU 的特点:

  1. 内核 patch:修改内核获取 root 权限
  2. 策略修改:运行时注入 SELinux 规则
  3. 模块系统:支持 Magisk 模块

9.4 检测 Root 框架的 SELinux 修改

bash
# 检查异常 domain
seinfo -t sepolicy.bin | grep -E 'magisk|su|ksu'

# 检查过于宽松的规则
sesearch -A sepolicy.bin | grep 'allow.*\* \*'

# 检查 permissive domain
sesearch -A --permissive sepolicy.bin

# 内核完整性检查
adb shell cat /proc/version
adb shell cat /proc/kallsyms | grep -E 'ksu|magisk'

10. 实际利用案例

10.1 案例:Vendor 策略过于宽松

场景:某设备的 vendor 策略允许 system_app 访问所有 vendor_data_file

发现过程

bash
sesearch -A -s system_app -t vendor_data_file sepolicy.bin
# 输出:allow system_app vendor_data_file:file *;

利用

  1. 找到以 system_app 权限运行的应用(如预装应用)
  2. 利用该应用的漏洞(如 intent 注入)
  3. 读取 vendor 目录下的敏感数据(如密钥、配置)

10.2 案例:Type Transition 缺失

场景:自定义服务未正确配置 domain transition,以 init domain 运行

发现过程

bash
# 检查服务进程的 domain
adb shell ps -AZ | grep vendor_service
# 输出:u:r:init:s0  ... vendor_service  # 应该是专用 domain

# 检查是否缺少 transition 规则
sesearch -T -s init -t vendor_service_exec sepolicy.bin
# 无输出,说明缺少 transition

利用

  1. 服务以 init domain 运行,拥有大量权限
  2. 找到服务中的漏洞(如命令注入、路径遍历)
  3. 利用 init 的权限访问敏感资源

10.3 案例:MCS 隔离失效

场景:不同用户的应用能访问彼此的数据

发现过程

bash
# 检查不同用户应用的安全上下文
adb shell ps -AZ | grep u0_a100
# u:r:untrusted_app:s0:c100,c256,c512 ...

adb shell ps -AZ | grep u10_a100  
# u:r:untrusted_app:s0:c100,c256,c512 ... # 应该有不同的 category

利用

  1. 多用户场景下,不同用户的应用共享相同的 MCS category
  2. 应用可以跨用户访问数据

11. 审计 checklist

  1. 关键守护进程是否运行在预期 domain
  2. 高风险资源(设备节点、socket、属性)是否存在异常可达路径
  3. vendor 定制策略是否扩大了 privileged domain 的权限
  4. neverallow 是否被绕过(构建期检测与实际镜像一致性)
  5. 是否存在 permissive domain
  6. 敏感 type 的访问者列表是否合理
  7. type_transition 链是否完整
  8. MCS category 分配是否正确

12. 常用命令(设备侧)

  • 查看进程上下文:adb shell ps -AZ | head(工具可用性依赖环境)
  • 查看文件上下文:adb shell ls -lZ <path>
  • 查看当前 enforcing:adb shell getenforce

参考(AOSP)