用于阻止缓冲区溢出攻击的 Linux 内核参数与 gcc 编译选项
先来看看基于 Red Hat 与 Fedora 衍生版(例如 CentOS)系统用于阻止栈溢出攻击的内核参数,主要包含两项:
kernel.exec-shield
可执行栈保护,字面含义比较“绕”,
实际上就是用来控制能否执行存储在栈
中的代码,其值为1时表示禁止;为0时表示允许;默认为1,表示禁止执行栈
中的代码,如此一来,即便覆盖了函数的返回地址导致栈溢出,也无法执行
shellcode
查看与修改系统当前的可执行栈保护参数:
[root@localhost 桌面]# sysctl -a | grep -e exec
ernel.exec-shield = 1
[root@localhost 桌面]# cat /proc/sys/kernel/exec-shield
1
[root@localhost 桌面]# sysctl -w kernel.exec-shield=0
kernel.exec-shield = 0
[root@localhost 桌面]# cat /proc/sys/kernel/exec-shield
0
注意:
一,只有在学习和测试栈溢出攻击的原理时,才建议关闭可执行栈保护机制
二,在基于 Debian 与 Ubuntu 衍生版
(例如 BackTrack 5 Release 3)的系统上,不支持可执行栈保护机制:
root@bt:~# uname -a
Linux bt 3.2.6 #1 SMP Fri Feb 17 10:40:05 EST 2012 i686 GNU/Linux
root@bt:~# sysctl -a | grep -e kernel.exec
error: permission denied on key 'vm.compact_memory'
error: permission denied on key 'dev.parport.parport0.autoprobe'
error: permission denied on key 'dev.parport.parport0.autoprobe0'
error: permission denied on key 'dev.parport.parport0.autoprobe1'
error: permission denied on key 'dev.parport.parport0.autoprobe2'
error: permission denied on key 'dev.parport.parport0.autoprobe3'
error: permission denied on key 'net.ipv4.route.flush'
error: permission denied on key 'net.ipv6.route.flush'
root@bt:~#
或许是社区的开发人员认为,有下面另一种叫做堆栈地址随机化的机制,就足够应对缓冲区溢出攻击了,也可能是由于 Debian / Ubuntu 面向运行桌面应用居多的用户群体,它并不像销售给企业公司用户的 Red Hat 类发行版那样,对安全性的要求更为严格,才能保护用户的服务器,资产安全
kernel.randomize_va_space
堆栈地址随机初始化,很好理解,就是在每次将程序加载到内存时,进程地
址空间的堆栈起始地址都不一样,动态变化,导致猜测或找出地址来执行
shellcode 变得非常困难,它和可执行栈保护的区别在于,后者即便是找到了
地址也是无法执行的;所有的 2.6 以上版本的 Linux 内核都支持并启用了
这一项特性;
查看与修改系统当前的堆栈地址随机初始化参数:
[root@localhost 桌面]# sysctl -a | grep randomize
kernel.randomize_va_space = 2
[root@localhost 桌面]# cat /proc/sys/kernel/randomize_va_space
2
[root@localhost 桌面]# sysctl -w kernel.randomize_va_space=0
kernel.randomize_va_space = 0
[root@localhost 桌面]# cat /proc/sys/kernel/randomize_va_space
0
参数值为2时,表示启用随机地址功能;0表示关闭;
基于 Debian 与 Ubuntu 衍生版默认支持并且启用了随机地址功能:
1
2
root@bt:~# sysctl -a | grep -e kernel.randomize
kernel.randomize_va_space = 2
下面,我们在一个启用了随机地址功能的机器上,查看执行同一个程序4次加载的动态链接共享库的入口地址(linux-gate.so.1),发现每次的入口地址都不同,验证了随机地址的效果:
[root@centos6-5 桌面]# cat /proc/sys/kernel/randomize_va_space
2
[root@centos6-5 桌面]# ldd /bin/ls | grep linux-gate.so.1
linux-gate.so.1 => (0x00dff000)
[root@centos6-5 桌面]# ldd /bin/ls | grep linux-gate.so.1
linux-gate.so.1 => (0x00503000)
[root@centos6-5 桌面]# ldd /bin/ls | grep linux-gate.so.1
linux-gate.so.1 => (0x00213000)
[root@centos6-5 桌面]# ldd /bin/ls | grep linux-gate.so.1
linux-gate.so.1 => (0x004b5000)
[root@centos6-5 桌面]#
同理,关闭随机地址功能,连续5次查看执行程序时加载的 linux-gate.so.1 入口地址:
[root@centos6-5 桌面]# sysctl -w kernel.randomize_va_space=0
kernel.randomize_va_space = 0
[root@centos6-5 桌面]# ldd /bin/ls | grep linux-gate.so.1
linux-gate.so.1 => (0x00110000)
[root@centos6-5 桌面]# ldd /bin/ls | grep linux-gate.so.1
linux-gate.so.1 => (0x00110000)
[root@centos6-5 桌面]# ldd /bin/ls | grep linux-gate.so.1
linux-gate.so.1 => (0x00110000)
[root@centos6-5 桌面]# ldd /bin/ls | grep linux-gate.so.1
linux-gate.so.1 => (0x00110000)
[root@centos6-5 桌面]# ldd /bin/ls | grep linux-gate.so.1
linux-gate.so.1 => (0x00110000)
再来看看 GCC 4.1 版本以后引入的两个编译参数:
(默认情况下,编译时不会指定这两个参数,用于阻止缺乏安全编码意识的程序员写出存在缓冲区溢出漏洞的程序,
但是站在逆向工程的角度来看,指定这两个参数,特意构造有漏洞的程序,对于学习和理解栈溢出的原理还是有帮助的;
再次提醒,这里介绍的两个编译参数必须同时打开,而且还要同时禁用前面讲的可执行栈保护与栈地址随机初始化,满足这4个条件后,一般而言,就可以测试 shellcode 的效果了
)
GCC 编译选项 -fno-stack-protector
禁用栈保护功能,默认是启用的;
gcc 的栈保护机制是指,在栈的缓冲区(大小通常由程序员给定)写入内容前
,在结束地址之后与返回地址之前,放入随机的验证码,由于栈帧是从内存高
址段向内存低址段增长的(回顾第一张图),结束地址在高址段;返回地
址在低址段,栈溢出时,会从高址段向低址段覆盖数据,因此如果想要覆盖返
回地址的内容,必定先覆盖结束地址,验证码,才能覆盖返回地址,
因此可以通过比较写入缓冲区前后的验证码是否发生改变,来检测并阻止溢出
攻击
GCC 编译选项 -z execstack
启用可执行栈,默认是禁用的;
该选项与 ld 链接器有关:ld 链接器在链接程序的时候,如果所有的 .o 文
件的堆栈段都标记为不可执行,那么整个库的堆栈段才会被标记为不可执行;
相反,即使只有一个 .o 文件的堆栈段被标记为可执行,那么整个库的堆栈段
将被标记为可执行,
换言之,默认是所有的 .o 文件的堆栈段都标记为不可执行
检查堆栈段可执行性的方法是:
1
[root@centos6-5vm /]# readelf -lW stack-overflow1-test | grep GNU_STACK
找出在输出中有无 E 标记;有 E 标记就说明堆栈段是可执行的,其中,
stack-overflow1-test 为我们编写的简单栈溢出测试程序,源码如下:
#include <stdio.h>
greeting(char *temp1, char *temp2){
char name[20];
strcpy(name, temp2);
printf("hello %s %s\n", temp1, name);
}
main(int argc, char *argv[]){
greeting(argv[1], argv[2]);
printf("bye %s %s\n", argv[1], argv[2]);
}
这是一个典型的存在缓冲区溢出漏洞的程序,greeting 函数定义了一个只有20字节大小的字符数组(缓冲区),strcpy 函数不检查用户输入的第二个参数(由 main 函数的第二个参数传递)是否超过20字节,就写入这个缓冲区中
我们用 gcc 默认参数配置来编译这个源文件,然后检查堆栈段的可执行性:
[root@centos6-5vm /]# gcc -v -Wall -o stack-overflow1-test stack-overflow1-test.c
(***************省略部分输出*************
GNU C (GCC) 版本 4.4.7 20120313 (Red Hat 4.4.7-4) (i686-redhat-linux)
由 GNU C 版本 4.4.7 20120313 (Red Hat 4.4.7-4) 编译,GMP 版本 4.3.1,MPFR 版本 2.4.1。
GGC 准则:--param ggc-min-expand=100 --param ggc-min-heapsize=131072
Compiler executable checksum: 5f02f32570d532de29ae0b402446343a
stack-overflow1-test.c:2: 警告:返回类型默认为‘int’
stack-overflow1-test.c: 在函数‘greeting’中:
stack-overflow1-test.c:4: 警告:隐式声明函数‘strcpy’
stack-overflow1-test.c:4: 警告:隐式声明与内建函数‘strcpy’不兼容
stack-overflow1-test.c: 在文件层:
stack-overflow1-test.c:8: 警告:返回类型默认为‘int’
stack-overflow1-test.c: 在函数‘main’中:
stack-overflow1-test.c:11: 警告:在有返回值的函数中,控制流程到达函数尾
stack-overflow1-test.c: 在函数‘greeting’中:
stack-overflow1-test.c:6: 警告:在有返回值的函数中,控制流程到达函数尾
COLLECT_GCC_OPTIONS='-v' '-Wall' '-o' 'stack-overflow1-test' '-mtune=generic' '-march=i686'
as -V -Qy -o /tmp/ccv9qpvk.o /tmp/ccT7rHyO.s
GNU assembler version 2.20.51.0.2 (i686-redhat-linux) using BFD version version 2.20.51.0.2-5.36.el6 20100205
***************省略部分输出*************
可以看到, -Wall 选项(参考前文解释)显示所有的警告和错误信息,对于增加程序的可移植性非常有帮助,例如它指出在源码的二行,greeting 自定义函数没有定义返回类型,将采用默认返回类型:int
另外,在 greeting 函数中,调用 strcpy 函数前未声明和定义(在程序中调用 strcpy 函数需要包含系统头文件 string.h)
同样,我们没有为 main 函数指定返回类型
在上面的例子中,虽然每个警告都非致命的语法或词法错误,但是 -Wall 选项确实可以“强制”培养程序员的良好编程习惯
言归正传,检查生成的二进制可执行文件:
[root@centos6-5vm /]# readelf -lW stack-overflow1-test | grep GNU_STACK
GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RW 0x4
其中没有 E 标记,这说明该程序即便存在溢出漏洞,但是由于 GCC 的堆栈段不可执行保护机制,该漏洞也没有太大被利用的可能性
我们“故意”关掉 GCC 的堆栈段不可执行保护机制:指定 -z execstack 选项,再次编译源文件:
[root@centos6-5vm /]# gcc -v -Wall -z execstack -o stack-overflow1-test stack-overflow1-test.c
[root@centos6-5vm /]# readelf -lW stack-overflow1-test | grep GNU_STACK
GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RWE 0x4
这次的输出中,带有 E 标记,说明堆栈段是可执行的,
再次强调,在 CentOS6.5 中,要真正能利用这个程序测试你编写的 shellcode ,需要执行下面操作:
[root@localhost 桌面]# sysctl -w kernel.randomize_va_space=0
kernel.randomize_va_space = 0
[root@localhost 桌面]# sysctl -w kernel.exec-shield=0
kernel.exec-shield = 0
[root@localhost 桌面]# gcc -v -fno-stack-protector -z execstack -g -o stack-overflow1-test stack-overflow1-test.c
ubuntu –buffer-overflow
Ubuntu下面的GCC默认开启了Stack Smashing Protector,如果想在这个系统中学习缓冲区溢出的原理,在编译时要加上fno-stack-protector选项,否则运行时会出现*** stack smashing detected ***: xxx terminated,而不是期望的Segmentation fault。同时还需要加上允许栈执行的选项。
gcc -fno-stack-protector -z execstack -mpreferred-stack-boundary=2 -ggdb -o xxx xxx.c
execstack
用于阻止缓冲区溢出攻击的 Linux 内核参数与 gcc 编译选项的更多相关文章
- CSAPP:逆向工程【缓冲区溢出攻击】
逆向工程[缓冲区溢出攻击] 任务描述 掌握函数调用时的栈帧结构,利用输入缓冲区的溢出漏洞,将攻击代码嵌入当前程序的栈帧中,使程序执行我们所期望的过程. 主要方法 溢出的字符将覆盖栈帧上的数据,会覆盖程 ...
- Linux下缓冲区溢出攻击的原理及对策(转载)
前言 从逻辑上讲进程的堆栈是由多个堆栈帧构成的,其中每个堆栈帧都对应一个函数调用.当函数调用发生时,新的堆栈帧被压入堆栈:当函数返回时,相应的堆栈帧从堆栈中弹出.尽管堆栈帧结构的引入为在高级语言中实现 ...
- Linux下缓冲区溢出攻击的原理及对策
前言 从逻辑上讲进程的堆栈是由多个堆栈帧构成的,其中每个堆栈帧都对应一个函数调用.当函数调用发生时,新的堆栈 帧被压入堆栈:当函数返回时,相应的堆栈帧从堆栈中弹出.尽管堆栈帧结构的引入为在高级语言中实 ...
- linux内核参数sysctl.conf,TCP握手ack,洪水攻击syn,超时关闭wait
题记:优化Linux内核sysctl.conf参数来提高服务器并发处理能力 PS:在服务器硬件资源额定有限的情况下,最大的压榨服务器的性能,提高服务器的并发处理能力,是很多运维技术人员思考的问题.要提 ...
- linux内核参数sysctl.conf,TCP握手ack,洪水攻击syn,超时关闭wait(转)
http://www.xshell.net/linux/Linux_sysctl_conf.html 优化Linux内核sysctl.conf参数来提高服务器并发处理能力 Posted by 破冰 o ...
- CSAPP缓冲区溢出攻击实验(上)
CSAPP缓冲区溢出攻击实验(上) 下载实验工具.最新的讲义在这. 网上能找到的实验材料有些旧了,有的地方跟最新的handout对不上.只是没有关系,大体上仅仅是程序名(sendstring)或者參数 ...
- CSAPP缓冲区溢出攻击实验(下)
CSAPP缓冲区溢出攻击实验(下) 3.3 Level 2: 爆竹 实验要求 这一个Level的难度陡然提升,我们要让getbuf()返回到bang()而非test(),并且在执行bang()之前将g ...
- CSAPP阅读笔记-变长栈帧,缓冲区溢出攻击-来自第三章3.10的笔记-P192-P204
一.几个关于指针的小知识点: 1. malloc是在堆上动态分配内存,返回的是void *,使用时会配合显式/隐式类型转换,用完后需要用free手动释放. alloca是标准库函数,可以在栈上分配任 ...
- Linux内核参数配置
Linux在系统运行时修改内核参数(/proc/sys与/etc/sysctl.conf),而不需要重新引导系统,这个功能是通过/proc虚拟文件系统实现的. 在/proc/sys目录下存放着大多数的 ...
随机推荐
- Linux内核分析第十八章读书笔记
第十八章 调试 调试工作艰难是内核级开发区别于用户级开发的一个显著特点. 18.1 准备开始 我们需要什么? 一个bug 一个藏匿bug的内核版本 思路:假定能够让bug重现 在用户级程序中,bug直 ...
- Mybatis:Eclipse引入dtd约束文件使得xml文件有提示
https://blog.csdn.net/lsx2017/article/details/82558135
- Load balancing 各组件的比较
F5的Big-IP F5 lvs Nginx HAProxy ApacheProxy lighttpd Dubbo 专有硬件 是 Linux ALL IP 否 TCP 是 是 ...
- C语言入门:01.C语言概述
一.计算机和软件常识 1.计算机运行原理 (1)硬件基本组成:硬盘.内存.CPU (2)个部件之间的运作协调(下图)
- Js单元测试工具 以及 粗浅的对我的快乐运算进行测试
1. Karma的介绍 Karma是Testacular的新名字,在2012年google开源了Testacular,2013年Testacular改名为Karma.Karma是一个让人感到非常神秘的 ...
- send和sendmsg性能测试【sendmsg和send的性能基本一样,并没有得到优化】
1,摘要:测试send和sendmsg的性能,影响这两个函数性能的因素主要有发送的字节大小,增加循环次数,从100到10000000(千万)减少计算误差 2,基本信息cat /proc/cpuinfo ...
- Redis的核心Hystrix在Spring mvc的使用
核心Hystrix,Hystrix对于接口调用具有很好的保护,能在多服务依赖的分布式系统中,有效的提供应用的可用性,并且对失败应用进行熔断和恢复检查,让应用在复杂的环境中也能各种稳. http://t ...
- MT【92】空间余弦定理解题
评:学校常规课堂教学里很少讲到这个,有点可惜.
- 【题解】 bzoj2435: [Noi2011]道路修建 (傻逼题)
bzoj2435,懒得复制,戳我戳我 Solution: 模拟即可(有点傻逼啊 Code: //It is coded by Ning_Mew on 5.13 #include<bits/std ...
- 【转】#pragma的用法
在所有的预处理指令中,#Pragma 指令可能是最复杂的了,它的作用是设定编译器的状态或者是指示编译器完成一些特定的动作.#pragma指令对每个编译器给出了一个方法,在保持与C和C++语言完全兼容的 ...