漏洞 — zergRush (补充)

没看到上文的参见一下

 http://blog.csdn.net/tomken_zhang/article/details/6866260

 

上文中还没有来细细分析漏洞的出处及利用过程,现在又有所收获,特记录下来。

 

上文说到,向 vold 发送了一段 SOCKET 数据,然后 vold 崩溃了。 崩溃的愿意是栈溢出,究竟在哪溢出的呢,其实在 zergRush.c 中说了一些,文件的头部就写到 libsysutils root exploit,意思是这个库有个漏洞,恰恰被 vold 进程使用,而这个进程是 root 用户能力。

 

找到 libsysutils 代码,在 \android2.32\system\core\libsysutils\src 这个位置,文件不是太多,我是追查了好处才定位到一个函数有问题,具体过程不说了,比较痛苦和迂回。(经验少啊!)

FrameworkListener.cpp 文件

>> onDataAvailable 收到 Socket 数据。

>> 只读 255 的 buffer

>> dispatchCommand 分配数据

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;

    memset(argv, 0, sizeof(argv));
    memset(tmp, 0, sizeof(tmp));
    while(*p) {
        if (*p == '\\') {
            if (esc) {
                *q++ = '\\';
                esc = false;
            } else
                esc = true;
            p++;
            continue;
        } else if (esc) {
            if (*p == '"')
                *q++ = '"';
            else if (*p == '\\')
                *q++ = '\\';
            else {
                cli->sendMsg(500, "Unsupported escape sequence", false);
                goto out;
            }
            p++;
            esc = false;
            continue;
        }

        if (*p == '"') {
            if (quote)
                quote = false;
            else
                quote = true;
            p++;
            continue;
        }

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

    argv[argc++] = strdup(tmp);
#if 0
    for (k = 0; k < argc; k++) {
        SLOGD("arg[%d] = '%s'", k, argv[k]);
    }
#endif

    if (quote) {
        cli->sendMsg(500, "Unclosed quotes error", false);
        goto out;
    }
    
    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;
        }
    }

    cli->sendMsg(500, "Command not recognized", false);
out:
    int j;
    for (j = 0; j < argc; j++)
        free(argv[j]);
    return;
}

 

溢出发生在栈变量 argv[FrameworkListener::CMD_ARGS_MAX] 上,

FrameworkListener::CMD_ARGS_MAX = 16  在我的看的源代码中。

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

这里是通过判断传来的数据是否有空格来进行 argc++ 的,而最大的下标明显是 16, 如果我们特意传送超过 16 个空格分割的字符串,就溢出了。

为了验证一下,把这段代码放到 PC 上写个函数,编译一下,得出结果,确实溢出。

 

回头看看 zergRush.c 吧

在 do_fault 函数中。 确实定义了一个 255 的 buf,和接受的缓冲一样长,通过一堆 strcat 函数,放入相关字段,

 还有一个循环,

 for(i=3; i < buffsz+1; i++)
    strcat(buf, " ZZZZ");

放了更多个

然后又放了一些“地址”  ,其实这些地址是一个 shellcode ,目的是覆盖寄存器 lr 被 Push 到栈数据空间。最后完成整个提权动作。

 

顺便提一下栈溢出原理

当一个函数进入到另一个函数的时候(典型情况),程序会把函数的参数,及一些环境(寄存器,包括反回地址 LR 寄存器)入到栈空间保存。

同时新函数执行的时候会把函数栈变量也放到栈空间上,大概如下:

void func(int a, int b) {
        int var;
        return;
}

int main() {
        func(1, 2)
        do_somting();
        return;
}

当调用 func 的时候,

栈是这样的:

>>>>>       2 (就是 b 的值)

>>>>>       1 (就是 a 的值)

>>>>>       var 的值

>>>>>       该函数时要 push 的通用寄存器 比如 r0

>>>>>       lr 反回地址,其实指向的应该是 do_somting 的地址

 

当 var 的赋值溢出的时候,就会覆盖到下面的一些寄存器,如果替换得当,就完成了程序的非正常流程的运行。

 

以上大概就是这样子,具体情况实际上更为复杂,本人也不明白,另外 PC 与手机的还不相同。仅作参考!

 

 

©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页