首页
社区
课程
招聘
[翻译]利用 QuickZip 4.60 的缓冲区溢出漏洞在 Win7 X64 系统上弹出计算器
发表于: 2017-5-19 21:14 6052

[翻译]利用 QuickZip 4.60 的缓冲区溢出漏洞在 Win7 X64 系统上弹出计算器

2017-5-19 21:14
6052

        最近我在准备OSCE exam, 所以一直想找一些有趣的漏洞和POC代码来练习一下,顺便在漏洞方面学习一些新的东西。

在寻觅了一会后,发现了 QuickZip v4.60 Buffer Overflow exploit, 是 corelanc0d3r这篇博客里记录的漏洞。

因为这个漏洞是2010年的,是为了32 位的window-xp 设计的。我试下能否在64位的windows 7 操作系统上重现它,这会是一个有趣的挑战!

        起初,我先获取了 QuickZip v4.60 Windows XP exploit from exploit-db, 然后分成几部分来写一个简单的POC,从而触发崩溃。

        上面的代码创建了一个名为4064A;的压缩文件,后缀名为 txt; Header_1, header_2  和 header_3 是zip 文件结构要求的文件头,我不会深入探讨这个问题。你想了解更多的话,看 这里

        如果你在QuickZip 中打开创建的ZIP文件,尝试提取其内容(只是双击文件名),QuickZip就会崩溃。

        好的,让我们来运行一下POC,看看究竟会发生什么。

        用上面的python代码创建zip文件,用QuickZip打开它,打开 ImmunityDebugger, 附加到QuickZip 进程上,在QuickZip 中,双击文件名来引发崩溃。 注意: 我们会反复不厌其烦地重复这个过程,所以习惯它吧!



        非常好,我们像预期那样引发了崩溃。同时,我们得到了一个异常 - 看屏幕的底部 ; 写入[00190000];时访问冲突。这说明我们向无效的内存地址写入时引发了一个异常。

        让我们来看看SHE(结构化异常处理)链。



        非常棒,上图表明我们能够控制nSHE 指针! 看起来很有希望,让我们来试试找出偏移量。

        和往常一样,我还是使用mona (https://github.com/corelan/mona) 来解决这里的问题。

        首先,先生成一个有4064 个特殊字符的样例,把它放在POC 利用的payload上:

        再次引发崩溃,看看这次发生了什么。



        这次与上次不太一样了。 LEAVE 指令尝试从堆栈跳转回一段无效的内存地址0EEDFADE

        但,我们确不再能够控制SHE了。



        然而,注意到我们实际是在内核模块(看下Immunity 窗口的名称-CPU 主线程,KERNELBA 模块)。通过 SHIFT + F9 返回程序执行的上一步,看看在QuickZip 模块中能否引发异常情况。


    

    



        很好,看来我们又回来了!

        用下面的命令让mona计算偏移量:


        到这以后,一个很有意思的事情是 nSEH字段:偏移量 292

        让我们用偏移信息更新下POC,再次尝试触发崩溃。


        太好了,我们控制了SEH!让我们跳过异常程序(SHIFT + F9) 并进一步看下发生了什么。



        当然了,另一个异常程序也触发了,因为43434343对于这个程序来说是一段无效的内存地址,但是我们来看看在栈上发生了什么--通常为SEH溢出,我们需要调用一组POP-POP-RET指令来返回到我们的缓冲区。

        很容易用mona找到这样的指令,但是首先我们必须知道哪些指令是被允许使用的。而这才是问题的关键。

        大体上来说,大部分是这样的。为什么?因为我们在文件名参数和文件名上面的溢出是被严格限定的-------通常只有可打印的ASCII字符。

        因为实际上如果手工通过mona 找到所有坏的字符需要很长时间,我只是假设使用了整张ASCII 表(字符最多为127个)除了0x00, 0x0a0x0d(NULL字节,换行回车符)。

        这个假设可能会使问题变难(因为我没有使用那些本来应该没有问题的字符),或者会导致更多后续的问题,如果我的假设的字符范围是不对的话。

        我不喜欢做出这样的假设,但是出于练习的原因,这次姑且做个假设吧。

        我只需要记住要小心,如果有什么问题的话,再次检查下坏的字符。有点冒险,但没关系,来吧!

        让我们用mona 找到一个易于使用的POP-POP-RET 指令地址:


        发现了很多东西(7909个!),但是突出显示的看起来更让人兴奋------全部由数字字母这样的字符组成,出现在QuickZip.exe二进制文件中,希望这能够使它更加地跨平台,而我们就不必依赖于特定操作系统的DLL库了。

        这里只有一个问题,就是 0x00 字节。又因为程序地址全部以0x00开始 我们来试试看这是否会破坏我们的漏洞。

        更新下PoC漏洞,用 \x33\x28\x42\x00替换目前代表SEH的CCCC 再次触发崩溃,调查下SEH链。



        很好,看来我们的地址没有乱码,正如我们所预料的那样。设置断点 (F2) ,按 SHIFT + F9将控件传给程序。



        如你所见,我们被重定向到了POP-POP-RET 指令,通过F8 单步步过, 然后在 RETN 4 指令后停下。



        太好了,我们又重新地找到了我们的有效数据。但是有个问题,由于存在NULL字节,每个SEH链后面都被切断了,所以我们只有很少的空间来完成任务了。

        好的,我们来分析下目前处于哪一阶段。

        程序崩溃,然后控制了SEH,太棒了!问题是我们被限制在一个非常有限的字符集里,当使用payload时,因为必须使用NULL 字节的地址来调用POP-POP-RET指令 ,我们payload 中的重要部分被截断了,我们的shellcode剩余的空间一点也不大。

        但是到底有多大呢?记住我们还有payload开始时使用的填充为了获得SEH:



        所以我们有多少空间呢?正好是292个字节。不幸的是,对于 任何有用的shellcode--只包含可打印的 ASCII码字符并且需要加密,都是 不够的。

这听起来可以用egghunter来解决!

        Egghunter 是一堆在程序内存空间中查找一个特定的,已知的字节序列的指令,一旦找到的话,将重定向到该区域。这样的话,我们不用担心shellcode在哪里结束了,只需要调用egghunter例程,它会为我们找到的!

        听起来不错,接下来的问题是,payload 中被截断的部分在内存的哪里呢?我们来找找看。

        让我们生成3764个唯一字符的模式(在NULL字节后填充payload),并用它替换现有的A。

        触发崩溃,当第一次出现 异常时,不要将异常传给程序,而是调用下面的命令在内存中搜索以前生成的模式:


        太好了!payload中被截断的部分仍然在内存中,所以我们能够成功地用egghunter来获取到shellcode。

        所以现在我们知道我们应该能够使用egghunter来获取我们的shellcode,但是我们只有292个字节可供我们使用。实际上,292字节做很多事儿了,但是,我们得记住只能使用非常有限的字符集。

        我们试着用metasploit的x86/alpha_mixed编码器对egghunter进行编码,看看在这之后剩下多少空间。

        首先,让我们生成egghunter 的有效载荷。牢记现在是64位的操作系统,所以我们需要使用正确的egghunter 例程 (更多细节可以在https://www.corelan.be/index.php/2011/11/18/wow64-egghunter/)找到:

        把生成的字节拷贝到一个文本文件中,用xxd把它转换为二进制文件:

        现在,我们需要用编码器来确保只用了可打印的ASCII码 字符。

        注意: 我已经用了 bufferedregister=eax 选项。原因在于需要知道编码器在内存中的位置,以便于能对有效载荷解码。最初,负责这个的例程不在 可打印的ASCII码字符集中,因此这会毁掉我们的payload。

        指定 bufferregister选项只是告诉编码器不用担心找不到它在内存中的位置,因为已经提前把它的地址放在了EAX 寄存器中。这样的话,我们的编码器egghunter就只含有ASCII码字符了(更多关于生成只包含字母和数字的shellcode可以在 这里找到)。

        更新下POC漏洞,看看目前为止做到哪了。

       

        让我们触发崩溃,通过执行程序执行POP-POP-RET 指令。在这之后,在CPU窗口中向上滚动,尝试找到egghunter 的有效载荷和长指令集INC ECX指令的结束(代表A字符)。



      非常好,看起来就是这里,都是和预期想的一样,没有错误-也没有坏字符出现!

跳回

  

         现在,我们有更多的事情要考虑---最重要的是要把egghunter的起始地址放入EAX寄存器中,然后跳转到它。

         我们能用有限的空间做什么呢?首先是---我们有多少空间?Quick math 算出有146个字节(nseh 偏移量减去egghunter 的大小)。

         那么146个字节可以干什么呢?我们只要写几条指令,但是所使用的字符要在所要求的字符集中。在这种情况下,我们不能使用已经egghunter 的通用编码器,因为根本没有足够的空间容纳它。

        这样只有一个选择了-自己来写编码器! 听起来很可怕并且复杂,但事实比想象的要简单的多。

        但是我们先来看看我们目前在程序的哪里。



        

所以我们只有4个字节跳转回有效载荷,开始编写定制的编码器。而且,这4个字节最好只含字母数字。幸运的是,在那些情况下,我们有很少的指令可以使用。

信用额度到期了,特别感谢TheColonial 分享的这个技巧: http://buffered.io/posts/jumping-with-bad-chars/

简而言之,我们可以只使用JO 和JNO指令跳回我们的有效载荷。但是能跳多远呢?在处理了一些允许的字符后,发现一些坏字符被转换成了 A2,就是十进制的92…这使得有足够的空间来写自制的编码器。

让我们用metasm生成所需的操作码,并添加到我们的有效载荷中替代nSEH。

        注意: \x9b (-99), 因为它是一个坏字符,实际上将转化为 \xa2 (-92)。

        我们的PoC部分应该像下面这样:

        让我们触发崩溃,将执行过程传递给程序,步过POP-POP-RET 指令,观察当我们步过 JNO/JO 指令时会发生什么。



        太棒了,跳转到了payload处。让我们来向自己的编码器里写入指令跳转到egghunting 例程处。

        我们需要写几条指令才能跳到我们的egghunter,但是,不使用坏的字符的话,没有办法直接写出来。

        要解决这个问题,只需这样做:

        听起来很复杂?实际上并没有,一旦你开始尝试它会变得很有意思。

        首先,我们需要调整堆栈才能写入我们控制的内存区域。看下 ESP 寄存器的值和我们在程序里的位置(上面的截图),我们需要将 ESP偏移量0x62C (0x0018FB58 ( EIP的值) 减去 0x0018F528 (value of ESP) 减去 0x4 (用于填充的空字节))。

        用下面的指令可以实现:

        以上指令的相应操作码如下:


[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 1
支持
分享
最新回复 (4)
雪    币: 3302
活跃值: (1144)
能力值: ( LV9,RANK:260 )
在线值:
发帖
回帖
粉丝
2
帖子发了之后,原先的文章中的代码全都消失了,求指教?
2017-5-19 21:17
0
雪    币: 3302
活跃值: (1144)
能力值: ( LV9,RANK:260 )
在线值:
发帖
回帖
粉丝
3

原先的代码是 基本上这个样子的,

2017-5-19 21:20
0
雪    币: 47147
活跃值: (20450)
能力值: (RANK:350 )
在线值:
发帖
回帖
粉丝
4
fyb波 帖子发了之后,原先的文章中的代码全都消失了[em_5],求指教?
你再上传一份word或其他格式的,我们查查
2017-5-20 08:24
0
雪    币: 3302
活跃值: (1144)
能力值: ( LV9,RANK:260 )
在线值:
发帖
回帖
粉丝
5
kanxue 你再上传一份word或其他格式的,我们查查
源文件是这个样子的

上传的附件:
2017-5-20 10:03
0
游客
登录 | 注册 方可回帖
返回
//