RIPE包含850种缓冲区溢出 的攻击形式
ProPolice(栈保护stack-smashing)未命中60%,
LibSafePlus+BIED(缓冲区溢出检测)未命中23%,
CRED未命中21%,
Ubuntu 9.10不可执行内存和堆栈保护未命中11%
虽然对于缓冲区溢出攻击有具体的性能测试基准SPEC,但没有标准的方法来测试和比较这些对策的覆盖范围。
RIPE目的:执行广泛的缓冲区溢出攻击,记录其成功或失败,来量化给定防御措施的保护范围。
850=4个缓冲区溢出位置*16个目标代码指针*2种溢出技术*5种正在执行的攻击代码变体*10个被滥用的函数
对策:金丝雀,边界检查,复制和检查目标数据,库包装,不可执行内存
C后端:允许用户动态指定她想要测试的缓冲区溢出类型
python前端:
测试维度:5种
1,位置,缓冲区溢出位置:堆,栈,BSS,数据段
2,目标代码指针:指向攻击代码的代码指针,
返回地址
旧的基指针:EBP寄存器的先前内容,用于引用函数参数和局部变量
函数指针:通用函数指针允许程序员从相同的代码中动态调用不同的函数
Longjmp缓冲区:Setjmp/longjmp是一种技术,它允许程序员轻松地跳转回他们代码中的预定义点
脆弱结构:将一个缓冲区和一个函数指针组合在一起的结构,可被攻击者滥用,从一个缓冲区溢出到另一个缓冲区
3,溢出技术
直接溢出:溢出目标与溢出的缓冲区相邻
间接溢出:利用泛型指针,首先泛型指针被目标地址溢出,其次在以后的解引用(取指针指向地址的内容)中,目标被攻击者控制的数据覆盖
4,攻击代码:生成shell攻击代码,在特定的目录创建文件攻击代码
shellcode without NOP sled:该选项可用于测试攻击的准确性,以及挑战依赖于检测进程地址空间中特定代码模式(如一组0x90字节(NOP))的对策。
shellcode with NOP sled:这是shellcode最常用的形式,它在攻击者的功能之前添加了一组无操作指令,以提高攻击者将程序的执行流正确重定向到其注入代码中的机会。
shellcode with polymorphic NOP sled:在这种情况下,NOP sled不是标准的0x90字节集,而是一组可以执行而不影响实际攻击代码正确性的指令
Return-into-libc:是指攻击者不会在进程的地址空间中注入新的代码,而是使用现有的函数来发起攻击
Return-Oriented Programming (ROP):一旦攻击者获得了对执行流的控制,面向返回的编程[38]就是执行输出利用的最新方式。ROP是返回到libc的推广,现在攻击者可以使用现有代码(小工具)中的功能块,并将它们组合起来创建新的功能。
5,函数滥用memcpy(),strcpy(),strncpy(),strncpy(),sprintf(),snprintf(),strcat(),sscanf(),fscanf()
构建有效载荷并对自身进行攻击
运行时缓冲区溢出保护
金丝雀:通过在敏感的存储区域添加一个金丝雀值来防止缓冲区溢出。金丝雀的完整性在敏感的记忆被使用之前被检查。如果金丝雀已经被改变,敏感的记忆可能已经被破坏,程序通常被终止。
边界检查工具
复制和检查目标数据:使用在单独的堆栈上存储返回地址副本的技术的缓冲区溢出防止工具。当一个函数返回时,它存储的返回地址将与独立堆栈上的副本进行核对。如果地址不同,要么复制回正确的地址,要么暂停执行
库包装器:它对C语言中构成潜在缓冲区溢出漏洞的库函数进行补丁。在修补过的函数中,在实际的函数调用之前进行范围检查。
不可执行和随机内存:它对C语言中构成潜在缓冲区溢出漏洞的库函数进行补丁。在修补过的函数中,在实际的函数调用之前进行范围检查。随机化从内存位置基础的地址偏移,称为地址空间布局随机化,以进一步对抗缓冲区溢出攻击
评估实验的建立
使用RIPE的评估:ProPolice (canary-based), CRED (boundarychecking), StackShield and Libverify (copying and checkingtarget data), Libsafe, LibsafePlus, LibsafePlus+TIED (library wrappers), and PAE and XD (non-executable memory).
攻击形式理论数量3840=4个位置* 16个目标代码指针* 2种技术* 3种攻击代码变体,没有NOP sled变体* 10个函数被滥用-2990种不可能的攻击形式(例如,由于未映射的堆页和分隔堆栈和堆的保护页,不可能在从BSS段到栈的所有路径上执行直接缓冲区溢出。)
ProPlice:借用了StackGuard的主要思想——canary,或保护值来检测对栈的攻击。保护区位于缓冲区和旧的基指针之间,这意味着它保护返回指针和旧的基指针免受直接溢出的影响
StackShield:GCC编译器补丁。它实现了三种类型的保护,两种防止重写返回地址(两者可以同时使用),一种防止重写函数指针
Global Ret Stack是一个独立的栈,用于存储执行过程中调用的函数的返回地址。栈是32位条目的全局数组。每当进行函数调用时,被推送到正常栈的返回地址同时被复制到全局返回栈数组中。当函数返回时,正常栈上的返回地址被全局返回栈上的副本替换
Ret Range Check:更简单更快的版本使用一个全局变量来存储当前函数的返回地址。在返回之前,栈上的返回地址与全局变量中存储的副本进行比较。如果存在差异,执行将被暂停。请注意,与上述全局Ret栈相反,Ret范围检查可以检测攻击
Protection of Function Pointers还旨在保护函数指针不被覆盖。这个想法是,函数指针通常应该指向进程内存的代码段,程序员可能已经实现了指向的函数。如果进程能够确保不允许函数指针指向除代码段之外的其他内存部分,那么攻击者就不可能将它指向注入到进程中的代码,因为只能将数据注入到堆栈、堆、BSS或数据段中,StackShield在所有使用函数指针的函数调用之前添加检查代码。然后在数据段中声明一个全局变量,其地址用作边界值。检查函数确保任何将要被取消引用的函数指针指向低于全局边界变量地址的内存。如果它指向边界以上,则过程终止。如果程序使用动态分配的函数指针,这种保护会产生误报
Libsafe and Libverify提供了静态和动态入侵防御的结合。它静态地修补了C语言中构成潜在缓冲区溢出漏洞的库函数。在实际函数调用之前进行范围检查,以确保返回地址和基指针不会被覆盖。使用类似的动态方法对堆栈进行验证,Libsafe是在运行时估计堆栈上缓冲区的安全边界,然后在允许任何易受攻击的函数写入缓冲区之前检查该边界。Libsafe作为一个边界值,Libsafe使用返回地址后被推送到堆栈上的旧基指针。不应该允许任何局部变量在堆栈中比旧的基指针的开头扩展得更远。这样,基于堆栈的缓冲区溢出不会覆盖返回地址。Libverify关键思想是改变一个进程中的所有函数,这样每个函数做的第一件事就是将返回地址复制到位于堆上的栈canary上,返回之前做的最后一件事是通过将返回地址与canary栈上保存的地址进行比较来验证返回地址。
LibsafePlus and TIED:TIED收集静态信息,LibsafePlus收集关于栈和堆缓冲区大小的动态信息。该信息在运行时用于确保没有字符缓冲区被写入超过其限制
CRED只对字符串缓冲区进行边界检查
不可执行内存和栈保护器(Ubuntu 9.10)
实际评估结果
总体有效性是被阻止的攻击形式的百分比。成功的攻击会导致可重复的任意代码执行。部分成功的攻击有时是成功的,有时是不成功的,总体上不太稳定。失败的攻击被反复阻止。
RIPE是一个攻击自身,然后检查所发起攻击的成功或失败的过程。