16、Binder与SELinux策略绕过:SELinux策略编写缺陷、binder_call权限绕过、avc_audit日志分析

好,咱们今天聊点硬核的。Binder 和 SELinux 的组合,说白了就是 Android 安全体系里的「门锁」和「监控摄像头」。Binder 负责开门关门,SELinux 盯着谁该进谁不该进。但这两者一旦配合出问题,漏洞就来了。

我在做系统安全审计时,见过太多「策略写得太松」或者「Binder 调用没校验」导致的提权案例。今天我就把这块的坑,一个一个给你掰开讲。

16.1 SELinux 策略编写中的常见缺陷

SELinux 策略文件(.te 文件)是定义进程权限的核心。但很多开发者写策略时,容易犯几个低级错误。

16.1.1 过度授权的 allow 规则

最常见的问题就是「图省事,直接给了大权限」。比如:

allow system_app untrusted_app:file { read write open };

这条规则允许 system_app 对 untrusted_app 的文件进行读写。你想想看,这等于把自家保险柜的钥匙给了陌生人。我遇到过某厂商的预装应用,就因为这种规则被第三方应用提权,直接读取了通讯录数据库。

⚠️ 避坑指南: 我曾经审计过一个项目,发现某个系统服务居然允许所有域(domain)调用它的 Binder 接口。原因就是策略里写了个 allow * system_server:binder { call };。这种通配符写法,等于把大门敞开了。

16.1.2 缺失的 neverallow 规则

neverallow 是 SELinux 的「底线规则」。但很多厂商为了兼容性,会故意删掉或放宽这些规则。比如:

neverallow { domain -system_app } system_server:binder { call };

这条规则禁止非 system_app 域调用 system_server 的 Binder。如果删掉它,任何第三方应用都能直接跟系统服务通信。嗯,这里要注意,这种漏洞在 Android 8 之前特别常见。

16.1.3 类型转换(type_transition)配置错误

类型转换用于控制进程创建子进程时的域切换。如果配置不当,可能导致子进程继承父进程的高权限域。

type_transition init_t shell_exec:process shell_t;

这条规则允许 init 域执行 shell 二进制时切换到 shell_t 域。但如果某个恶意进程伪造了 shell 二进制,就能从低权限域跳到高权限域。我见过一个案例,攻击者利用这个漏洞从普通应用直接拿到了 root 权限。

16.2 binder_call 权限绕过分析

Binder 是 Android 进程间通信的核心。SELinux 通过 binder_call 权限控制哪个进程能调用哪个服务的 Binder 接口。但绕过方式也不少。

16.2.1 利用服务端未校验调用方身份

有些系统服务在收到 Binder 请求后,不会二次校验调用方的 UID 或 PID。即使 SELinux 策略正确,服务端代码也可能存在漏洞。

// 错误示例:未校验调用方身份
public void onTransact(int code, Parcel data, Parcel reply, int flags) {
    String action = data.readString();
    // 直接执行高危操作
    executeAction(action);
}

正确的做法是:

// 正确示例:校验调用方 UID
public void onTransact(int code, Parcel data, Parcel reply, int flags) {
    int callingUid = Binder.getCallingUid();
    if (callingUid != Process.SYSTEM_UID) {
        throw new SecurityException("Unauthorized caller");
    }
    String action = data.readString();
    executeAction(action);
}
💡 个人经验: 我在分析某款手机的系统服务时发现,它的 Binder 接口居然没有做任何 UID 校验。攻击者只需要通过普通应用调用这个接口,就能执行系统级命令。说白了,这就是「门锁没锁,但监控(SELinux)还在工作」的典型场景。

16.2.2 利用 Binder 上下文对象(Binder Context Object)

Binder 允许服务端返回一个 Binder 对象给客户端。如果这个对象没有正确设置 SELinux 上下文,客户端就能绕过策略限制。

// 服务端返回一个未设置上下文的 Binder 对象
IBinder binder = new MyBinder();
reply.writeStrongBinder(binder);

正确的做法是:

// 设置 Binder 对象的 SELinux 上下文
IBinder binder = new MyBinder();
binder.setSELinuxContext("u:object_r:my_service:s0");
reply.writeStrongBinder(binder);

