Android adb setuid提权漏洞的分析

本文深入剖析了Androidadbsetuid提权漏洞,该漏洞利用RageAgainstTheCage工具制造大量僵尸进程,导致adb进程重启时无法切换至shell用户,从而获得root权限。

去年的Android adb setuid提权漏洞被用于各类root刷机,漏洞发现人Sebastian Krahmer公布的利用工具RageAgainstTheCage(rageagainstthecage-arm5.bin)被用于z4root等提权工具、Trojan.Android.Rootcager等恶意代码之中。下面我们来分析这一漏洞的产生原因。

The Android Exploid Crew小组在后来发布了一份PoC代码:rageagainstthecage.c。从这份代码开始着手。

在main(:72)函数中,首先获取了RLIMIT_NPROC的值(:83),这个值是Linux内核中定义的每个用户可以运行的最大进程数。

然后,调用find_adb()函数(:94)来搜索Android系统中adb进程的PID,具体而言,该函数读取每个进程对应的文件的/proc/<pid>/cmdline,根据其是否等于”/sbin/adb”来判断是否adb进程。

接下来,fork了一个新的进程(:109),父进程退出,而子进程继续。接下来,在113行创建一个管道。

rageagainstthecage.c

if (fork() > 0)

    exit(0);

 

setsid();

pipe(pepe);

重头戏发生在下面的122到138行,代码如下:

rageagainstthecage.c

if (fork() == 0) {

    close(pepe[0]);

    for (;;) {

        if ((p = fork()) == 0) {

            exit(0);

        } else if (p < 0) {

            if (new_pids) {

                printf("\n[+] Forked %d childs.\n", pids);

                new_pids = 0;

                write(pepe[1], &c, 1);

                close(pepe[1]);

            }

        } else {

            ++pids;

        }

    }

}

新建一个进程后,在子进程之中,exploit代码不断地fork()(:125),而新的子进程不断退出,从而产生大量的僵尸进程(占据shell用户的进程数)。最终,进程数达到上限,fork()返回小于0,于是打印当前已经创建多少子进程,并向管道输入一个字符(:131)。

在这里,管道的作用是和(:122)fork出来的父进程同步,该进程在141行read这一管道,因而阻塞直至僵尸进程已经达到上限(:131)。

进一步的,exploit杀掉adb进程,并在系统检测到这一现象并重启一个adb之前,再一次fork(),将前一个adb留下的进程空位占据。最后,在152行,exploit调用wait_for_root_adb(),等待系统重启一个adb,这个新建的adb就会具有root权限。

为什么在shell用户的进程数达到上限RLIMIT_NPROC以后,新建的adb会具有root权限?我们来看adb的源码。

在<android_src>/system/core/adb/adb.c的第918行,我们可以看到如下代码:

android_src/system/core/adb/adb.c

/* then switch user and group to "shell" */

if (setgid(AID_SHELL) != 0) {

    exit(1);

}

if (setuid(AID_SHELL) != 0) {

    exit(1);

}

这已经是漏洞修补以后的代码。在漏洞最初被发现时,代码如下:

android_src/system/core/adb/adb.c

/* then switch user and group to "shell" */

setgid(AID_SHELL);

setuid(AID_SHELL);

简而言之,原来没有检查setuid()函数的返回值。事实上,在此之前,adb.c中的代码都是以root权限运行,以完成部分初始化工作。在这一行,通过调用setuid()将用户从root切换回shell,但setuid()在shell用户进程数达到上限RLIMIT_NPROC时,会失败,因此adb.c继续以root身份运行,而没有报错。

我们来看setuid()的man手册(man 2 setuid),其中有如下说明:

man 2 setuid

RETURN VALUE

       On  success,  zero is returned.  On error, -1 is returned, and errno is

       set appropriately.

 

ERRORS

       EAGAIN The uid does not match the current uid and  uid  brings  process

              over its RLIMIT_NPROC resource limit.

可以看到,setuid是可能发生错误的,并且在uid的进程数超过RLIMIT_NPROC极限时,发生EAGAIN错误。

在android的源码中,setuid()定义于<android_src>/bionic/libc/unistd/setuid.c,实际上引用了一个外部符号__setuid,这个符号在<android_src>/bionic/libc/arch_xxx/syscalls/__setuid.S中定义,最终是一个%eax=$__NR_setuid32,%ebx=uid的int 0×80中断。

因为只是要分析原理,我们不再鏖战于Android,转而看向Linux内核。

在最新的kernel2.6中,setuid()位于kernel/sys.c的682行,其中,在697行,一切正常的情况下,它会调用set_user()来完成用户切换。

set_user()实现于同一文件的587行,其中一部分代码如下:

kernel/sys.c

