CTF-All-In-One/doc/4.3_gcc.md
2017-08-16 15:07:55 +08:00

6.8 KiB
Raw Blame History

GCC 堆栈保护技术

技术简介

Linux 中有各种各样的安全防护,其中 ASLR 是由内核直接提供的通过系统配置文件控制。NXCanaryPIERELRO 等需要在编译时根据各项参数开启或关闭。未指定参数时,使用默认设置。

CANARY

启用 CANARY 后,函数开始执行的时候会先往栈里插入 canary 信息,当函数返回时验证插入的 canary 是否被修改,如果是,就停止运行。

FORTIFY

FORTIFY 的选项 -D_FORTIFY_SOURCE 往往和优化 -O 选项一起使用,以检测缓冲区溢出的问题。

下面是一个简单的例子:

#include<string.h>
void main() {
	char str[3];
	strcpy(str, "abcde");
}
$ gcc -O2 fortify.c
$ checksec --file a.out
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH	FORTIFY	Fortified Fortifiable  FILE
Partial RELRO   No canary found   NX enabled    PIE enabled     No RPATH   No RUNPATH   No	0	0a.out

$ gcc -O2 -D_FORTIFY_SOURCE=2 fortify.c
In file included from /usr/include/string.h:639:0,
                 from fortify.c:1:
In function strcpy,
    inlined from main at fortify.c:4:2:
/usr/include/bits/string3.h:109:10: warning: __builtin___memcpy_chk writing 6 bytes into a region of size 3 overflows the destination [-Wstringop-overflow=]
   return __builtin___strcpy_chk (__dest, __src, __bos (__dest));
          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
$ checksec --file a.out
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH	FORTIFY	Fortified Fortifiable  FILE
Partial RELRO   Canary found      NX enabled    PIE enabled     No RPATH   No RUNPATH   Yes	2	2a.out

