zergRush (CVE-2011-3874) 提权漏洞分析

最近(终于)转Android了,2011年著名的zergrush是接触的第一个ROOT漏洞。虽然它已经过气了,只影响Android 2.2 - 2.3.6,但觉得还是有必要记录一下分析所得。

市面上各种ROOT工具基本都包含zergrush,大多是开源的zergRush.c直接编译而来。已有的分析文章:

  tomken_zhang,漏洞 — zergRush漏洞 — zergRush (补充)

  Claud, Android提权代码zergRush分析

分析内容集中在zergRush.c的代码结构上,对漏洞原理没有解析,或者错误地认为是栈溢出。其实CVE-2011-3874已经描述得很明白,这个漏洞的本质是"use after free"。

1. 栈溢出?No.

漏洞存在于/system/bin/vold这个root身份的系统程序。具体地,vold调用了libsysutils.so,真正有问题的是这个so。再具体地,问题出在/system/core/libsysutils/src/FrameworkListener.cpp的FrameworkListener::dispatchCommand方法。

它在栈上分配了一个固定大小的数组argv,

void FrameworkListener::dispatchCommand(SocketClient *cli, char *data) {
    FrameworkCommandCollection::iterator i;
    int argc = 0;
    char *argv[FrameworkListener::CMD_ARGS_MAX];
    char tmp[255];
    char *p = data;
    char *q = tmp;
    bool esc = false;
    bool quote = false;
    int k;

FrameworkListener::CMD_ARGS_MAX = 16。但后面填充argv数组时,代码并没有检查是否发生了越界。

        if (!quote && *q == ‘ ‘) {
            *q = ‘\0‘;
            argv[argc++] = strdup(tmp);
            memset(tmp, 0, sizeof(tmp));
            q = tmp;
            continue;
        }

让argv越界是很容易的,越界的数据会被写入argv下面的tmp数组中。zergRush实际上只是向argv中填充了CMD_ARGS_MAX + 2个char *,越界了8字节而已,255字节的tmp数组完全接得住,并没有破坏dispatchCommand的栈。其实,就算dispatchCommand的栈溢出了,也没什么事,因为它被编译进了__stack_chk,一旦返回地址改变,会异常结束。所以,这个漏洞不是“栈溢出”。

2. free(任意地址)

用户程序向system/bin/vold发送的数据会到达FrameworkListener::onDataAvailable。onDataAvailable会从数据中提取命令字符串,并调用dispatchCommand处理命令。用户数据可能包含多条命令,每条命令是一个以‘\0‘结尾的字符串,由名称和若干参数构成,名称和参数由空格分隔。例如,

"cmd1 arg11 arg12\0cmd2 arg21 arg22 arg23\0"

这样的数据进入onDataAvailable后,会提取出2条命令字符串:"cmd1 arg11 arg12" 和 "cmd2 arg21 arg22 arg23",对每条命令调用一次dispatchCommand。dispatchCommand接收到命令字符串后,会进一步解析出命令名称和参数,并存入argv数组。比如,对于"cmd1 arg11 arg12",dispatchCommand完成解析后,

argv[0] == "cmd1";
argv[1] == "arg11";
argv[2] == "arg12";

名称和每个参数,最初都是解析到tmp中,argv的每个字符串指针都是strdup(tmp)出来的,在dispatchCommand返回前要free掉,

    int j;
    for (j = 0; j < argc; j++)
        free(argv[j]);

这是一个很正常的逻辑,但在argv数组越界,与tmp发生交叠的情况下,有意思的事情就发生了。假设有一条命令带有17个参数,

"cmd p1 p2 p3 p4 p5 p6 p7 p8 p9 p10 p11 p12 p13 p14 p15 p16 \x78\x56\x34\x12"

解析后,argv数组的内容是:

argv[0] == "cmd";
argv[1] == "p1";
argv[2] == "p2";
argv[3] == "p3";
argv[4] == "p4";
argv[5] == "p5";
argv[6] == "p6";
argv[7] == "p7";
argv[8] == "p8";
argv[9] == "p9";
argv[10] == "p10";
argv[11] == "p11";
argv[12] == "p12";
argv[13] == "p13";
argv[14] == "p14";
argv[15] == "p15";
argv[16] == 0x12345678;
argv[17] == "\x78\x56\x34\x12";

argv数组大小只有16项,argv[16]实际上是tmp数组的前4个字节,而最后一个参数是"\x78\x56\x34\x12",它被解析出来后缓存到tmp里,这样tmp的前4字节就成了0x78,0x56, 0x34, 0x12 ,因此argv[16]==0x12345678。之后,argv[16]被free,即执行了free(0x12345678)。我们可以任意控制参数内容,即可实现:free(任意地址)。

3. 指针复用和vtable覆盖

能够free掉任意指针了,如何实现exploit呢?最简单的办法是free掉一个带vtable的C++对象指针,然后重用这个指针,从而控制vtable。很幸运的是,dispatchCommand的代码逻辑支持这么做。它会对比接收到的命令和已注册的FrameworkCommand,如果匹配,就去运行匹配的FrameworkCommand。

    for (i = mCommands->begin(); i != mCommands->end(); ++i) {
        FrameworkCommand *c = *i;

        if (!strcmp(argv[0], c->getCommand())) {
            if (c->runCommand(cli, argc, argv)) {
                SLOGW("Handler ‘%s‘ error (%s)", c->getCommand(), strerror(errno));
            }
            goto out;
        }
    }

runCommand是FrameworkCommand的virtual方法,FrameworkCommand对象指针就是要free掉然后复用的目标。mCommands是FrameworkListener的成员,它包含了若干FrameworkCommand对象指针,如果知道了FrameworkListener *this,寻址到FrameworkCommand不在话下,这也是zergRush.c的heap_oracle() 做的事情。

假设我们成功free掉了一个FrameworkCommand指针,怎样复用这个指针呢?只需要再调用dispatchCommand解析一条命令,在解析过程中,第一次strdup(tmp)返回的就是这个指针,并且已经把tmp字符串复制到这个地址了。即:命令的前4个字节已经成为这个FrameworkCommand对象的vtable,实现了vtable的任意指定。观察上面C++代码对应的汇编:

.text:000029F6                 LDR.W           R6, [R8]
.text:000029FA                 MOV             R0, R10 ; s1
.text:000029FC                 LDR             R1, [R6,#4] ; s2
.text:000029FE                 BLX             strcmp
.text:00002A02                 CBNZ            R0, loc_2A38
.text:00002A04                 LDR             R0, [R6]           ;R0 = vtable
.text:00002A06                 MOV             R1, R5
.text:00002A08                 MOV             R2, R4
.text:00002A0A                 MOV             R3, R7
.text:00002A0C                 LDR.W           R12, [R0,#8]       ;R12 = runCommand
.text:00002A10                 MOV             R0, R6
.text:00002A12                 BLX             R12                ;Call runCommand
.text:00002A14                 CBZ             R0, loc_2A50
.text:00002A16                 LDR             R5, [R6,#4]
.text:00002A18                 BLX             __errno
.text:00002A1C                 LDR             R0, [R0] ; errnum
.text:00002A1E                 BLX             strerror
.text:00002A22                 LDR             R2, =(aFrameworkliste - 0x2A2E)
.text:00002A24                 LDR             R3, =(aHandlerSErrorS - 0x2A30)
.text:00002A26                 MOVS            R1, #5
.text:00002A28                 STR             R5, [SP,#0x484+var_484]
.text:00002A2A                 ADD             R2, PC  ; "FrameworkListener"
.text:00002A2C                 ADD             R3, PC  ; "Handler ‘%s‘ error (%s)"
.text:00002A2E                 STR             R0, [SP,#0x484+var_480]
.text:00002A30                 MOVS            R0, #3
.text:00002A32                 BLX             __android_log_buf_print
.text:00002A36                 B               loc_2A50

调用runCommand,实际是调用了[vtable + 8]。比如第二次传给dispatchCommand的命令是"AAAA blablabla",vtable会被覆盖成0x41414141,PC将被指向 [0x41414149]。至此,已经成功的控制了指令流,剩下的就是构造攻击数据的奇淫技巧了,比如ROP等,和漏洞本身没有直接关系了。zergRush.c成功地调用system()执行任意命令,达到了ROOT的目的。

4. 总结

CVE-2011-3874这个漏洞源于栈上的数组越界,用户程序通过向/system/bin/vold发送一段包含2条特殊命令的数据,可以控制root身份的vold执行system(),借以提权。用户数据中的第1条命令会free掉一个FrameworkCommand对象;而第2条命令会重新占用对象地址,进而覆盖vtable实现控制指令流。

在Android 2.2-2.3.6没有ASLR的前提下,漏洞利用过程只有2个必要数据无法直接获取:dispatchCommand的this指针和栈地址SP。不幸(幸运)的是,强大的logcat填上了这个坑。有了logcat,即使有ASLR,对这个漏洞也没有防护能力。

时间: 2024-10-10 13:48:58

zergRush (CVE-2011-3874) 提权漏洞分析的相关文章

主机溢出提权漏洞分析

背景介绍公司内部服务器,上面有一简单的上传入口,刚入职的小伙伴在C盘根目录下有一个TXT文本文件,说权限设置的很低,除Administrator外,其他用户无法读取到内容,直接向安全工程师"墨者"发出挑战,让其测试. 实训目标1.掌握文件上传的技巧:2.掌握IIS中间件存在的畸形解析漏洞:3.了解Windows系统CMD命令执行:4.了解查看操作系统安全补丁情况:5.了解Windows操作系统的文件权限设置:6.了解操作系统的溢出漏洞的提权方式: 解题方向通过上传脚本文件,读取C盘下T

10.16Maccms后门分析、Sudo提权漏洞(CVE-2019-14287)复现

Maccms后门分析 maccms网站基于php+mysql的系统,易用性.功能良好等优点,用途范围广 打开源码,extend\Qcloud\Sms\Sms.php.extend\upyun\src\Upyun\Api\Format.php是两个后门(Sms.php和Format.php是后门木马 二者代码相同) <?php error_reporting(E_ERROR);//报错 @ini_set('display_errors','Off'); @ini_set('max_executio

Android提权漏洞CVE-2014-7920&amp;CVE-2014-7921分析

没羽@阿里移动安全,更多安全类技术干货,请访问阿里聚安全博客 这是Android mediaserver的提权漏洞,利用CVE-2014-7920和CVE-2014-7921实现提权,从0权限提到media权限,其中CVE-2014-7921影响Android 4.0.3及以后的版本,CVE-2014-7920影响Android 2.2及以后的版本.Google直到Android5.1才修复这2个漏洞.该漏洞[1]披露过程如下: 2016年1月24日漏洞作者发布了漏洞分析及exploit[2],

基于RedHat发行的Apache Tomcat本地提权漏洞

描述 Tomcat最近总想搞一些大新闻,一个月都没到,Tomcat又爆出漏洞.2016年10月11日,网上爆出Tomcat本地提权漏洞,漏洞编号为CVE-2016-5425.此次受到影响的主要是基于RedHat发行版Apache Tomcat,包括CentOS,RedHat,OracleLinux,Fedora等等.主要原因是普通Tomcat用户拥有权限来对/usr/lib/tmpfiles.d/tomcat.conf这个配置文件进行读写,那么该用户组成员或者拥有普通Tomcat权限的WebSh

/dev/socket/vold exploit 本地提权漏洞

EXPLOIT "0 asec create ../../../../../../../../xxxxx/xx/xx/xx 1 ext4 98235792350852308254872354983460 2000 1" /dev/socket/vold 分析中... 一起研究分析  /dev/socket/vold exploit 本地提权漏洞

脏牛Linux本地提权漏洞复现(CVE-2016-5195)

学习该漏洞的原因: 总是看到圈子里一位老哥发文章使用这个漏洞来提权,进过测试发现centos比较难提取,而Ubuntu是比较好提权的. 漏洞范围: Linux kernel >= 2.6.22(2007年发行,到2016年10月18日才修复) 危害: 低权限用户利用该漏洞可以在众多Linux系统上实现本地提权 简要分析: 该漏洞具体为,get_user_page内核函数在处理Copy-on-Write(以下使用COW表示)的过程中,可能产出竞态条件造成COW过程被破坏,导致出现写数据到进程地址空

2019-10-16,sudo提权漏洞(CVE-2019-14287)实现

sudo是linux系统命令,让普通账号以root身份执行某些命令,比如,安装软件,查看某些配置文件,关机,重启等,如果普通用户需要使用sudo需要修改配置文件,/etc/sudoers,将sudo使用权限赋予该用户 sudo提权漏洞,是一个安全策略绕过问题,去执行某些敏感的命令,cve编号是CVE-2019-14287,影响版本是 sudo版本<1.8.28 漏洞复现1,查看sudo版本,命令sudo -V 2,修改配置文件,vim /etc/sudoers, root ALL(ALL:ALL

sudo 提权漏洞(CVE-2019-14287)复现 (10.16 第二十二天)

sudo是Linux系统命令,让普通账号以root身份去执行某些命令,比,安装软件.查看某些配置文件.关机.重启等操作,如果普通账号需要使用sudo需要修改配置文件/etc/sudoers,将sudo使用权赋予该账号 sudo提权漏洞是一个安全策略绕过问题,去执行某些敏感的命令,CVE编号是CVE-2019-14287,影响的版本是:<1.8.28 漏洞复现过程 1.查看版本:sudo -V 2.修改配置文件:vim/etc/sudoers在root ALL=(ALL:ALL)ALL 下面添加一

android ioctl fuzz,android 本地提权漏洞 android root

目前正在研究android 三方设备驱动 fuzzer , 也就是下图所说的 ioctl fuzzing, 下图是由keen team nforest 大神发布: 欢迎正在研究此方面的人联系我共同交流进步!Email:  Blind Fuzz Smart Fuzz android 内核栈溢出android slab/slub 类堆溢出android 数组越界导致的复写内核中的重要数据android 内核信息泄漏android null pointer deference 空指针引用android