2014年,OpenSSL
加密库中的一个缓冲区溢出漏洞被公开。该缺陷被称为“心脏出血”。它使受欢迎的在线服务和软件平台的数亿用户暴露于易受攻击的OpenSSL
软件版本中。于是操作系统提供了许多安全机制来尝试降低或阻止缓冲区溢出攻击带来的安全风险,包括DEP
(Linux
下对应NX
)、ASLR
(Linux
下对应PIE
)等。我们先从什么是缓冲区溢出开始。
什么是缓冲区溢出?
缓冲区溢出漏洞,顾名思义,就是计算机对接收的输入数据没有进行有效的检测(理想的情况是程序检查数据长度并不允许输入超过缓冲区长度的字符),向缓冲区内填充数据时超过了缓冲区本身的容量,而导致数据溢出到被分配空间之外的内存空间,使得溢出的数据覆盖了其他内存空间的数据。
缓冲区溢出代码示例
缓冲区溢出的想法很简单。以下是具有缓冲区溢出漏洞的C程序的源代码:
char greeting[5];
memcpy(greeting, "Hello, world!\n", 15);
printf(greeting);
当我们编译并运行此有漏洞的程序时,你认为会发生什么?答案可能令人惊讶:任何事情都可能发生。执行此代码段时,它将尝试将15个字节放入只有5个字节长的目标缓冲区中。这意味着将有十个字节写入数组外部的内存地址。以后发生的情况取决于被覆盖的十个字节的内存的原始内容。也许重要的变量存储在这里,而我们刚好更改了它们的值?
如何防止缓冲区溢出
通常的做法是编写安全的代码。同时当今的操作系统以不可执行的堆栈和地址空间布局随机化(ASLR
)的形式提供了其他防御措施。不可执行的堆栈(即数据执行保护DEP
)将堆栈以及在某些情况下的其他结构标记为无法执行代码的区域。这意味着攻击者无法将利用代码注入堆栈并期望其成功运行。
编写安全的代码
编写正确的代码是一件非常有意义的工作,特别像编写C语言那种风格自由而容易出错的程序,这种风格是由于追求性能而忽视正确性的传统引起的。尽管花了很长的时间使得人们知道了如何编写安全的程序,具有安全漏洞的程序依旧出现。比如C标准库里面的strcpy
,strcat
,sprintf
函数都没有缓冲区溢出保护功能。
程序员必须通过始终验证用户输入长度来避免缓冲区溢出攻击。但是,避免缓冲区溢出漏洞的一种通用方法是坚持使用包括缓冲区溢出保护的安全功能。此类功能可在不同平台上使用,例如,strlcpy
,strlcat
,snprintf
(OpenBSD
)或strcpy_s
,strcat_s
,sprintf_s
(Windows
)。下表列出了有C标准库漏洞函数的一些更安全替代品:
Linux
系统中的DEP
和ASLR
保护机制
Linux
系统中的DEP
通过NX
来实现,NX
即No-eXecute
(不可执行)的意思,NX
的基本原理是将数据所在内存页标识为不可执行,当程序溢出成功转入shellcode
时,程序会尝试在数据页面上执行指令,此时CPU
就会抛出异常,而不是去执行恶意指令。
gcc
编译器默认开启了NX
选项,如果需要关闭NX
选项,可以给gcc
编译器添加-z execstack
参数。
例如:
gcc -o test test.c // 默认情况下,开启NX保护
gcc -z execstack -o test test.c // 禁用NX保护
gcc -z noexecstack -o test test.c // 开启NX保护
开发ASLR
是为了防御面向返回的程序设计(一种针对非可执行堆栈的解决方法,在该堆栈中,现有代码段根据其在内存中的地址偏移量被链接在一起),ASLR
和DEP
配合使用,能有效阻止攻击者在堆栈上运行恶意代码。内存地址随机化机制有以下三种情况:
0 - 表示关闭进程地址空间随机化。
1 - 表示将mmap的基址,stack和vdso页面随机化。
2 - 表示在1的基础上增加堆(heap)的随机化。
Linux
系统中的ASLR
机制是通过PIE
(position-independent executables)来实现的。gcc
编译命令如下:
# -fPIE/-fpie这个选项与-fPIC/-fpic大致相同,不同点在于:-fPIC用于生成动态库,-fPIE用与生成可执行文件。
gcc -o test test.c // 默认情况下,不开启PIE
gcc -fpie -pie -o test test.c // 开启PIE,此时强度为1
gcc -fPIE -pie -o test test.c // 开启PIE,此时为最高强度2
gcc -fpic -o test test.c // 开启PIC,此时强度为1,不会开启PIE
gcc -fPIC -o test test.c // 开启PIC,此时为最高强度2,不会开启PIE
使用checksec
工具来检查系统和程序的属性
Checksec
是一个bash
脚本,用于检查可执行文件的属性(例如PIE,RELRO,PaX,Canaries,ASLR,Fortify Source)。源码请参考:checksec.sh
首先我们下载这个工具:
$ git clone https://github.com/slimm609/checksec.sh.git
然后我们就可以使用这个工具来检查Linux
系统和程序的一些安全属性,包括之前提到的DEP
和ASLR
机制,下面是一些简单的例子:
$ cd checksec.sh
$ ./checksec --file=/bin/ls
RELRO STACK CANARY NX PIE RPATH RUNPATH Symbols FORTIFY Fortified Fortifiable FILE
Full RELRO Canary found NX enabled PIE enabled No RPATH No RUNPATH No Symbols Yes 5 17 /bin/ls
$ ./checksec --kernel
* Kernel protection information:
Description - List the status of kernel protection mechanisms. Rather than
inspect kernel mechanisms that may aid in the prevention of exploitation of
userspace processes, this option lists the status of kernel configuration
options that harden the kernel itself against attack.
Kernel config:
/boot/config-5.3.0-45-generic
Warning: The config on disk may not represent running kernel config!
Running kernel: 5.3.0-45-generic
Vanilla Kernel ASLR: Full
NX protection: Skipped
Protected symlinks: sysctl: permission denied on key 'fs.protected_symlinks'
Disabled
Protected hardlinks: sysctl: permission denied on key 'fs.protected_hardlinks'
Disabled
Protected fifos: sysctl: permission denied on key 'fs.protected_fifos'
Unsupported
Protected regular: sysctl: permission denied on key 'fs.protected_regular'
Unsupported
Ipv4 reverse path filtering: Enabled
Kernel heap randomization: Enabled
GCC stack protector support: Enabled
GCC stack protector strong: Enabled
SLAB freelist randomization: Enabled
Virtually-mapped kernel stack: Enabled
Restrict /dev/mem access: Enabled
Restrict I/O access to /dev/mem: Disabled
Enforce read-only kernel data: Enabled
Enforce read-only module data: Enabled
Full reference count validation: Disabled
Exec Shield: Unsupported
Hardened Usercopy: Enabled
Harden str/mem functions: Enabled
Restrict /dev/kmem access: Enabled
* X86 only:
Address space layout randomization: Enabled
* SELinux: Disabled
SELinux infomation available here:
http://selinuxproject.org/
* grsecurity / PaX: No GRKERNSEC
The grsecurity / PaX patchset is available here:
http://grsecurity.net/
而这个强大的工具同样可以在嵌入式Linux
系统中直接使用,这简直是太棒了!