理解对C++裸指针释放后重用的问题
本文将以Android 2.2-2.3上的一个zergRush漏洞为例,分析指针释放后重用的问题。
zergRush是Android 2.2-2.3上的一个漏洞,主要问题就在于指针的释放后重用。 zergRush利用了libsysutils库提供的Framework套接字的通用接口。 程序从套接字收到的消息中出抽取出的文本命令会导致栈缓冲区溢出,进而造成释放后重用问题。 具体地,是vold后台程序调用了libsysutils.so,bug出在FrameworkListener.cpp的dispatchCommand方法。
什么是释放后重用
释放后重用(Use After Free)问题是指,程序使用指针访问了一个已经通过free函数或者delete操作符释放过的对象,并且这个指针没有置空,攻击者在这块释放后的内存中写入了恶意的数据shellcode,当程序第2次使用这个指针的时候,控制流就转向了攻击者构造的恶意数据中了。
FrameworkListener中的bug
FrameworkListener.cpp中有bug的关键代码如下:
//参数cli为与用户进程连接的socket链接;参数data为用户进程的命令参数
void FrameworkListener::dispatchCommand(SocketClient *cli, char *data){
FrameworkCommandCollection::iterator i;
int argc = 0;
//在栈上临时分配的局部缓冲区,用来存放从socket中解析命令参数指针
char *argv[16];
//栈上分配的缓冲区,存放从socket中解析命令参数数据
char tmp[255];
char *p = data; //p指向用户数据
char *q = tmp; //q指向tmp数组
//...
//下面的循环遍历输入中的所有字符,直到遇到一个结尾
while(*p) {
//...
//将用户输入复制到缓冲区,参数放入tmp数组,但是没有检查边界
*q = *p++;
//如果引用的字符串外面还有一个空格,则将q重置到tmp的起始位置
if (!quote && *q == ' ') {
*q = ' ';
//strdup会在堆上分配空间,返回这块堆内存的指针
argv[argc++] = strdup(tmp);
memset(tmp, 0, sizeof(tmp));
q = tmp;
continue;
}
q++;
}
argv[argc++] = strdup(tmp);
for (i = mCommands->begin(); i != mCommands->end(); ++i) {
FrameworkCommand *c = *i;
if (!strcmp(argv[0], c->getCommand())) {
//调用FrameworkCommand的虚函数
if (c->runCommand(cli, argc, argv)) {
}
}
}
int j;
for (j = 0; j < argc; j++){
//因为是strdup动态分配出来的,所以需要主动释放
free(argv[j]);
}
return;
}
下图是第1次调用dispatchCommand
的内存布局:
假设其中一个FrameworkCommand对象所在的内存地址是0x12345678,这个地址值,用户进程可以在参数中以字符串的形式提供,即x78x56x34x12
,这里要考虑到字节序,内存低地址将存放小端的字节。
假设参数data的数据为“cmd p1 p2 p3 p4 p5 p6 p7 p8 p9 p10 p11 p12 p13 p14 p15 p16 x78x56x34x12”
。
前15个参数的处理过程中,argv数组中的元素都是正常的从strdup返回的指向堆的指针值,即指向参数字符串的指针。
当p指针指向p16这个参数值,argv[16]=strdup(“p16”),这时argv[16]已经超出了argv数组的范围,此时&argv[16]=&tmp[0]
,这个参数值将覆盖tmp数组的头4字节。之后tmp清空,q指针重新指向tmp数组的开头,继续读入最后一个参数。
继续调用*q = *p++
,此时tmp开头4字节即为x78x56x34x12
,同时也是argv[16]元素的值,注意到这个值有别于argv数组中其它的元素的值,其它元素的值都是strdup动态分配返回的堆指针,而argv[16]是攻击者恶意构造的地址值。
此时argv[16]的头4字节,也就是tmp头4字节的数据是0x78,0x56,0x34,0x12, free(argv[16])调用的是free(0x12345678),即释放掉了FrameworkCommand所在内存,即这块内存被内存分配器添加到类似freelist这样的数据结构中,供下一次动态分配使用。
这里需要说明下strdup这个函数。char* strdup(const char *s1)
函数会为s1指针指向的字符串数据分配等大小的内存,并返回指向这块内存的指针。因为是动态分配的,这块内存在堆上,实际使用Android系统中Bionic lib库内置的dlmalloc分配器来动态分配的。dlmalloc分配器在某些情况下内存被free后不会马上释放回内核,而是保留给应用程序重新申请。
下图是第2次调用dispatchCommand
的内存布局:
当用户进程第2次调用dispatchCommand
,走到argv[0] = strdup(tmp)
处时,strdup分配的内存就是上次释放掉的FrameworkCommand所在内存,并把tmp的字节数据拷贝到这块内存中。这时可以构造恶意数据覆盖vtable指针,让它指向shellcode的内存地址,这样当函数主动调用runCommand时,控制流就会跑到shellcode中了。比如第二次传给dispatchCommand的命令是”AAAA param”,vtable指针会被覆盖成0x41414141,EIP将被指向 [0x41414141+runCommand虚函数在虚表中的偏移]
。剩下的问题就是如何巧妙的构造shellcode和放在哪块内存区域了。
修复方法
补丁libsysutils: Fix potential overwrites in FrameworkListener 给出了一个修复方法,增加了数组越界检查。
+ if (argc >= CMD_ARGS_MAX)
+ goto overflow;
参考
zergRush (CVE-2011-3874) 提权漏洞分析 从zergRush深入理解Use After Free 《Android安全攻防权威指南》
- ipvsadm启动报错解决办法,另附ipvsadm详细参数
- Linux:mv 命令的10个实用例子
- Linux优化方法收集与整理
- 常用MySQL语句搜集整理
- ASM 翻译系列第十二弹:ASM Internal amdu - ASM Metadata Dump Utility
- 分享一个Linux无法创建文件夹,但是目录权限却显示正常的问题和解决
- 桌面白屏(Active故障)修复批处理
- ASM 翻译系列第十三弹:ASM 高级知识 - Forcing the issue
- ASM 翻译系列第十四弹:ASM Internal Rebalancing act
- DIY网站统计:WordPress排除管理员评论及精准友链数的方法
- Linux运维工程师:30道面试题整理
- ASM 翻译系列第十五弹:ASM Internal ASM File Directory
- ASM 翻译系列第十六弹:ASM Internal ASM Active Change Directory
- ASM 翻译系列第十七弹:ASM Internal ASM Disk Directory
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- AIM Tech Round 5 (rated, Div. 1 + Div. 2)C. Rectangles
- PAT (Basic Level) Practice (中文)1006 换个格式输出整数 (15 分)
- 编译原理实战入门:用 JavaScript 写一个简单的四则运算编译器(二)语法分析
- PAT (Basic Level) Practice (中文)1023 组个最小数 (20 分)
- Angular里使用createEmbeddedView动态加入新的模板元素
- PAT (Basic Level) Practice (中文)1002 写出这个数 (20 分)
- [笔记整理]几个有点重要的知识点
- PAT (Basic Level) Practice (中文)1005 继续(3n+1)猜想 (25 分)
- PAT (Basic Level) Practice (中文)1033 旧键盘打字 (20 分)
- SAP电商云CCV2 Restful API enablement
- PAT (Basic Level) Practice (中文)1007 素数对猜想 (20 分)
- 两种方式解决子集问题
- PAT (Basic Level) Practice (中文)1035 插入与归并 (25 分)
- PAT (Basic Level) Practice (中文)1008 数组元素循环右移问题 (20 分)
- PAT (Basic Level) Practice (中文)1037 在霍格沃茨找零钱 (20 分)