Canary
Stack Canaries(栈金丝雀)是一种用于检测缓冲区溢出的安全机制。它的基本思想是在栈帧中插入一个特殊的值(称为“canary”),并在函数返回前检查这个值是否被篡改。如果canary值被修改,说明发生了缓冲区溢出,系统可以采取相应的措施(如终止程序)来防止进一步的攻击。下面详细介绍Stack Canaries的来源、原理、防护、检查和绕过方法。
来源
Stack Canaries的概念最早出现在1997年,由Crispin Cowan等人在他们的论文《StackGuard: Automatic Adaptive Detection and Prevention of Buffer-Overflow Attacks》中提出。StackGuard是第一个广泛使用的栈保护机制,它通过在栈帧中插入一个随机值来检测缓冲区溢出。
原理
- 插入Canary值:在函数调用时,编译器会在栈帧中插入一个特殊值(canary)。这个值通常是一个随机数,存储在栈帧的顶部,位于缓冲区和返回地址之间。
- 检查Canary值:在函数返回前,编译器会检查canary值是否被修改。如果canary值被修改,说明缓冲区可能已经被溢出,系统会采取措施(如终止程序)来防止进一步的攻击。
- 随机化:为了增加安全性,canary值通常是随机生成的,每次程序启动时都会有所不同,这使得攻击者难以预测canary值。
防护
- 检测缓冲区溢出:通过在栈帧中插入canary值,可以在函数返回前检测到缓冲区溢出,从而防止攻击者覆盖返回地址。
- 随机化:canary值的随机化增加了攻击的难度,因为攻击者无法预先知道canary的具体值。
- 集成到编译器:现代编译器(如GCC和Clang)提供了对Stack Canaries的支持,可以通过编译选项(如
-fstack-protector
)启用。
检查
- 编译器插桩:编译器在生成代码时会自动插入canary值的插入和检查逻辑。
- 运行时检查:在函数返回前,运行时系统会检查canary值是否被修改。如果被修改,会触发一个异常,通常会导致程序终止。
绕过方法
尽管Stack Canaries大大提高了系统的安全性,但攻击者仍然可以通过以下方法绕过这种保护机制:
- 暴力破解:攻击者可以通过多次尝试来猜测canary值。虽然这种方法效率低下,但在某些情况下仍然可行。
- 信息泄露:如果程序中存在其他漏洞(如格式字符串漏洞),攻击者可能能够泄露canary值,从而绕过保护。
- 绕过canary检查:攻击者可以构造特殊的输入,使canary值保持不变,但仍然能够覆盖返回地址。例如,通过覆盖canary值后面的其他数据(如函数指针)来实现攻击。
- Return-Oriented Programming (ROP):ROP是一种高级攻击技术,通过利用程序中存在的可执行代码片段来构建恶意行为,绕过canary保护。
NX
Linux中的NX保护,全称为No-eXecute(不可执行),是一种安全机制,其核心原理是将数据所在的内存页标识为不可执行。这样做的目的是为了防止恶意代码通过溢出攻击(如缓冲区溢出)来执行。当攻击者尝试利用程序溢出将控制权转移到包含恶意代码的数据页面上时,CPU会抛出异常,而不是执行这些恶意指令。
NX保护机制通过阻止在数据页上执行代码,有效地减少了利用栈溢出攻击的风险。在默认情况下,许多现代操作系统和编译器(如GCC)都会启用NX保护。如果需要关闭NX保护,可以在编译时使用特定的编译器选项,例如GCC中的-z execstack
参数来禁用NX保护,或者使用-z noexecstack
来启用NX保护。
NX保护是操作系统层面的安全特性,与应用程序的代码无关,它依赖于CPU的支持。在支持NX的CPU上,操作系统可以将内存页标记为可执行或不可执行,从而提供额外的安全层。这种保护机制对于提高系统的安全性至关重要,因为它能够防止许多类型的内存攻击,尤其是那些依赖于在栈或其他数据段执行恶意代码的攻击。
PIE(ASLR)
PIE(Position Independent Executable)和ASLR(Address Space Layout Randomization)是Linux系统中两种增强安全性的技术,它们共同作用于提高系统的安全性,尤其是针对缓冲区溢出攻击。
-
PIE(Position Independent Executable):
- PIE是一种编译技术,它允许程序在编译时生成位置无关的代码。这意味着程序的代码段可以在内存中任意位置执行,而不需要任何地址的重定位。
- 当PIE与ASLR结合使用时,程序的代码段、数据段以及库等都会被加载到随机的内存地址,增加了攻击者预测和利用程序漏洞的难度。
-
ASLR(Address Space Layout Randomization):
- ASLR是一种操作系统级别的安全功能,它通过在程序加载时随机化堆、栈、共享库等内存区域的地址来增加攻击者预测目标地址的难度,从而降低成功攻击的可能性。
- Linux上的ASLR分为三个等级,可以通过内核参数
randomize_va_space
进行控制:- 0:关闭ASLR。
- 1:部分随机化,包括共享库、栈、mmap()以及VDSO。
- 2:完全随机化,在1的基础上,通过brk()分配的内存空间也将随机化。
- ASLR不负责代码段和数据段的随机化工作,这部分工作由PIE完成。
RELRO
RELRO是一种安全特性,用于保护程序的全局偏移表(GOT)和程序的数据段,防止攻击者通过修改这些区域来执行恶意代码。RELRO有两种模式:
-
Partial RELRO:这是默认的模式,它将ELF文件的内部数据段(如.got、.dtors等)放在程序数据段(如.data和.bss)之前,使得没有PLT(过程链接表)指向的GOT条目是只读的。这意味着攻击者不能修改这些条目来改变函数调用的目标地址。但是,GOT表的其他部分仍然是可写的,这为攻击者提供了修改其他GOT条目的可能。
-
Full RELRO:在这种模式下,整个GOT表都被映射为只读的。这提供了更严格的保护,因为它防止了对任何GOT条目的修改。Full RELRO可以通过编译时使用
-Wl,-z,relro,-z,now
选项来启用。