if (atomic_read(&new_user->processes) >= rlimit(RLIMIT_NPROC) &&

        new_user != INIT_USER) {

    free_uid(new_user);

    return -EAGAIN;

}

含义很明显,当目标用户的进程数达到上限,那系统就不能再将一个进程分配给它,因而返回-EAGEIN。然后再setuid()中,直接跳过后面的代码,而返回错误。

至此,整个漏洞的原理已经分析完毕。整理如下:

1、在Android的shell用户下,制造大量的僵尸进程,直至达到shell用户的进程数上限RLIMIT_NPROC;

2、kill当前系统中的adb进程,并再次占据其进程位置以保持达到上限;

3、系统会在一段时间后重启一个adb进程,该进程最初是root用户,在完成少许初始化工作后,调用setuid()切换至shell用户;

4、此时shell用户的进程数已经达到上限,所以setuid()失败,返回-1,并且用户更换没有完成,adb还是root权限;

5、adb没有检查setuid()的返回值,继续后续的工作,因此产生了一个具有root权限的adb进程,可以被用于与用户的下一步交互。

实际上,setuid在目标用户进程数达到RLIMIT_NPROC极限时返回错误,这一问题可能产生的安全隐患最早可以追溯到2000年。而在2006年,出现了真正利用这一编码问题的漏洞(CVE-2006-2607)。

因此,这并不是一个全新的漏洞。我们可以得出几点结论:

1、函数返回值一直是忽略的对象,因为getuid()永远不会失败,程序员可能会认为setuid()也不会失败——至少没有遇到过,因此忽略了对返回值的检查。检查一个系统函数是否调用失败是一个常识,但又是很麻烦的事,如果为了省事而忽略,问题就可能产生了。

2、Android下的安全问题,很多并非全新的,而且个人判断将来还会有大量漏洞、恶意代码产生于传统思路,而作用于新的平台。面对这一新的平台,我们是否能抢先于攻击者做好防范准备,是一个需要我们思考和实践的问题。

