ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

某 Android 4.4 TV 设备的提权与持久化机制分析

某 Android 4.4 TV 设备的提权与持久化机制分析 此文章为AI生成过程为AI辅助完成。摘要本文以一台已停止维护的 Android 4.4 YunOS TV 设备为研究对象分析其从普通 adb shell 权限到持久 uid0 的完整研究路径。研究过程中涉及多个技术点Dirty COWCVE-2016-5195文件写原语Android 4.4 MountService / vold / ASEC 存储栈default container JNI 动态加载与进程注入run-as 的 file capabilities 机制init 管理的 vold 服务与ctl.restart机制FIBMAP BLKROSET 裸块写入持久化厂商硬件看门狗与安全回滚机制最终实现了一个重启后依然有效的受限 root shell并分析了完整 root 能力按需获取的工程方法。一、背景与环境1.1 设备概况测试设备为一台老式运营商定制 TV 盒子主要环境如下项目内容芯片平台某电视芯片平台CPUARM Cortex-A53ARMv7系统Android 4.4.4 定制 YunOS内核Linux 3.10.40签名test-keysSELinuxpermissive / disabled调试通道adb over Wi-Fi初始权限uid2000(shell)由于设备发布较早内核停留在 2014 年前后版本未修补 Dirty COW。1.2 初始能力与限制通过 adb 可以获得text复制uid2000(shell) gid2000(shell)可执行普通 shell 命令但无法写系统分区访问其他应用私有目录修改系统包状态以 root 启动服务打开 system 块设备因此本文的目标为在用户态找到一条稳定、可验证、可持久化的提权路径同时理解厂商的多层安全保护机制。二、侦察阶段权限、分区与安全机制2.1 系统分区只读保护设备 system 对应块设备处于内核只读状态text复制/sys/block/mmcblk.../ro 1这意味着正常情况下系统不能写自己的 system 分区普通mount -o rw,remount /system无法生效即使获得完整 root强行 remount 也可能触发系统复位。因此后续必须寻找不依赖 remount的系统修改方式。2.2 关键 UID 模型Android 4.4 的权限模型按 UID 和能力划分。侦察中发现几个关键身份UID身份值得关注的能力2000shelladb 调试1000system / 系统应用部分系统权限10001default containerASEC 容器权限10023预置服务少量系统权限其中 default container 进程拥有text复制ASEC_CREATE ASEC_MOUNT_UNMOUNT ASEC_DESTROY这些权限与 vold 的 ASEC 容器操作相关成为后续代码执行链的重要节点。2.3 看门狗与安全回滚设备上同时存在多层“看门狗”类机制芯片平台硬件看门狗底层异常或挂起时直接复位 SoC。Android framework Watchdogsystem_server 关键服务阻塞或崩溃时重启系统。YunOS 安全回滚守护监控系统文件和关键业务发现改动后可能回滚并重启。实际观察到的现象操作结果直接禁用系统包进程被 Kill或系统重启破坏系统 APK广告短暂消失随后可能被恢复remount /system秒级重启pkill -9 vold整机重启setprop ctl.restart vold可较安全地重启 voldFIBMAP 裸块写不触发 remount 看门狗这提示我们上层文件系统操作更容易触发保护机制绕过 mount 层的底层写入反而可能更稳定。三、Dirty COW稳定的文件写原语3.1 原理CVE-2016-5195 的核心是 Linux 内核 COW 处理中的竞态条件进程通过mmap(MAP_PRIVATE)映射只读文件一个线程执行madvise(MADV_DONTNEED)尝试丢弃私有映射另一个线程向/proc/self/mem写入数据内核在竞态窗口内把数据写入只读文件的 page cache。因此Dirty COW 本身不是直接提权而是对任意可读文件实现“绕过文件权限的写原语”。3.2 工程化实现研究中使用逐页写入器text复制for each page: mmap target start madvise thread start /proc/self/mem write thread wait until target page matches payload关键点每个页独立竞争成功一页再处理下一页写入完成后立刻读回校验页面不匹配就继续重试直到超时。3.3 限制Dirty COW 有明确边界只改 page cache不一定写入磁盘重启后可能丢失。对 system 分区不稳定如果底层块设备 ro1page cache 改动很难持久化。ARM I-cache 影响修改正在运行进程的代码页不一定会被 CPU 立即取到新指令往往需要重新 exec。需要目标文件可读对不可读文件无法映射。这些限制决定了Dirty COW 适合作为“进程内文件覆盖原语”但持久化必须另找办法。四、Android 4.4 存储栈MountService、vold 与 ASEC4.1 MountService 事务号Android 4.4 的 MountService 通过 Binder 暴露给上层。经过 AIDL 顺序分析和行为探测可以确认部分事务号事务号方法9getStorageUsers10getVolumeState11createSecureContainer14 / 15mount / unmountSecureContainer16isSecureContainerMountedcreateSecureContainer需要 ASEC_CREATE 权限。4.2 Defcontainer JNI 落点default container 进程在包安装、空间计算等场景会动态加载 JNItext复制/system/lib/libdefcontainer_jni.so触发路径之一text复制pm install -l → DefaultContainerService.calculateDirectorySize → MeasurementUtils.measureDirectory → System.loadLibrary(defcontainer_jni)这提供了一个重要机会如果能够修改该 JNI 的代码页就可以在 JNI 被加载时执行攻击者控制的 constructor从而以容器进程的 UID 执行代码。该 UID 为 10001拥有 ASEC 相关权限但还不是 root。4.3 ASEC 格式化的意外原计划设想是text复制uid10001 调用 createSecureContainer → vold 创建 ASEC 容器 → vold 执行某个外部格式化程序 → 外部程序被替换获得 root但实际结果并非如此该设备上的 vold 处理 ASEC 时并不总是 exec 外部二进制格式化逻辑更接近内部链接库调用对文件系统级 trap 方案无效对内部库入口做页缓存 patch也未能被实际调用到。这个失败点带来一个很重要经验不能假设 Android 4.4 的 vold 一定通过外部程序完成格式化必须动态验证其真实调用链。五、run-as 的 file capabilities 利用5.1 非 setuid 的 run-as传统 Android 提权常寻找 setuid root 程序。但测试设备上的run-as没有 setuid 位却可以通过 shell 切换到应用 UID其能力来自file capabilities。也就是说权限存在于 inode 的 capability xattr 中而不是 setuid 位或文件内容中。5.2 数据块替换利用 Dirty COW 修改run-as的数据页写入一个小型 raw-syscall payloadtext复制setuid(0) setgid(0) setfsuid(0) setfsgid(0) execve(/system/bin/sh)因为只改数据、不改 inode 和 xattrrun-as原本的 capability 仍然有效。执行后得到text复制uid0 gid0 CapEff 0x00000000000000c0对应能力text复制CAP_SETUID CAP_SETGID5.3 受限 root 的边界这个 root shell 可以以 uid0 运行命令读取 root-owned 文件kill / restart root 进程使用setprop ctl.*控制 init 服务但不能remount /system打开 system 块设备写 /data 顶层目录创建 /system/bin/su绕过 DAC 访问任意目录虽然能力受限但它足以作为下一阶段的跳板。六、通过 vold 获得完整 root 能力6.1 为什么不能直接 kill vold直接text复制pkill -9 vold会触发整机重启因为 vold 是 init 管理的核心存储服务。更稳定的方式是text复制setprop ctl.restart vold从 uid0 root shell 执行该属性设置可以让 init 优雅地重启 vold而不会立刻复位整机。6.2 页缓存替换 vold测试中用完整 root 通道准备一个 raw-syscall payload用 Dirty COW 修改/system/bin/vold的代码页执行setprop ctl.restart voldinit 启动新的 vold 进程新 vold 以完整 root 能力运行 payload。新 vold 的能力text复制CapEff 0x0000001fffffffff这就是完整 root 能力。payload 完成操作后必须text复制exec /data/local/vold.orig把原始 vold 接管回去否则系统存储服务缺失会导致异常。6.3 链路示意text复制受限 root shell → setprop ctl.restart vold → init 重启 vold → vold 代码页已被替换 → full root payload 执行 → exec 回原 vold七、FIBMAP BLKROSET持久化思路7.1 为什么 remount 不可行即使拥有完整 roottext复制mount -o rw,remount /system也会触发系统复位。原因可能包括system 块设备 ro 标志厂商硬件看门狗安全回滚守护拦截 remount。因此持久化不能走常规 remount 路线。7.2 FIBMAP 查询物理块FIBMAP 是文件系统提供的逻辑块到物理块映射接口c复制int block 0; ioctl(fd, FIBMAP, block);拿到物理块后可以直接对块设备进行写操作。7.3 BLKROSETsystem 块设备默认只读text复制/sys/block/.../ro 1完整 root 可以通过BLKROSETioctl 清除 roc复制int zero 0; ioctl(disk_fd, BLKROSET, zero);写入完成后再恢复只读c复制int one 1; ioctl(disk_fd, BLKROSET, one);7.4 写入 run-as 数据块最终持久化方案text复制1. 打开 system 块设备 2. BLKROSET 清除只读 3. FIBMAP 查询 run-as 第一块物理块 4. 将 raw-syscall payload 写入对应物理块 5. BLKROSET 恢复只读 6. sync因为只覆盖数据块run-as的以下内容全部不变inodemodeownerfile capabilities重启后run-as仍然带有原有 capability执行它依旧可以进入 uid0 shell。这就是“持久 root”的关键。7.5 写后验证写入后需要校验FIBMAP 返回值物理块写入返回值目标文件头部 hash重启后再次执行 run-as确认仍为 uid0只有通过重启验证才能称为“持久化”。八、看门狗与回滚机制分析8.1 三层机制设备上可能同时存在text复制硬件看门狗 → 系统不喂狗/底层挂起时直接复位 Android Watchdog → system_server 关键服务阻塞时重启 厂商安全回滚守护 → 监控系统改动恢复或重启8.2 行为对比操作触发结果推测原因remount /system秒重启硬件看门狗或安全回滚pm disable 系统包Kill/重启framework/PMS 保护破坏系统 APK短暂生效可能被恢复安全回滚pkill vold整机重启init 关键服务保护ctl.restart vold可接受正常服务重启FIBMAP BLKROSET可完成绕过 mount 层8.3 经验直接对抗系统包和 mount 层容易触发保护以“服务正常重启 底层块写入”的方式触发面更小永远优先尝试官方支持的配置项而不是硬改改 MBOOT/bootloader 风险极高容易无法恢复。九、失败路径与踩坑记录9.1 备份文件为 0 字节初始阶段备份机制不完善导致关键文件.orig为 0 字节恢复时失败。教训备份必须校验大小、hash 和可读性。9.2 一次替换多个 native 库一次性替换多个 App native 库导致应用崩溃循环。教训控制变量一次只改一个组件。9.3 MountService 事务号误判把getStorageUsers当成createSecureContainer后续调用全部失败。教训事务号必须通过 AIDL 顺序或行为探测确认。9.4 假设 ASEC 一定 exec 外部程序对文件级 trap 失效。教训关键服务调用链必须动态验证不能仅靠静态推测。9.5 pm disable 管理系统包导致进程被 Kill、系统重启。教训系统级去广告/精简不应通过 PackageManager 暴力禁用系统包实现。十、防御建议针对此类设备厂商和安全研究者可以关注10.1 内核与系统修补 Dirty COW 等历史漏洞保持内核安全更新开启 SELinux enforcing使用 verified boot。10.2 权限与能力移除 run-as 等工具上不必要的 file capabilities限制 shell 对块设备的访问限制 ASEC/JNI 相关 Binder 接口。10.3 服务安全root 服务不应允许页缓存替换vold 等核心服务的可执行文件应受到保护init 服务重启策略应区分正常重启和异常 kill。10.4 看门狗设计不应将关键业务绑定广告/OTA 包避免“禁用一个业务包就触发整机重启”的脆弱设计安全回滚应该分层而不是直接复位。10.5 安全研究建议先只读侦察先备份完整文件和关键块一次只改一个变量区分 page cache、数据块和文件系统元数据有 recovery/线刷方案后再动底层。十一、结论本文以某 Android 4.4 TV 设备为例展示了从普通 adb shell 到持久 uid0 的完整研究路径text复制Dirty COW → run-as file capabilities → 受限 root → ctl.restart vold → vold full root → FIBMAP / BLKROSET → 持久 root其中最有工程价值的点包括file capabilities 与 inode/xattr 分离机制init 服务ctl.restart的正确使用方式用 FIBMAP 绕过 remount watchdog 实现持久化对硬件看门狗、framework Watchdog、厂商回滚机制的分层分析。对于老式 Android TV 设备真正的难点通常不是“拿 root”而是在多层保护机制下安全地获得能力并避免把设备送进重启循环。附录 A术语表术语说明Dirty COWLinux 内核 COW 竞态漏洞CVE-2016-5195voldAndroid 存储守护进程ASECAndroid Secure Container加密/安全容器JNIJava Native Interfacefile capabilities文件级 Linux capability xattrFIBMAP查询文件逻辑块到物理块映射的 ioctlBLKROSET设置块设备只读标志的 ioctlwatchdog看门狗用于检测异常并复位/重启page cache页缓存文件数据的内存缓存inode/xattr文件元数据和扩展属性附录 B时间线简表阶段工作侦察分区、UID、权限、JNI、vold 分析原语Dirty COW 逐页写入器验证注入default container JNI 动态加载跳板run-as file capabilities 获取 uid0提权vold 服务页缓存替换 ctl.restart持久化FIBMAP BLKROSET 写 run-as 数据块验证重启后 run-as 仍为 uid0防御看门狗/回滚机制分析与安全建议
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进