从run-as到setuid:探秘Android应用沙盒的“特权钥匙”

张开发
2026/5/19 18:52:52 15 分钟阅读
从run-as到setuid:探秘Android应用沙盒的“特权钥匙”
1. 揭开run-as的神秘面纱Android开发者的后门钥匙第一次在未root的手机上使用run-as命令时我仿佛发现了新大陆。这个看似普通的命令竟然能让我以root身份直接访问debug应用的沙盒目录。记得当时我正在调试一个SharedPreferences数据异常的问题通过adb shell run-as com.example.app瞬间就进入了/data/data/com.example.app/shared_prefs目录那种感觉就像拿到了万能钥匙。run-as的本质是Android为开发者预留的特殊通道。在Linux系统中每个应用都被严格限制在自己的沙盒里/data/data/package_name普通用户无权访问其他应用的数据。但debug应用是个例外——通过run-as包名开发者可以绕过这个限制。这背后的魔法源自Linux古老的setuid权限机制。就像酒店万能门卡只能由经理授权使用一样run-as也是在特定条件下debug模式才生效的临时特权。2. Linux权限体系的隐藏王牌setuid机制解析2.1 从rwx到s权限位的进化史在Linux文件权限中除了常见的rwx读、写、执行还有两个特殊权限位ssetuid和gsetgid。当可执行文件被设置setuid位时无论谁执行它都会以文件所有者的权限运行。最经典的例子是/usr/bin/passwd$ ls -l /usr/bin/passwd -rwsr-xr-x 1 root root 59976 Nov 24 2022 /usr/bin/passwd普通用户修改密码时需要改写/etc/shadow文件root专属正是setuid让这个操作成为可能。Android的run-as也是同样原理$ ls -l /system/bin/run-as -rwsr-sr-x 1 root shell 13008 2023-03-31 14:37 /system/bin/run-as2.2 setuid的双刃剑特性我在一次安全审计中深刻体会到setuid的危险性。某个系统工具因为存在缓冲区溢出漏洞攻击者通过它获得了root权限。这就是为什么Android对run-as做了严格限制仅限debug应用android:debuggabletrue只能访问目标应用的沙盒目录执行用户必须与目标应用UID相同通过以下命令可以验证这些限制# 尝试访问非debug应用失败 adb shell run-as com.secure.app ls # 输出run-as: package not debuggable # 尝试访问其他目录失败 adb shell run-as com.example.app ls /data/system # 输出opendir failed: Permission denied3. Android沙盒模型的特殊设计3.1 Zygote的降权魔法所有Android应用进程都源自Zygote如果直接继承其root权限将极其危险。在forkAndSpecializeCommon()过程中系统会完成以下关键操作根据应用的AndroidManifest.xml分配UID/GID挂载应用专属的/data/data子目录设置SELinux安全上下文这就像给每个应用分配了独立的公寓而run-as相当于物业给开发商开发者的临时门禁卡。通过ps命令可以看到权限差异# Zygote进程 USER PID PPID NAME root 123 1 zygote # 普通应用进程 u0_a123 456 123 com.example.app3.2 共享库的特殊权限在调试NDK项目时我发现/data/data/pkg/lib目录的权限很特殊adb shell ls -l /data/data/com.example.app drwxr-xr-x system system lib drwx------ u0_a123 u0_a123 files这种system:system的权限设计使得多个应用可以共享相同的.so文件。但要注意从Android 7.0开始即使使用run-as也无法修改这些文件这是SELinux加强的结果。4. 实战利用run-as进行高效调试4.1 数据库调试技巧当应用数据库出现问题时我常用这个流程进入沙盒目录adb shell run-as com.example.app导出数据库文件cp databases/app.db /sdcard/使用DB Browser等工具分析adb pull /sdcard/app.db4.2 SharedPreferences实时监控对于XML格式的偏好设置文件可以直接观察修改run-as com.example.app cat shared_prefs/config.xml更高效的方法是使用watch命令实时监控adb shell watch -n 1 run-as com.example.app cat shared_prefs/config.xml4.3 Native层崩溃分析当遇到NDK崩溃时通过run-as获取关键信息run-as com.example.app ls -l /data/tombstones run-as com.example.app cat /data/tombstones/tombstone_055. 安全边界与最佳实践5.1 Debug模式的风险控制我曾遇到一个案例测试版APK泄露导致攻击者利用run-as窃取数据。因此必须发布前确保android:debuggablefalse使用Gradle自动管理构建类型android { buildTypes { release { debuggable false ... } } }5.2 敏感文件保护策略对于必须共享的文件建议采用以下模式// 加密存储敏感配置 FileOutputStream fos openFileOutput(config.enc, MODE_PRIVATE); fos.write(encryptData(rawData)); fos.close();避免使用危险的全局可读写权限// 危险示例Android 7.0已废弃 openFileOutput(data.txt, MODE_WORLD_READABLE);6. 从setuid看Android安全演进早期Android版本中run-as的权限控制相对宽松。直到Android 4.4引入SELinux才真正构建起完整的安全体系。现在即使使用run-as不能绕过SELinux策略avc: denied日志受限于Capability机制如不能执行ptrace受限于命名空间隔离不能访问其他应用的procfs通过以下命令可以观察这些限制adb shell run-as com.example.app ls /proc/self/attr/current # 输出u:r:untrusted_app:s0在逆向分析过程中我发现新版Android甚至对run-as本身也做了限制——必须满足以下条件之一当前shell是root当前用户与目标应用UID匹配目标应用是debuggable这种精细化的权限管理正是Android安全模型成熟的体现。

更多文章