首页
论坛
课程
招聘
[缓冲区溢出] [Windows] [原创]Windows 安全机制---GS 尚未完结
2021-4-5 21:43 1586

[缓冲区溢出] [Windows] [原创]Windows 安全机制---GS 尚未完结

2021-4-5 21:43
1586

Windows 安全机制---GS

一;GS基本原理

    GS功能是Windows 针对栈溢出而产生得防御技术。其主要原理是在调用函数初始化一个栈帧之后将一个随机数放入栈当中,并且在“.data“节区保存一个副本。每次在执行返回地址得指令之前都需要验证一下随机值。如果发生变化,则认为产生溢出。接下来,我们通过对比看看启动GS前后栈空间得变化。

    测试环境:




操作系统Windows 家庭版
编程环境VS 2019

    测试代码:

#include <stdio.h>
#include <stdlib.h>
#include <cstring>
#pragma strict_gs_check(on) 

int vul(const char * str) {
	char arry[8];
	strcpy_s(arry,str);
	return 1;
}

int main() {
	__asm int 3
	const char* str = "qqqq";
	int c = vul(str);
	return 0;
}

    注释:如何启动关闭GS功能如下图:

    我们设定好之后,直接编译,发现代码在“__asm int 3”的位置断下来,接着我们查看“反汇编”窗口,如下代码。我们知道在断点之后的代码首先将字符串“qqqq”放入内存并且将对应的地址放入寄存器EAX当中压入栈。

008518AE  int         3  
008518AF  mov         dword ptr [str],offset string "qqqq" (0857B30h)  
    16: 	const char* str = "qqqq";
    17: 	int c = vul(str);
008518B6  mov         eax,dword ptr [str]  
008518B9  push        eax  
008518BA  call        vul (085139Dh)

    我们步入进入函数vul()查看。如下汇编指令:

008517C0  push        ebp  
008517C1  mov         ebp,esp  
008517C3  sub         esp,0D4h  
008517C9  push        ebx  
008517CA  push        esi  
008517CB  push        edi  
008517CC  lea         edi,[ebp-0D4h]  
008517D2  mov         ecx,35h  
008517D7  mov         eax,0CCCCCCCCh  
008517DC  rep stos    dword ptr es:[edi]  
008517DE  mov         eax,dword ptr [__security_cookie (085A004h)]   //初始cookie值
008517E3  xor         eax,ebp   //初始cookie值与当前栈EBP的值进行异或
008517E5  mov         dword ptr [ebp-4],eax  //将异或后的值放入栈空间
008517E8  mov         ecx,offset _43DCD8FE_test@cpp (085C009h)  
008517ED  call        @__CheckForDebuggerJustMyCode@4 (085130Ch)  
     9: 	char arry[8];
    10: 	strcpy_s(arry, str);
008517F2  mov         eax,dword ptr [str]  
008517F5  push        eax  
008517F6  lea         ecx,[arry]  
008517F9  push        ecx  
008517FA  call        strcpy_s<8> (08512D5h)  
008517FF  add         esp,8  
    11: 	return 1;
00851802  mov         eax,1  
    12: }
00851807  push        edx  
00851808  mov         ecx,ebp  
0085180A  push        eax  
0085180B  lea         edx,ds:[851838h]  
00851811  call        @_RTC_CheckStackVars@8 (08511D1h)  
00851816  pop         eax  
00851817  pop         edx  
00851818  pop         edi  
00851819  pop         esi  
0085181A  pop         ebx  
0085181B  mov         ecx,dword ptr [ebp-4]   //取出cookie值
0085181E  xor         ecx,ebp  //异或处理
00851820  call        @__security_check_cookie@4 (085113Bh)  //调用验证函数
00851825  add         esp,0D4h  
0085182B  cmp         ebp,esp  
0085182D  call        __RTC_CheckEsp (0851230h)  
00851832  mov         esp,ebp  
00851834  pop         ebp  
00851835  ret

    由上述代码,我们首先将vul() 函数分配得栈空间画出来,详细如下图:

    注释:这里因为vul() 函数当中同样调用了strcpy_s()函数,所以同样会有对strcpy_s()函数得栈空间,这里不去讨论。


     1.1;security_cookie值得产生

    我们看到,在正常进行初始化一个栈空间的程序当中,插入了如下代码。由代码我们可以知道,首先会有一个初始的security_cookie值放入寄存器。

