mugen凶恶研究(3)——原版zlib溢出
2023-4-17 17:36:35 Author: 珂技知识分享(查看原文) 阅读量:17 收藏

6.    原版zlib溢出

这个测试需要用到原版winmugen,winmugen最初是官方给捐献者私有的2人限制版,经过2004年无限制破解,以及2007年的多轮高分辨率破解,得到现在常用的Win Mugen Plus。但还有一种国内流行的星光主程序,其zlib和原版winmugen不一样,导致zlib溢出没有那么通杀。我前的文章全部使用星光主程序测试,现在替换成原版winmugen。
https://www.infinitymugenteam.com/infinity.wiki/mediawiki/index.php?title=M.U.G.E.N
zlib溢出常见的是两个人物模板,一个是五月雨的,一个是遥远的。
遥远的血神模板打开比较直接。

但五月雨的就没那么直接,上调试(x32dbg),根据前人研究,断点422438

可以看得出来,此时已经完成溢出,422438是栈溢出中覆盖了ret的地址,和之前Reboot用过的403246一样是个add esp;ret。看栈可以看得出来,接下来会跳到ALLEG40.DLL上的10078CEF上。跟进之后发现就是jmp esp

跟我们之前用mona找到的1005FB9D(call esp)效果一样都是进栈执行。

血神的则是直接ret10078D87(也是jmp esp),血神压缩文件符合010Editorzip模板,比较方便我们定位溢出点在哪里,是deFileTime+deFileDate

同理五月雨模板可以推算出来,他用的溢出点在deCompressedSize上。

接下来主要研究血神,断点10078D87,在栈上找到一个最近的返回地址418BE1,这个就是离栈溢出比较近的代码。

其位于sub_418B90(),似乎是个文件读取函数,非常像溢出点,断点下在418BDC(call sub_418A70)上。

418BDC,F8之后完成栈溢出,直接跳到10078D87,那么证实栈溢出确实发生在sub_418A70()。
那么断点418A70,一点一点动态调试,最终搞清楚整个栈溢出流程。从最外层函数到最内层函数如下。

sub_40CF80()——0x40D8F9 call sub_418B90sub_418B90()——0x418A70 call sub_418A70sub_418A70()——0x418ACE call sub_4854E0sub_4854E0()——0x48550A call sub_485520sub_485520()——0x485D0E rep movsd(即qmemcpy,发生栈溢出)sub_418A70()——0x418B2A call sub_486510sub_486510()——0x4866E4 ret(跳转到jmp esp)

两张图概括如下

485D0E rep movsd(qmemcpy)为什么会导致栈溢出呢?rep movsd的意思很简单,就是从ESI复制内容到EDI上去(复制ECX个字节)。在发生栈溢出前,也有两次正常的rep movsd,我们可以看内存变化。
rep movsd前

rep movsd后

可以清楚的看到92C8120上被改写了,也就是说它将栈上的值复制到内存中,这也正是qmemcpy()的主要功能。然而在发生问题的rep movsd上,EDI居然也是栈上的地址,那么就是从栈复制到栈上。

再往前看edi的赋值汇编就会发现EDI本质上是a2,也就sub_485520()的第二个参数造成的。

00485CFD    8B7C24 78    mov edi,dword ptr ss:[esp+78]

那为什么之前两次rep movsd没问题呢?因为它们的调用链是这样的sub_418A70()——sub_485F00()——sub_485520(),结构图如下。

只有sub_4854E0(),将栈上的v6当成第二个参数传入,导致了最后栈溢出的发生。

如何从零制作zlib溢出包呢?根据遥远的提示,他是将正常人物包进行仅存储的压缩模式,然后发现mugen崩溃发现的。看血神模板也可以看得出来,整个人物本质上就是一个写了shellcode/0/空格的def,然后仅存储压缩,

那么我们将正常的kfm.def改为test.def,加上大概40KB左右的空格,然后单独将def仅存储压缩,断点004866E4。

这样非常轻松就获取了栈溢出的偏移量,将ZIPDIRENTRY dirEntry的deFileTime/deFileDate修改为我们自己找到的1005FB9D (call esp)。成功跳转,栈上执行。

那么如何执行shellcode呢?因为zip模板限制,接下来CRC等空间显然不够填充所有的shellcode。我们可以学习Reboot的思路,跳转到一个存储test.def自己文本的地方。而这个地方就在栈上。

那么CRC应该填充的东西就呼之欲出了。sub esp,0x20;ret(83EC20C3)

这也正是血神的利用方式,但是如果我们这样做的话,会发现溢出不成功了,会直接在40DAF1(repne scasb)抛出错误。

这个错误很有可能是因为CRC校验的问题,参考血神和五月雨模板的做法,是同时修改了ZIPFILERECORD recordZIPDIRENTRY dirEntry两处的FileTime/FileDate/Crc9DFB051083EC20C3,即可成功实现完美栈布局。

那么接下来要做的就是将test.def前面的那些注释替换成shellcode。注意原版kfm.def的那些注释不够填充220长度的shellcode,直接填充会覆盖到name导致报错,所以需要先加上220+的A,重新压缩,在010editor中手动替换成shellcode。
msfvenom -p windows/exec cmd="calc.exe" exitfunc=thread -b "\x00" -o calc.bin

但这样最终还是无法执行shellcode,动态调试一下,很容易发现我们sub esp,0x20;ret这一步错了,跳到其他地方去了。

929D550才是我们需要跳的地方,多跳了4位。但从929D550存储的0x20可以看得出来那是我们写入的非常多的空格,也可以往里面放shellcode。
当然,最好还是改成sub esp,0x1C;ret(83EC1CC3)

最终效果如下。


文章来源: http://mp.weixin.qq.com/s?__biz=MzUzNDMyNjI3Mg==&mid=2247486325&idx=1&sn=3bd40f69c707cca2baa717f69721f3ca&chksm=fa97361acde0bf0c2fa0b54a0df3a652742e8f623380f98f36c827568905c4ed7780f496babc#rd
如有侵权请联系:admin#unsafe.sh