我见过一个案例,某个系统服务返回了一个 Binder 对象,但没设置上下文。攻击者拿到这个对象后,就能调用原本被 SELinux 禁止的方法。

16.3 avc_audit 日志分析实战

avc_audit 日志是 SELinux 的「黑匣子」。每次权限被拒绝,都会生成一条日志。学会看这些日志,是定位漏洞的关键。

16.3.1 日志格式解析

典型的 avc_audit 日志长这样:

avc: denied { call } for pid=1234 comm="app_process" scontext=u:r:untrusted_app:s0 tcontext=u:r:system_server:s0 tclass=binder

拆开来看:

字段 含义 示例值
denied 被拒绝的操作 { call }
pid 发起请求的进程 ID 1234
comm 发起请求的进程名 app_process
scontext 源安全上下文(调用方) u:r:untrusted_app:s0
tcontext 目标安全上下文(被调用方) u:r:system_server:s0
tclass 对象类别 binder

16.3.2 如何从日志中定位策略缺陷

假设你看到这样一条日志:

avc: denied { read write } for pid=5678 comm="system_app" scontext=u:r:system_app:s0 tcontext=u:r:untrusted_app_file:s0 tclass=file

这条日志说明 system_app 试图读写 untrusted_app_file 类型的文件。如果这个操作是正常的业务需求,那就需要在策略中添加 allow 规则。但如果这个操作不应该发生,那就说明存在权限滥用。

🔍 调试技巧: 我习惯在开发阶段开启 SELinux 的 permissive 模式,收集所有 avc_audit 日志。然后通过 audit2allow 工具自动生成需要的 allow 规则。但注意,生产环境一定要切回 enforcing 模式。

16.3.3 利用 avc_audit 发现绕过攻击

当攻击者尝试绕过 SELinux 策略时,avc_audit 日志会留下痕迹。比如:

avc: denied { call } for pid=9999 comm="malicious_app" scontext=u:r:untrusted_app:s0 tcontext=u:r:system_server:s0 tclass=binder

这条日志表明 untrusted_app 试图调用 system_server 的 Binder 接口。如果这个调用被 SELinux 拒绝,说明策略是有效的。但如果日志中出现了大量类似的 denied 记录,那就说明有人在尝试暴力破解。

我遇到过一种情况:攻击者通过修改 Binder 事务的上下文对象,让 SELinux 误判调用方的身份。这种情况下,avc_audit 日志中会出现 scontext 和实际进程不匹配的记录。比如:

avc: denied { call } for pid=9999 comm="malicious_app" scontext=u:r:system_app:s0 tcontext=u:r:system_server:s0 tclass=binder

注意看,scontext 是 system_app,但 comm 却是 malicious_app。这就是典型的上下文伪造攻击。

16.4 知识体系总览

下面这张图总结了 Binder 与 SELinux 策略绕过的核心逻辑:

Binder与SELinux策略绕过知识体系 攻击入口 SELinux策略编写缺陷 过度授权 / 缺失neverallow / 类型转换错误 binder_call权限绕过 服务端未校验 / 上下文对象伪造 avc_audit日志分析 日志解析 / 定位缺陷 / 发现绕过 漏洞点 • allow规则过于宽松 • neverallow规则缺失 漏洞点 • 服务端未校验UID/PID • Binder对象未设置上下文 分析手段 • 日志字段解析 • 上下文不匹配检测 攻击结果:权限提升 / 系统服务劫持 防御:最小权限原则 + 服务端校验 + 日志监控

这张图展示了从攻击入口到最终防御的完整链路。你可以看到,SELinux 策略缺陷、Binder 权限绕过和日志分析这三块是环环相扣的。任何一个环节出问题,都可能导致整个安全体系崩溃。

📌 核心要点:
  • SELinux 策略要遵循最小权限原则,别图省事写通配符
  • Binder 服务端必须校验调用方身份,别依赖 SELinux 做唯一防线
  • avc_audit 日志是发现攻击的重要线索,别忽视任何一条 denied 记录

好了,这一章的内容就到这儿。记住,安全是一个系统工程,Binder 和 SELinux 只是其中的两个环节。但把这两个环节守住了,大部分提权攻击就挡在门外了。

公众号:蓝海资料掘金营,微信deep3321