008517DE  mov         eax,dword ptr [__security_cookie (085A004h)]  
008517E3  xor         eax,ebp  
008517E5  mov         dword ptr [ebp-4],eax

    通过使用工具,我们发现初始值得地址在节区“.data”范围。详细如下图:


    1.2;security_cookie值的验证

    我们知道,在vul()函数返回得时候,会执行针对security_cookie得验证,接下来,我们来看看如下代码。首先将放入[ebp-4]位置得security_cookie值取出并且再次与当前得栈EBP值进行异或,并且将值放入ECX当中,之后调用验证函数。

0085181B  mov         ecx,dword ptr [ebp-4]  
0085181E  xor         ecx,ebp  
00851820  call        @__security_check_cookie@4 (085113Bh)

    接下来,我们步入验证函数,看看操作了什么,这里我们看如下代码,我们发现它将ECX,与“085a004h”地址得值进行对比。对比相同则执行ret指令,即跳转会之前vul()得栈空间,如果不相同值跳转到“0851b4bh”。这里我们注意地址“085a004h”得值就是初始得security_cookie。

00851B40  cmp         ecx,dword ptr [__security_cookie (085A004h)]  
00851B46  bnd jne     failure (0851B4Bh)  
00851B49  bnd ret

    总结:

  •  在正常执行完初始化一个栈帧结束之后,会执行将“.data”节的第一个双字作为初始化cookie(具备随机性)。

  • 然后使用初始化的种子cookie种子与当前的栈EBP进行异或运算,将值放入栈[ebp-4]的位置当中。

  • 在函数返回之前,即执行ret指令之前,调用验证函数进行验证。


二;GS基本防护范围例外

  1. 函数不包含缓冲区。

  2. 函数被定义为具有变量参数列表。

  3. 函数使用无保护的关键字标记。

  4. 函数在第一个语句众包含内嵌汇编代码。

  5. 缓冲区不是8字节类型且大小不大于4个字节。

三;GS常见绕过手段

  • 利用未被保护的内存突破GS

  • 覆盖虚函数突破GS

  • 攻击异常处理突破GS

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

案例:

1;覆盖虚函数突破GS

    测试Shellcode:

    注释:该Shellcode来自ioiojy讲师CVE实战课程

char ShellCode[] = 
              "\x33\xDB"                          // xor ebx,ebx
              "\xB7\x06"                           // mov bh,6
              "\x2B\xE3"                          // sub esp,ebx
              "\x33\xDB"                          // xor ebx,ebx
              "\x53"                              // push ebx
              "\x68\xB9\xFE\xB9\xFE"              // push "哈哈"
              "\x8B\xC4"                          // mov eax,esp
              "\x53"                              // push ebx      
              "\x68\xD0\xA1\xEA\xCA"
              "\x68\x20\x62\x79\x3A"
              "\x68\xB2\xE2\xCA\xD4"
              "\x68\xD2\xE7\xB3\xF6"               // push "溢出测试 by:小晔"
              "\x8B\xCC"                           // mov ecx,esp
              "\x53"                               // push ebx
              "\x50"                               // push eax
              "\x51"                               // push ecx
              "\x53"                               // push ebx
              "\xB8\xea\x07\xd5\x77"               
              "\xFF\xD0"                           // call MessageBox
              "\x53"
              "\xB8\xFA\xCA\x81\x7C"
              "\xFF\xD0" ;                          // call ExitProcess

    测试代码:

#include <stdio.h>
#include <stdlib.h>
#include <cstring>
#pragma warning( disable : 4996)    //强制允许编译器可以使用strcpy()函数
#pragma strict_gs_check(on)


class GS
{
public:
	void a(char* str) {
		char buf[200];
		strcpy(buf, str);
		v();   //虚函数
	}
	virtual void v() {}
};

int main() {
	char ShellCode[] = "qqqqqqqqqqqqqqqqqq";
	char* str = ShellCode;
	GS test;  //使用栈空间
	test.a(str);
	return 0;
}

    我们先思考一下,GS防护机制在函数返回的时候才会产生效果,而如果在不返回函数就劫持程序流是否就可以绕过了呢?虚函数就提供了这样的一次机会。

    我们先执行代码,看看变量与虚表指针在栈中的布局。

    首先,执行代码,代码会停止在int 3 得位置,映入眼帘的是如下汇编代码:

00A51968  int         3  
00A51969  mov         eax,dword ptr [string "qqqqqqqqqqqqqqqqqq" (0A57B3Ch)]  
00A5196E  mov         dword ptr [ShellCode],eax  
00A51971  mov         ecx,dword ptr ds:[0A57B40h]  
00A51977  mov         dword ptr [ebp-18h],ecx  
00A5197A  mov         edx,dword ptr ds:[0A57B44h]  
00A51980  mov         dword ptr [ebp-14h],edx  
00A51983  mov         eax,dword ptr ds:[00A57B48h]  
00A51988  mov         dword ptr [ebp-10h],eax  
00A5198B  mov         cx,word ptr ds:[0A57B4Ch]  
00A51992  mov         word ptr [ebp-0Ch],cx  
00A51996  mov         dl,byte ptr ds:[0A57B4Eh]  
00A5199C  mov         byte ptr [ebp-0Ah],dl  
    21: 	char ShellCode[] = "qqqqqqqqqqqqqqqqqq";
    22: 	char* str = ShellCode;
