首页
社区
课程
招聘
[翻译]加固 C/C++ 程序
发表于: 2018-1-16 23:01 5044

[翻译]加固 C/C++ 程序

2018-1-16 23:01
5044

在这个文章的开头部分,我们先讨论一下编码的机制,加固的概念,尤其是栈的加固技巧。这里解释的一些概念也将在下面的部分中使用到,所以你可能至少要阅读前几段。

如第一部分所述,渗透常常会通过向程序中注入代码的方法,来使数据结构溢出,比如char 寄存器。接下来执行的代码就会跳转到攻击者可以进一步使用利用的内存位置,例如请求ROOT权限的shell脚本,(因此常称作“shell code”)。注意,即使代码是保存在程序的数据内存空间中(栈或是堆),它仍然可以被执行。可执行区域保护通过将这些内存页标记为需要它的可执行文件来更改此操作。这在大多数现代处理器和大多数操作系统上都有支持。不同厂家的名称各不相同,但基本概念保持不变。

尤其是Linux系统中,解决的措施如下:

(1)当程序被加载到内存中,只有这些内存页中的代码才允许执行。加载器会识别ELF头部。这些部分包含了一下代码:

                           1)  .init 和 .fini:这是在初始化和清除进程是运行的代码;
                           2) .plt 和.plt.got: 用于访问位于其他共享库中的函数的Trampoline代码;
                           3) .text: 其他的,也就是程序中实现功能的代码;

(2)堆没有执行权限。

(3)ELF文件的可执行源代码和共享库也包含了GNU_STACK程序头部,也就意味着有执行栈内存页面的权限。默认情况下,不会设置执行标志,而堆栈只具有读/写权限。有三个例外:

                           1)当链接到一个可执行文件或共享库,-z execstack连接器会生成一个明确的标志;

                           2) 至少有一个目标文件是由汇编程序生成的。在这种情况下,还不知道堆栈是否可以在没有执行权限的情况下进行映射。需要一个明确的声                                  明: .section .note.GNU-stack,"",@progbits;

                           3)当你使用嵌套函数,有一个GNU C扩展(在GNU C++不可用);

可执行区域保护是很重要,幸运的是许多漏洞已经被处理了。接下来要做的是正确的gnu_stack设置,因此我们将重点放在这。

Linux使用最简化的GNU_STACK, 这就意味着你的共享库中的程序块只要有一个被标记为需要一个可执行栈,那么整个程序都会以这样的方式运行。即使是 dlopen()库也是一样的--内核将在运行时更改权限。!从安全的角度,这样会很不方便:你必须非常小心,不能在配置中将库错误设置。不涉及汇编源程序情况下,当你自己把所有东西都建好,那么应该就没问题。可以从分布服务器加载的共享库应该始终正确配置此标志。也就是说,我建议做两个测试:

1.      对于所有的可执行代码和共享库,检查他们的输出readelf -l ,这gnu_stack入口只有RW设置标志。应该像这样的:

它不应该是这样(注意E(执行)现在出现在这个标志中)

2.      在启动你的程序时可以测试一下,然后导入所有的动态链接库。检查在/proc/PID/maps(其中pid由运行程序的数字进程ID替换)路径下,[stack]是否正确映射。就如上面所提到的,此测试将排除某些库在运行时更改堆栈权限。运行的过程应该是这样:

而不是这样(注意:x(执行)现在存在):

注意:在JIT编码器中存在一个漏洞,需要在运行时生成代码。编程人员通常会注意将代码映射到只写/不可执行得内存空间,然后切换到只执行/写保护的内存空间。但一旦能够成功诱导JIT编译器生成代码时,这种做法也就失效了。

通过预防可执行代码被注入到程序中,可以消除很大部分的攻击媒介。但是假如一些对攻击者十分有用的代码已经保存在程序中了要怎么办?例如,所有的程序都链接到libc中,这样可以通过调用system()来启动SHELL。攻击者只要能覆盖栈指针中的函数返回地址,让指针指向system()的地址。这是内存地址布局随机化的本质:分为许多内存映射尽可能使系统无法预知的。在现代Linux系统中,这些区域受到影响:

主程序中可执行的代码

共享库的代码

堆和栈

mmap_base

VDSO 文件

部分内核本身

ASLR是在Linux内核中用到的。一般是在64位系统中使用,因为在可用的地址空间中随机保存会使地址范围大得多,极大地降低了遍历攻击的机会。如果拥有ROOT权限可以禁用ASLR:

这对调试来说很有用,但是它不应该被永远禁用。. gdb本身会默认禁用ASLR,让接下来的调试对话框中得地址都是恒定的。

堆和栈的随机分布是自动的,所以在当前这样的系统不需要执行什么命令。 为了能够在随机地址中加载主可执行文件和共享库,它们的代码必须是位置无关的。这实现如下:

使用-fpic 或-fPIC编译。-fpic在一些结构中是使用一个范围有限的GOT(全局偏置表)。 当地址溢出时,连接器就会提醒你。这时你就要转换到-fPIC ,这可能会有一些开销。在X86系统中,这两者是没差别的;

链接到-shared,指定和编译期间使用的相同选项(-fpicor-fPIC).;


[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

收藏
免费 1
支持
分享
最新回复 (1)
雪    币: 144
活跃值: (38)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
2018-1-17 10:24
0
游客
登录 | 注册 方可回帖
返回
//