中国科学家故事20字:【转】漏洞 — zergRush (补充)

来源:百度文库 编辑:偶看新闻 时间:2024/05/01 17:45:54

漏洞 — zergRush (补充)

分类: 移动安全 2011-10-13 15:20 694人阅读 评论(0) 收藏 举报

没看到上文的参见一下

 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 分配数据

view plainprint?

  1. void FrameworkListener::dispatchCommand(SocketClient *cli, char *data) {  
  2.     FrameworkCommandCollection::iterator i;  
  3.     int argc = 0;  
  4.     char *argv[FrameworkListener::CMD_ARGS_MAX];  
  5.     char tmp[255];  
  6.     char *p = data;  
  7.     char *q = tmp;  
  8.     bool esc = false;  
  9.     bool quote = false;  
  10.     int k;  
  11.   
  12.     memset(argv, 0, sizeof(argv));  
  13.     memset(tmp, 0, sizeof(tmp));  
  14.     while(*p) {  
  15.         if (*p == '\\') {  
  16.             if (esc) {  
  17.                 *q++ = '\\';  
  18.                 esc = false;  
  19.             } else  
  20.                 esc = true;  
  21.             p++;  
  22.             continue;  
  23.         } else if (esc) {  
  24.             if (*p == '"')  
  25.                 *q++ = '"';  
  26.             else if (*p == '\\')  
  27.                 *q++ = '\\';  
  28.             else {  
  29.                 cli->sendMsg(500, "Unsupported escape sequence", false);  
  30.                 goto out;  
  31.             }  
  32.             p++;  
  33.             esc = false;  
  34.             continue;  
  35.         }  
  36.   
  37.         if (*p == '"') {  
  38.             if (quote)  
  39.                 quote = false;  
  40.             else  
  41.                 quote = true;  
  42.             p++;  
  43.             continue;  
  44.         }  
  45.   
  46.         *q = *p++;  
  47.         if (!quote && *q == ' ') {  
  48.             *q = '\0';  
  49.             argv[argc++] = strdup(tmp);  
  50.             memset(tmp, 0, sizeof(tmp));  
  51.             q = tmp;  
  52.             continue;  
  53.         }  
  54.         q++;  
  55.     }  
  56.   
  57.     argv[argc++] = strdup(tmp);  
  58. #if 0  
  59.     for (k = 0; k < argc; k++) {  
  60.         SLOGD("arg[%d] = '%s'", k, argv[k]);  
  61.     }  
  62. #endif  
  63.   
  64.     if (quote) {  
  65.         cli->sendMsg(500, "Unclosed quotes error", false);  
  66.         goto out;  
  67.     }  
  68.       
  69.     for (i = mCommands->begin(); i != mCommands->end(); ++i) {  
  70.         FrameworkCommand *c = *i;  
  71.   
  72.         if (!strcmp(argv[0], c->getCommand())) {  
  73.             if (c->runCommand(cli, argc, argv)) {  
  74.                 SLOGW("Handler '%s' error (%s)", c->getCommand(), strerror(errno));  
  75.             }  
  76.             goto out;  
  77.         }  
  78.     }  
  79.   
  80.     cli->sendMsg(500, "Command not recognized", false);  
  81. out:  
  82.     int j;  
  83.     for (j = 0; j < argc; j++)  
  84.         free(argv[j]);  
  85.     return;  
  86. }  

 

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

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

view plainprint?

  1. *q = *p++;  
  2. if (!quote && *q == ' ') {  
  3.     *q = '\0';  
  4.     argv[argc++] = strdup(tmp);  
  5.     memset(tmp, 0, sizeof(tmp));  
  6.     q = tmp;  
  7.     continue;  
  8. }  
  9. 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 寄存器)入到栈空间保存。

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

view plainprint?

  1. void func(int a, int b) {  
  2.         int var;  
  3.         return;  
  4. }  
  5.   
  6. int main() {  
  7.         func(1, 2)  
  8.         do_somting();  
  9.         return;  
  10. }  

当调用 func 的时候,

栈是这样的:

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

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

>>>>>       var 的值

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

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

 

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

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