该漏洞属于一个整数溢出漏洞,影响范围波及firefox18.0之前的所有版本。对该漏洞的分析参考了《漏洞战争》。
火狐浏览器的javascript引擎在进行字符串的正则替换时由于过大的字符串导致的申请空间大小过大造成整数溢出,使得实际上申请了极小的空间造成了溢出。
由于火狐浏览器属于开源软件,因此这里直接下载源码进行编译。
环境:
windows 7 32位 Ultimate版
Visual Studio 2010
编译步骤:
1.在32位win7环境中首先点击MozillaBuildSetup-1.7.exe,(我之后的mozilla-build和mozilla-release两个文件夹都位于D盘根目录下),安装完成后将会在D:\下生成mozilla-build文件夹。
2.使用7-zip(不要使用WinRAR等解压)解压firefox-17.0.source.tar.bz2,要解压两次,直到出现mozilla-release文件夹,并将其移动至D:\下。
3.用记事本打开D:\mozilla-release\xulrunner\config下的mozconfig文件并修改其内容为:
ac_add_options --enable-application=browser
ac_add_options --enable-debug
ac_add_options --enable-tests
ac_add_options --disable-webgl
之后复制mozconfig到mozilla-build\和mozilla-release\两个路径下(mozconfig中若是加上ac_add_options -trace-malloc后make编译时总是会提示没有这个选项,所以这里将其去掉)。
4.由于这里使用的是VS2010,所以点击mozilla-build下的start-msvc10.bat,之后依次输入:
cd d:
cd mozilla-release
make -f client.mk build
之后就是漫长的等待时间(我这里等待了近4个小时)。
结束后会出现:
这时可以去D:\mozilla-release\obj-i686-pc-mingw32\dist\bin下点击firefox.exe执行。
若打开后出现以下场景则成功编译。
<html>
<script type="text/javascript">function puff(x, n){
while(x.length<n) x+=x;
x = x.substring(0, n);
return x;
}
var x = "1";
var rep = "$1";x = puff(x, 1<<20);
rep = puff(rep, 1<<16);
y = x.replace(/(.+)/g, rep);
alert(y.length);</script>
</html>
这里举个例子解释一下,比如说x="11111",而rep="
在这个poc中由于x中1的个数为1<<20,且$1的个数为1<<16,那么这里就可以理解为超大数额。
点击刚刚的firefox.exe,并使用windbg附加进程,之后ctrl+s输入符号表,再ctrl+p输入mozilla-release文件夹的路径后,输入g继续运行,在firefox中打开poc.html。
之后程序会被异常中断,发现漏洞触发点位于Vector函数中。
使用kb命令查看栈回溯(由于有源码所以会显示调用点具体位于哪个文件的哪一行,注意实际要为显示的行的上一行)。
发现函数调用链为ReplaceRegExpCallback->DoReplace->Vector
由于有源码,所以直接查看mozilla-release\js\src\jsstr.cpp的2067行。
说明是和rdata.sb有关,向上查看代码发现DoReplace中没有相关rdata.sb相关空间操作的代码,于是查看ReplaceRegExpCallback函数。
而rdata.sb的sb实际是StringBuffer的缩写,查看相关文档可知StringBuffer类的操作空间的函数包含reserve,于是重点分析ReplaceRegExpCallback中的rdata.sb.reserve。
重新附加进程进行动态调试,并在ReplaceRegExpCallback处设置断点。
首先依旧按照上面步骤点击刚刚的firefox.exe,并使用windbg附加进程,之后ctrl+s输入符号表后输入。
bp mozjs!ReplaceRegExpCallback
之后可能要输入多次g,直到可以在浏览器打开poc.html,断下后按p运行并观察源码页看看,看看是否停止在:
if (!rdata.sb.reserve(rdata.sb.length() + growth))
这里可以看出[esi+68h]的内容为0,且[ebp+14h]不为0,这里判断出growth为0,向上回溯growth可知growth来源于replen。
对于replen又来源于FindReplaceLength函数,遂分析FindReplaceLength函数。
对该函数的源码分析可知,该函数有多个分支点,于是动态调试该函数
重新附加进程,依旧在ReplaceRegExpCallback处设置断点,并单步执行至FindReplaceLength函数内部,多次p单步执行后发现对于该漏洞,执行在FindReplaceLength的最后一个分支。
分析该段代码,由于已知这是整数溢出漏洞,于是重点关注。
replen += sub.length - skip;
这段代码对应的汇编代码为:
sub ecx,eax
add ebx,ecx
重新调试,当单步运行到:
JSString *repstr = rdata.repstr;
打开反汇编窗口查找到上面所述的汇编代码。
设置条件断点:
bu 6ac96514 ".if(1){.echo 'replen= ';dd ebx l1;gc}"
6ac96514代表着add ebx,ecx后面一条指令的位置。
接着
bc 0
删除刚刚的ReplaceRegExpCallback的断点,接着g运行就会发现:
replen会已知递增,最终replen会等于00000000,(由于条件断点设置的是dd ebx l1,所以右边的内存内容忽略不要管,递增看左边数据)
因此最终造成申请的空间过小,复制内容过大造成溢出。
可以尝试修改poc中rep = puff(rep, 1<<16);
这里我修改为rep = puff(rep, 1<<17);
再次调试发现结果相同,说明该poc的替换字符串的超大概念具有普适性。
看雪ID:zer0_o
https://bbs.kanxue.com/user-home-971539.htm
# 往期推荐
球分享
球点赞
球在看