难点
- 特殊字符需要转码。
- 比如strcpy等函数造成的缓冲区溢出,会认为NULL是字符串的终结,所以shellcode中不能有null,如果有则需要变通或编码。
- 函数API定位很困难。
- 比如在windows系统下,系统调用多数都是封装在高级API中来调用的,而且不同的ServicePack或版本的操作系统其API都可能有所改动,所以不可能直接调用,因此需要采用动态的方法获取API地址。
简单的编写shellcode的方法步骤
- 用C语言书写要执行的shellcode,利用调试功能,找到其对应的汇编代码
- 对汇编语言进行再加工,对于push 0而言,可以通过xor ebx ebx 之后在push ebx 来实现。
- 在汇编第一行代码打断点,利用调试定位具体内存中的地址,找到对应地址中的机器码。
- 编写测试程序进行测试。
Windows中的防范措施
ASLR(address space layout randomization)
- 目的:打乱系统中的固定地址
- 包括:PE文件,映像地址,堆栈基址、堆地址、PEB和TEB(Thread Environment Block,线程环境块)地址。
- 当程序启动,将执行文件加载到内存时,操作系统通过内核模块提供的ASLR功能,在原来映像基址的基础上加上一个随机数作为新的映像基址。随机数的取值范围限定为1至254,并保证每个数值随机出现。
GS Stack Protection
- 在使用VC7.0、VS2005及后续版本编译时支持该选项。
- 在函数被调用时,在缓冲区和函数返回地址间增加一个32位的随机数security_cooie,在函数返回时,调用检查函数security_cookie的值是否有变化。
- security_cookie在进程启动时随机产生,并且它的原始存储地址因Windows操作系统的ASLR机制也是随机存放的,攻击者无法对security_cookie进行篡改。
DEP
- 数据执行保护DEP(data execute prevention)技术可以限制内存堆栈区的代码为不可执行状态,从而防范溢出后代码的执行。
- Windows操作系统中,默认情况下将包含执行代码和DLL文件的txt段,即代码段得到内存区域设置为可执行代码的内存区域,其它的内存区域不包含执行代码,不具有代码执行权限。
- WindowsXP及之前的操作系统没有对这些内存区域的代码执行进行限制。
- DEP分为软件DEP和硬件DEP。硬件DEP需要CPU支持,需要CPU在页表增加一个保护位NX(no execute),来控制页面是否可执行。现在的CPU一般都支持,现在的DEP一般都是硬件DEP。
SafeSEH
- SEH(structured exception handler)是Windows异常处理机制所采用的重要数据结构链表。程序设计者可以根据自身需要,定义程序发生各种异常时相应的处理函数,保存在SEH中。
- 通过精心构造,攻击者通过缓冲区溢出覆盖SEH中异常处理函数句柄,将其替换为指向恶意代码的地址,并触发相应的异常,从而使程序流程转向执行恶意代码。
- SafeSEH是保护Seh函数不被非法利用的技术。微软在编译器中加入了SafeSEH选项,采用该选项编译的程序将PE文件中所有合法的SEH异常处理函数的地址解析出来制成一张SEH函数表,放在PE文件的数据块中,用于异常处理时进行匹配检查。
- 函数表是加密的,读取时解密。
SEHOP(Structed Exception Handler Overwrite Protection)
- 检测:
- SEH结构都必须在栈上,最后一个SEH结构也必须在栈上
- 所有的SEH结构必须是4字节对齐的
- SEH结构中异常处理函数的句柄handle(即处理函数地址)必须不在栈上
- 最后一个SEH结构的handle必须是(ntdll!FinalExceptionHandler)函数等
- 检测:
漏洞利用技术
esp跳板
问题:
- 有些软件的漏洞存在于某些动态链接库中,进程运行时动态加载。此外,在使用类似ASLR技术的操作系统中,地址也会因为引入的随机数每次发生变化。
- 此时,需要让覆盖返回地址后 新写入的返回地址能够自动定位到shellcode起始地址。
解决:
- 利用esp寄存器的特性实现,使用jmp esp作为跳板。
- 在函数调用结束后,被调用函数的栈帧被释放,esp寄存器中断栈顶指针指向返回地址在内存高地址方向的相邻位置。可见,通过esp寄存器,可以准确定位返回地址所在的位置。
- 在内存中找到一个jmp esp(跳转到esp寄存器保存的地址),将这条指令的地址覆盖返回地址。
- 在返回地址相邻的高地址写入shellcode代码。
- move eax,exp 和 jmp eax等指令序列也可以实现。
heap spray
有些漏洞,不支持或者不能实现精确定位shellcode。且堆分配地址随机性比较大。
对喷洒是在shellcode前面加上大量的滑板指令(slide code),组成一个非常长的注入代码段。然后向系统申请大量内存,并且反复用这个注入代码段来填充。这样就使得内存空间被大量的注入代码所占据。攻击者再结合漏洞利用技术,只要使程序跳转到堆中被填充了注入代码的任何一个地址,程序指令就会顺着滑板指令最终执行到shellcode代码。
滑板指令一般是NOP(no-operation)0x90
随着一些新的攻击技术出现,滑板指令除了利用NOP指令填充外,也逐渐开始使用更多的类NOP指令,譬如0x0C,0x0D(回车、换行)等。
对喷洒用于针对浏览器漏洞的攻击较多,尤其是网页木马应用较多,这些漏洞利用程序通常会将EIP指向堆区的0x0C0C0C0C地址,然后使用Javascript脚本申请大量堆内存,在内存空间中填充大量包含0x和shellcode的注入代码。漏洞利用程序从低地址向高地址一直申请超过200MB的堆内存空间,由于200M对应的内存地址为0x0C800000,高于EIP指向的0x0C0C0C0C,因而申请的堆空间超过200MB时将覆盖0x0C0C0C0C,只要注入代码中的0x90能覆盖0x0C0C0C0C,就可以成功。
占用内存大,容易被察觉。
一般配合堆栈溢出攻击,不能用于主动攻击,也不能保证成功。
对于windows系统比较好的系统防范方法是开启DEP功能,即使被绕过,被利用的概率也会大大降低。
DEP绕过
基本思想:
- ROP(return-oriented programing)攻击者从已有的库或可执行文件中提取指令片段,构建恶意代码。ROP允许我们绕过DEP和ALSR,但不能绕开GS缓冲区溢出的检测防护技术。
- 借助已经存在的代码块(也叫配件),这些配件来自程序已经加载的模块。我们可以在已经加载的模块中找到一些以retn结尾的配件,把这些配件的地址布置在堆栈上,当控制EIP并返回的时候,程序就会跳去执行这些配件,而这些配件是在别的模块代码段,不受DEP影响。
- retn的操作是 pop eip,然后执行eip指向的指令。
- ROP通过ROP链(retn)实现有序的汇编指令执行
- ROP链由一个个ROP小配件组成
- ROP小配件由目的执行指令+return组成
未启用ASLR的小配件有很多