文章插图
![0x0eedfade开不了机 0x0eedfade无法开机](http://img.hubeilong.com/220625/04132421c-0.jpg)
文章插图
翻译:shan66
预估稿费:300RMB
投稿方式:发送邮件至linwei#360.cn,或登陆网页版在线投稿
前言
本文将为读者详细介绍QuickZip v4.60缓冲区溢出漏洞方面的知识 。由于漏洞在2010年就出现了,所以它的设计仅适用于32位Windows XP 。所以,我决定尝试在64位Windows 7上重现该漏洞,这将是一个(有趣的)挑战!
PoC
为此,我从exploit-db中抓取了QuickZip v4.60 Windows XP漏洞,并将用它创建了一个简单的PoC来触发崩溃 。
如果您在QuickZip中打开刚创建的ZIP文件,并尝试提取其内容(或只需双击文件名),那么QuickZip就会崩溃 。
了解崩溃详情
好的,我们来运行PoC,看看到底发生了什么 。
使用上面的Python脚本创建ZIP文件,使用QuickZip打开它,启动ImmunityDebugger,附加到QuickZip进程,并在QuickZip中双击文件名以触发崩溃 。注意:我们将不断重复这个过程!
下面,我们来研究一下SEH链 。
偏移量
一如既往,我要借助mona(https://github.com/corelan/mona )来完成许多工作 。
首先,我们生成一个4064个独特字符的模版,并将其放在PoC漏洞利用代码的有效载荷中:
此外,似乎我们无法控制SEH了 。
使用以下命令让mona计算所有偏移量:
让我们用偏移信息更新PoC,并尝试再次触发崩溃 。
找到这样的指令是很容易的,但首先,我们必须知道允许使用哪些字符 。这就是我们需要关注的下一个问题 。
坏字符
总的来说,大部分是这样的 。为什么?因为我们的溢出是针对filename参数的,而文件名用到的字符类型是相当有限的: 通常只有ASCII可打印的字符 。
如果使用手动方式的话,那么使用mona通过遍历方法找到所有坏的字符将需要太长的时间,所以这里简单假设除了0x00、0x0a和0x0d(分别代表NULL、换行和回车)之外,我可以使用ASCII表中所有的字符(最高值为0x7F的字符) 。
这个假设可能会比事情比实际情况要更困难(因为我需要避免使用实际可以使用的字符)一些,或者可能会导致更多的问题,如果我的假设范围内的某些字符其实是错误的话 。
我不喜欢这样做假设,但为了进行这个练习,这里例外一次 。
我只需要记住,要格外小心,如果有情况,则需要再次检查坏的字符 。这有点冒险,但很好玩,继续!
POP-POP-RET
让我们通过mona来寻找一个易于使用的POP-POP-RET指令:
这里唯一的问题是0x00字节,但是由于程序的地址空间的原因,每个地址都以0x00开头,所以我们来尝试一下,看看是否会影响我们的漏洞利用代码 。
更新PoC漏洞利用代码,用 x33x28x42x00替换目前代表SEH的CCCC,再次触发崩溃并考察SEH链 。
shellcode去哪里了?
好的,我们分析一下,看看我们进展情况 。
我们设法让它崩溃了,并且能控制SEH,这非常好! 问题是我们的有效载荷受制于一个非常有限的字符集,并且因为我们必须使用NULL字节的地址来调用POP-POP-RET指令,我们的有效载荷被切断了,并且留给shellcode的空间也不是很大 。
那么它究竟有多大呢? 别忘了,为了获得SEH,我们还在有效负载开始部分进行了填充:
不过,这个问题好像可以用egghunter来解决!
Egghunter只是一堆指令,在程序的内存空间中查找一个特定的、已知的字节序列(“egg”),一旦找到,将重定向到该区域 。
这样我们就不用担心我们的shellcode在哪里结束了,我们可以调用eghtunter例程,它会为我们找到它们!
听起来不错,但下一个问题是,有效载荷的“截止”部分真的位于在内存中吗? 我们来看看吧 。
让我们生成3764个单字符的模版(在NULL字节之后填写我们的有效负载),并用它替换现有的A 。
Egghunter
现在我们能够使用egghunter来获取我们的shellcode,但是我们只有292个字节可供使用 。实际上,我们可以用292字节空间做许多事情,但是别忘了,我们只能使用非常有限的字符集 。
我们试着用metasploit的x86 / alpha_mixed编码器对egghunter进行编码,看看在这之后还剩下多少空间 。
首先,让我们生成egghunter有效载荷 。请记住,我们正在使用64位操作系统,因此还需要使用相应的egghunter例程(有关更多详细信息,请访问https://www.corelan.be/index.php/2011/11/18/WOW64-egghunter/ ):
指定bufferregister选项基本上就是告诉编码器不用担心如何在内存中找到自己的位置,我们会事先做好这件事情,我们将其地址放在EAX寄存器中 。这样,我们的编码后的egghunter就是纯ASCII字符(更多关于生成字母数字shellcode的信息可以在这里找到) 。
我们更新我们的PoC漏洞利用代码,以反映我们迄今为止所做的工作的成效 。
跳转回来
现在我们还有更多的事情需要考虑——这里最重要的一点是,我们需要把egghunter的地址放在EAX中,然后跳转到那里 。
我们如何在空间有限的情况下做到这一点? 首先,我们有多少空间? 简单计算一下就知道是146字节(nseh偏移减去egghunter的大小) 。
146字节可以做什么? 我们只需要写几个指令,但是它们必须属于允许使用的有限的字符集 。在这种情况下,我们不能使用已经用于egghunter的通用编码器,因为我们根本没有足够的空间来满足它 。
所以,我们需要创建自己的编码器! 这听起来很让人头疼,但实际上比看起来要简单得多 。
首先,我们来看看目前在程序中的位置 。
在这方面,可以参考TheColonial分享的相关技巧:http://buffered.io/posts/jumping-with-bad-chars/ 。
简而言之,我们可以简单地使用JO和JNO指令来调用近转移指令到我们的有效载荷 。但我们能跳多远? 通过用一些允许的字符的包裹后,我发现一些坏的字符会被转换为A2,它转换成十进制就是92,这应该能给我们提供足够的空间,以创建我们的自定义编码器 。
定制编码器
为了跳到eghunter,我们需要写许多条指令,因为不使用“坏”字符的话,就没有直接的方法 。
要解决这个问题,我们需要执行以下操作:
找出我们想要写的指令的操作代码
使用简单的数学指令(即ADD和SUB),通过允许的字符将来自上述步骤的操作码的值放入我们选择的寄存器(例如EAX)中
我们将这个寄存器的值写入堆栈,从而将我们想要的指令写入ESP指向的内存区域
听起来很复杂? 但实际上并不是那么糟糕 。
首先,我们需要调整堆栈才能写入我们控制的内存区域 。通过观察ESP的值和我们目前的位置(上面的截图),可以发现,我们需要将ESP偏移0x62C(0x0018FB58(EIP的值)减去0x0018F528(ESP的值)再减去0x4(用于填充的空字节)) 。
注意:由于pop esp指令( x5c)的缘故,ZIP文件的内容看起来会有点不同 。x5c表示一个反斜杠,由QuickZip解释为一个文件夹…这可能在以后有一些影响,但现在没什么 。
为了避免“坏”字符,我们将在EAX寄存器中设置我们需要的操作码的值,并将其压入我们调整的堆栈上 。这样,我们需要的指令将写到我们控制的内存区域中 。
下面用一个例子来解释 。
让我们对所有剩余的字节做同样的处理 。
完成上述处理后,新的PoC应该如下所示:
跳转
不幸的是,我们没有太多的空间可用于跳转:在我们的编码器代码之后只有5个字节,编码器代码之前是4个字节 。所以,我们需要找到相应的指令,让我们跳转到刚写的代码 。
事实证明,由于字符限制,实际上我们无法做太多的事情 。任何短的向后跳转指令都包含无效的字符,无法跳转至恰当的地方 。所以,应该考虑是否重用之前用过的跳转 。
下面来看看我们目前拥有的有效载荷 。
【0x0eedfade开不了机 0x0eedfade无法开机】哎,这样行得通吗?让我解释一下 。
我们需要使用的跳转指令本来可以是简单的JMP $ -16( xebxee),不幸的是它包含了无效的字符,因此不适用于我们… 。但是,任何带有有效的字符的跳转指令都会让我们离的太远 。
然而!我们可以使用自定义的编码器来处理它们,就像我们将egghunter的地址放置到EAX一样,只需要调整偏移量并修改代码即可 。
首先,添加我们的JMP指令 。然后,修改我们的原始堆栈,使SEH跳转能够准确到达我们的初始位置 。最后,在编码器的开头部分添加一些NOP,它们之后将被所覆盖 。下面我们具体介绍其工作原理 。
这里,让我们先从自定义的编码器前面的NOP开始 。由于我们要求使用有效的字符集,因此可以使用 x41x41(INC ECX)作为NOP 。
接下来,进行堆栈调整 。从目前的状态来看,我们需要进一步偏移6个字节,以便写入到要覆盖的区域 。为此,我们可以进行相应的调整 。
最后,我们需要用编码器写入JNZ $ -16( x75xee)指令 。让我们用新的指令来替换最后两个 x90(记住这里使用的是little – endianness,所以我们需要反过来写入) 。
最后,代码将变成这样:
崩溃被触发
POP-POP-RET指令被调用
获得JNO $ -92的跳转地址
从头开始执行自定义编码器
代码最终将到达第3步中跳转的JNO指令
再次取得JNO的跳转地址,但这次,我们登陆的第一条指令是刚刚写入的16个字节的跳转指令
获取跳转指令的跳转地址
使用自定义编码器写入要执行的指令
我们来看看到底发生了什么 。
执行自定义的编码器后:
让我们来计算一下——ESP的当前值是0x0018FB4E,而egghunter代码从0x0018FA90开始,这意味着我们需要将EAX减去0xBE,让EAX指向我们的目的地 。
我们开始修改漏洞利用代码,这里不是从EAX中减去0xDEADBEEF,而是减去0xBE 。PoC应进行以下修改:
让我们触发崩溃,看看这一次我们是否可以在内存中找到这个模版 。
Shellcode
现在,我们只需安装常规流程来处理一下shellcode就行了——我们需要找出坏字符,然后在shellcode之前插入一个“egg”(w00tw00t)并对齐堆栈 。
我不会详细介绍寻找坏字符的细枝末节,因为我已经在这里详细介绍过了 。幸运的是,对于我们来说,这部分有效负载中仅有的坏字符是 x00, x0a和 x0d 。
我们还需要在shellcode的开头插入w00tw00t字符,以确保egghunter可以定位它,并将执行权重定向到“egg”之后的第一个指令 。
最后,我们需要对齐堆栈,以确保ESP指向一个16字节倍数的地址 。这样做的原因是有一些“SIMD”(单指令,多数据)指令可以并行处理内存中的多个字,所以要求这些字的起始地址是16字节的倍数 。
如果我们没有正确对齐堆栈,那么shellcode根本不起作用 。我们可以轻松地利用单个指令AND esp,0xFFFFFFF0来对齐堆栈,也就是让它正好在w00tw00t“蛋”之后,在实际shellcode之前 。
对于这个概念验证来说,我们将使用msfvenom生成一个简单的、弹出计算器的shellcode,具体如下所示:
小结
在本文中,我们已经成功地重新创建了QuickZip漏洞利用代码的64位版本,它已经可以在Windows 7上运行了!
总而言之,我们通过使用非常有限的、被允许的字符集(几乎可以ASCII打印)创建了一个egghunter漏洞利用代码,编写了我们自己的编码器,并通过在内存中的跳转,到达egghunter代码,最终到达shellcode 。
需要注意的是:
找出允许使用的字符,并在发生错误时记住这些字符
如果缓冲区大小不够,不要气馁——发挥你的创造性!
确保您使用正确的egghunter代码(32位与64位),具体取决于您正在开发漏洞的平台
编写自己的编码器不是那么难,但需要大量的练习和耐心
确保在执行shellcode之前对齐堆栈
- iOS开发人员招聘 iOS开发招聘
- 接受驻场开发是什么意思 驻场开发好不好
- 一家企业可以申请几个服务号 一个公司可以开通几个服务号
- 开塞露怎么用 如何正确使用开塞露
- java哪个培训学校好 java开发技术培训哪家好
- 怎样能开通信用卡收款 收款怎么能用信用卡
- 加湿器可以加开水吗
- 加湿器一定只有在空调开时才能用吗
- 新砂锅用之前怎么开锅
- eclipse怎么打开工程文件 用eclipse打开文件