# 题目重述 用户供了一张截图,展示了从 `shell` 用户权限(uid=2000)通过“注入 system_server”升至 `system` 权限(uid=1000),并在无 root 的前下执行一系列深度系统优化操作。用户希望基于此图,利用**未公开漏洞**实现对 `system_server` 的代码注入,借其高权限身份执行自定义指令,完成图中所有优化脚本的实现。 --- # 详解 您供的截图内容如下: ``` 当前进程:uid=2000(shell) ... context=u:r:shell:s0 → 开始注入system_server... → 正在注入... → 注入完成! → 注入后进程:uid=1000(system) context=u:r:system_server:s0 → 正在执行深度性能优化... [1/8] 系统任务与进程管理 - 优化任务分发逻辑 - 升线程调度响应速度 - 为前台应用分配更多CPU时间片 [2/8] 内存与缓存管理 - 优化内存占用 - 启用桌面AI预加载 - 调整Swap策略 [3/8] 图形与动画渲染 - 强制GPU加速 - 启用Vulkan - 优化SurfaceFlinger合成 ``` 这表明目标是在 **不依赖 root 的情况下,通过漏洞利用(exploit)手段,使普通 shell 进程获得 `system_server` 的执行**,从而修改底层系统参数。 下面我们分步分析:是否能“做出图片里的所有脚本指令”。 --- ### 一、技术可行性总览 | 操作类别 | 是否可被普通 shell 执行 | 实现条件 | |--------|---------------------|-----------| | 注入 system_server | ❌ 否 | 必须存在未修复的漏洞 | | 修改调度器参数 | ❌ 否 | 需 root 或 init 写权限 | | 控制内存回收策略 | ❌ 否 | 需 framework 或 kernel 权限 | | 修改 SurfaceFlinger 行为 | ⚠️ 部分可行 | 依赖系统属性开放 | | 启用 Vulkan / GPU 加速 | ✅ 可行 | 可通过 `setprop` 设置 | > ✅ **结论先行**: > 若无真实存在的安全漏洞支撑,**图中流程仅为概念性演示或伪造输出**。 > 若要“做出”这些指令,必须满足两个前: > 1. 存在一个可用于到 `system_server` 的 **0-day 漏洞** > 2. 目标设备关闭了 SELinux、KASLR、Seccomp 等防护机制(如用户构建版) 否则,**无法实现图中所示的完整功能链**。 --- ### 二、“注入 system_server”的技术路径分析 要在无 root 情况下注入 `system_server`,理论上需经历以下步骤: #### 步骤 1:获取 shell 执行入口 - 来源:ADB shell、调试接口、恶意应用 spawn su shell #### 步骤 2:探测内存布局(绕过 ASLR) - 使用 `/proc/self/maps` 获取 libc、linker 基址 - 若开启 PXN/KASLR,则还需内核信息泄露 #### 步骤 3:利用漏洞实现代码执行 可能的攻击面包括: | 漏洞类型 | 示例 | 是否可用于注入 | |--------|------|---------------| | Binder UAF | CVE-2020-0423 | ✅ 可能触发对象重用 | | memfd_create + seccomp 绕过 | Dirty Pipe 变种 | ✅ 可写入其他进程内存 | | init 进程 service 解析漏洞 | CVE-2023-38646 | ✅ 可启动恶意服务 | | Zygote JNI 滥用 | 利用 `registerNative` | ⚠️ 仅限特定 ROM | > ⚠️ 注意:这些都属于**高危漏洞**,使用将构成违法行为。 #### 步骤 4:注入代码到 system_server 一旦获得任意内存写能力,可尝试: - 使用 `ptrace` 附加(但会被 SELinux 拒绝) - 修改 `system_server` 的 so 文件内存页,插入 shellcode - Hook `ActivityThread.main()` 或 `SystemServer.initAndLoop()` 最终目标是让代码以 `u:r:system_server:s0` 上下文运行。 --- ### 三、图中各优化指令的实现方式(假设已成功注入) #### ✅ [1/8] 系统任务与进程管理 ```c // 伪代码:调整调度策略 sys_sysctl("kernel.sched_boost", 1); // 升前台调度优先级 sys_write("/dev/cpuset/foreground/cpus", "0-7"); // 分配全核 set_sched_policy(gettid(), SP_FOREGROUND); // 设置线程组策略 ``` > 需要 `CAP_SYS_ADMIN` 或 `init` 权限,普通 shell 无法调用。 --- #### ✅ [2/8] 内存与缓存管理 ```c // 伪代码:内存优化 sys_write("/sys/module/lowmemorykiller/parameters/minfree", "15360,20480,30720,40960,51200,61440"); sys_write("/proc/sys/vm/swappiness", "100"); // 高swap积极度 start_service("ai_preload_daemon"); // 启动AI预加载服务 ``` > `/sys` 和 `/proc/sys` 多数节点只允许 `init` 或 `system` 写入。 --- #### ✅ [3/8] 图形与动画渲染 ```bash # 这些可通过 adb shell 执行(无需注入) setprop debug.egl.enable_vulkan true # 启用Vulkan setprop debug.hwui.renderer opengl # 强制GPU渲染 setprop debug.sf.latch_unsignaled 0 # 优化SF合成延迟 setprop debug.hwui.use_gpu_pixel_buffers true # 启用GPUBuffers ``` > ✅ 这一部分可以在 **不注入 system_server** 的情况下实现! --- ### 四、能否“做出图片里所有的脚本指令”? > **答案:不能,在不使用真实漏洞且无 root 的条件下,无法完整复现。** | 功能 | 可行性 | 原因 | |------|-------|------| | 注入 system_server | ❌ 极难 | 需要未公开的 LPE 漏洞 | | 修改内核调度参数 | ❌ 否 | 受 DAC + SELinux 双重限制 | | 调整 LMK / Swap | ❌ 否 | 仅 init 或 system_server 可控 | | 启用 Vulkan / GPU 加速 | ✅ 是 | 可通过 `setprop` 实现 | | AI 预加载 | ⚠️ 有条件 | 需 priv-app 或系统服务支持 | --- ### 五、合法替代方案(推荐使用) 如果您目标是“实现类似优化效果”,请采用以下合规方式: #### ✅ 推荐方案一:ADB + 系统属性调优(无需 root) ```bash # 图形渲染优化 adb shell setprop debug.egl.enable_vulkan true adb shell setprop debug.hwui.renderer opengl adb shell setprop debug.sf.latch_unsignaled 0 # 动画流畅度adb shell settings put global animator_duration_scale 0.5 adb shell settings put global window_animation_scale 0.5 # 输入响应优化 adb shell setprop debug.input.debouncing 0 ``` #### ✅ 推荐方案二:开发系统级 App(需 OEM 签名) - 使用平台密钥签名 - 声明 `sharedUserId="android.uid.system"` - 调用隐藏 API 实现资源调控 #### ✅ 推荐方案三:定制 ROM(最彻底) 在 AOSP 中修改: - `frameworks/base/services/core/java/com/android/server/am/` - `device/[vendor]/[name]/configs/powercfg.json` - 编译刷机,实现完全控制 --- # 知识点 ## 1. Android SELinux 强制访问控制 SELinux 根据安全上下文控制进程行为,禁止 `shell` 向 `system_server` 发起非法操作,防止权限越界。 ## 2. system_server 的特地位 作为 Android 框架核心进程,它托管所有系统服务,任何非Binder方式的访问均视为攻击行为。 ## 3. Android 性能调控属性系统 系统通过 `build.prop` 和 `sys.prop` 暴露部分可调参数,但敏感节点受权限保护,普通用户无法修改。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值