00A5199F  lea         eax,[ShellCode]  
00A519A2  mov         dword ptr [str],eax  
    23: 	GS test;
00A519A5  lea         ecx,[test]  
00A519A8  call        GS::GS (0A510B9h)  
    24: 	test.a(str);
00A519AD  mov         eax,dword ptr [str]  
00A519B0  push        eax  //压入参数
00A519B1  lea         ecx,[test]  
00A519B4  call        GS::a (0A51069h)  // 调用a()函数

    单步执行,进入a()函数当中,下面便是汇编代码

00A517F0  push        ebp  
00A517F1  mov         ebp,esp  
00A517F3  sub         esp,1A0h  
00A517F9  push        ebx  
00A517FA  push        esi  
00A517FB  push        edi  
00A517FC  push        ecx  
00A517FD  lea         edi,[ebp-1A0h]  
00A51803  mov         ecx,68h  
00A51808  mov         eax,0CCCCCCCCh  
00A5180D  rep stos    dword ptr es:[edi]  
00A5180F  pop         ecx  //字符长度
00A51810  mov         eax,dword ptr [__security_cookie (0A5A004h)]  
00A51815  xor         eax,ebp  
00A51817  mov         dword ptr [ebp-4],eax  
00A5181A  mov         dword ptr [this],ecx  
00A5181D  mov         ecx,offset _43DCD8FE_test@cpp (0A5C009h)  
00A51822  call        @__CheckForDebuggerJustMyCode@4 (0A5132Ah)  
    12: 		char buf[200];
    13: 		strcpy(buf, str);
00A51827  mov         eax,dword ptr [str]  
00A5182A  push        eax  
00A5182B  lea         ecx,[buf]  
00A51831  push        ecx  
00A51832  call        _strcpy (0A5120Dh)  
00A51837  add         esp,8  
    14: 		v();   //虚函数
00A5183A  mov         eax,dword ptr [this]  
00A5183D  mov         edx,dword ptr [eax]  
00A5183F  mov         esi,esp  
00A51841  mov         ecx,dword ptr [this]  
00A51844  mov         eax,dword ptr [edx]  
00A51846  call        eax  
00A51848  cmp         esi,esp  
00A5184A  call        __RTC_CheckEsp (0A5124Eh)  
    15: 	}
00A5184F  push        edx  
00A51850  mov         ecx,ebp  
00A51852  push        eax  
00A51853  lea         edx,ds:[0A51880h]  
00A51859  call        @_RTC_CheckStackVars@8 (0A511EAh)  
00A5185E  pop         eax  
00A5185F  pop         edx  
00A51860  pop         edi  
00A51861  pop         esi  
00A51862  pop         ebx  
00A51863  mov         ecx,dword ptr [ebp-4]  
00A51866  xor         ecx,ebp  
00A51868  call        @__security_check_cookie@4 (0A51154h)  
00A5186D  add         esp,1A0h  
00A51873  cmp         ebp,esp  
00A51875  call        __RTC_CheckEsp (0A5124Eh)  
00A5187A  mov         esp,ebp  
00A5187C  pop         ebp  
00A5187D  ret         4

    下面我们使用OD来详细看一下,

    程序进入到函数a()


2;攻击异常处理突破GS



四;参考文件

1;0day 安全:软件漏洞分析技术




[看雪官方培训] Unicorn Trace还原Ollvm算法!《安卓高级研修班》2021年6月班开始招生!!

最后于 2021-4-8 17:17 被天象独行编辑 ,原因:
收藏
点赞1
打赏
分享
最新回复 (2)
雪    币: 3116
活跃值: 活跃值 (3326)
能力值: ( LV9,RANK:140 )
在线值:
发帖
回帖
粉丝
学技术打豆豆 活跃值 1 2021-4-6 08:45
2
0
没了?
雪    币: 377
活跃值: 活跃值 (2482)
能力值: ( LV10,RANK:165 )
在线值:
发帖
回帖
粉丝
天象独行 活跃值 2 2021-4-6 10:02
3
0
学技术打豆豆 没了?
不好意思,昨天晚上写到一半忙其他的事情了。很快会写完的。
游客
登录 | 注册 方可回帖
返回