开启优化 -O2编译没有检测出任何问题checksec 后 FORTIFY 为 No。当配合 -D_FORTIFY_SOURCE=2(也可以 =1使用时提示存在溢出问题checksec 后 FORTIFY 为 Yes。

NX

No-eXecute表示不可执行其原理是将数据所在的内存页标识为不可执行如果程序产生溢出转入执行 shellcode 时CPU 会抛出异常。其绕过方法是 ret2libc。

PIE

PIEPosition Independent Executable需要配合 ASLR 来使用以达到可执行文件的加载时地址随机化。简单来说PIE 是编译时随机化由编译器完成ASLR 是加载时随机化,由操作系统完成。

我们通过实际例子来探索一下 PIE 和 ASLR

#include<stdio.h>
void main() {
	printf("%p\n", main);
}

首先我们关闭 ASLR使用 -pie 进行编译:

# echo 0 > /proc/sys/kernel/randomize_va_space
# gcc -m32 -pie random.c -o a.out
# checksec --file a.out
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH	FORTIFY	Fortified Fortifiable  FILE
Partial RELRO   No canary found   NX enabled    PIE enabled     No RPATH   No RUNPATH   No	0		2	a.out

# ./a.out
0x5655553d
# ./a.out
0x5655553d

我们虽然开启了 -pie,但是 ASLR 被关闭,入口地址不变。

# ldd a.out
	linux-gate.so.1 (0xf7fd7000)
	libc.so.6 => /usr/lib32/libc.so.6 (0xf7dd9000)
	/lib/ld-linux.so.2 (0xf7fd9000)
# ldd a.out
	linux-gate.so.1 (0xf7fd7000)
	libc.so.6 => /usr/lib32/libc.so.6 (0xf7dd9000)
	/lib/ld-linux.so.2 (0xf7fd9000)

可以看出动态链接库地址也不变。然后我们开启 ASLR

# echo 2 > /proc/sys/kernel/randomize_va_space
# ./a.out
0x5665353d
# ./a.out
0x5659753d
# ldd a.out
	linux-gate.so.1 (0xf7727000)
	libc.so.6 => /usr/lib32/libc.so.6 (0xf7529000)
	/lib/ld-linux.so.2 (0xf7729000)
# ldd a.out
	linux-gate.so.1 (0xf77d6000)
	libc.so.6 => /usr/lib32/libc.so.6 (0xf75d8000)
	/lib/ld-linux.so.2 (0xf77d8000)

入口地址和动态链接库地址都变得随机。

接下来关闭 ASLR并使用 -no-pie 进行编译:

# echo 0 > /proc/sys/kernel/randomize_va_space
# gcc -m32 -no-pie random.c -o b.out
# checksec --file b.out
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH	FORTIFY	Fortified Fortifiable  FILE
Partial RELRO   No canary found   NX enabled    No PIE          No RPATH   No RUNPATH   No	0		2	b.out

# ./b.out
0x8048406
# ./b.out
0x8048406
# ldd b.out
	linux-gate.so.1 (0xf7fd7000)
	libc.so.6 => /usr/lib32/libc.so.6 (0xf7dd9000)
	/lib/ld-linux.so.2 (0xf7fd9000)
# ldd b.out
	linux-gate.so.1 (0xf7fd7000)
	libc.so.6 => /usr/lib32/libc.so.6 (0xf7dd9000)
	/lib/ld-linux.so.2 (0xf7fd9000)

入口地址和动态库都是固定的。下面开启 ASLR

# echo 2 > /proc/sys/kernel/randomize_va_space
# ./b.out
0x8048406
# ./b.out
0x8048406
# ldd b.out
	linux-gate.so.1 (0xf7797000)
	libc.so.6 => /usr/lib32/libc.so.6 (0xf7599000)
	/lib/ld-linux.so.2 (0xf7799000)
# ldd b.out
	linux-gate.so.1 (0xf770a000)
	libc.so.6 => /usr/lib32/libc.so.6 (0xf750c000)
	/lib/ld-linux.so.2 (0xf770c000)

入口地址依然固定,但是动态库变为随机。

所以在分析一个 PIE 开启的二进制文件时,只需要关闭 ASLR即可使 PIE 和 ASLR 都失效。

ASLRAddress Space Layout Randomization

关闭:# echo 0 > /proc/sys/kernel/randomize_va_space

部分开启(将 mmap 的基址stack 和 vdso 页面随机化):# echo 1 > /proc/sys/kernel/randomize_va_space

完全开启(在部分开启的基础上增加 heap的随机化# echo > /proc/sys/kernel/randomize_va_space

RELRO

RELROReLocation Read-Only设置符号重定向表为只读或在程序启动时就解析并绑定所有动态符号从而减少对 GOTGlobal Offset Table的攻击。

编译参数

各种安全技术的编译参数如下:

安全技术 完全开启 部分开启 关闭
Canary -fstack-protector-all -fstack-protector -fno-stack-protector
NX -z noexecstack -z execstack
PIE -pie -no-pie
RELRO -z now -z lazy -z norelro

关闭所有保护:

gcc hello.c -o hello-L -fstack-protector -z execstack -no-pie -z norelro

开启所有保护:

gcc hello.c -o hello-S -fstack-protector-all -z noexecstack -pie -z now
  • FORTIFY
    • -D_FORTIFY_SOURCE=1:仅在编译时检测溢出
    • -D_FORTIFY_SOURCE=2:在编译时和运行时检测溢出

保护机制检测

有许多工具可以检测二进制文件所使用的编译器安全技术。下面介绍常用的几种:

checksec

$ checksec --file /bin/ls
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH	FORTIFY	Fortified Fortifiable  FILE
Partial RELRO   Canary found      NX enabled    No PIE          No RPATH   No RUNPATH   Yes	5		15	/bin/ls

peda 自带的 checksec

$ gdb /bin/ls
gdb-peda$ checksec
CANARY    : ENABLED
FORTIFY   : ENABLED
NX        : ENABLED
PIE       : disabled
RELRO     : Partial