3 开发shellcode的艺术
3.1 shellcode概述
shellcode这个专用术语通称缓冲区溢出攻击中植入进程的代码。shellcode往往需要用汇编语言编写,并转换成二进制机器码,其内容和长度经常还会受到很多苛刻限制,故开发和调试的难度很高。
植入代码之前需要做大量的调试工作,
这个代码植入的过程就是漏洞利用,也就是exploit。
exploit一般以一段代码的形式出现,用于生成攻击性的网络数据包或者其他形式的攻击性输入。exploit的核心是淹没返回地址,劫持进程的控制权,之后跳转去执行shellcode。
3.2 定位shellcode
在实际的漏洞利用过程中,由于动态链接库的装入和卸载等原因,Windows进程的函数栈帧有可能产生移位,即shellcode在内存中的地址是会动态变化的。将返回地址简单地覆盖成一个定值的做法往往不能让exploit奏效。必须想出一种方法在程序运行时动态定位栈中的shellcode。
由于ESP寄存器在函数返回后不被溢出数据干扰,且始终指向返回地址之后的位置,可以
1)用内存中任意一个jmp esp 指令的地址覆盖函数返回地址,而不是原来用手工查出的shellcode起始地址直接覆盖;
2)函数返回后被重定向去执行内存中的这条jmp esp指令,而不是直接开始执行shellcode。
3)由于esp在函数返回时仍指向栈区(函数返回地址之后),jmp esp 指令被执行后,处理器会到栈区函数返回地址之后的地方取指令执行;
4)重新布置shellcode。在淹没函数返回地址后,继续淹没一片栈空间。将缓冲区前边一段地方用任意数据填充,把shellcode恰好摆放在函数返回地址之后。这样,jmp esp 指令执行过后会恰好跳进shellcode。
5 堆溢出利用
5.3 堆溢出利用 DWORD SHOOT
5.3.1 链表拆卸中的问题堆管理系统的三类操作:堆块分配、堆块释放和堆块合并归根结底都是对链表的修改。分配就是将堆块从空表中卸下;释放是把堆块链入空表;合并,把若干个堆块先从空表中卸下,修改块首信息,之后把更新后的新块链入空表。
如果我们能伪造链表结点的指针,在卸下和链入的过程中就有可能获得一次读写内存的机会。
堆溢出利用的精髓就是用精心构造的数据去溢出下一个堆块的块首,改写块首中的前向指针flink和后向指针blink,然后在分配、释放、合并等操作发生时伺机获得一次向内存任意地址写入任意数据的机会。
不但可以控制射击的目标(任意地址),还可以选用适当的子弹(4字节恶意数据)。
点射目标:栈帧中的函数返回地址;栈帧中的SEH句柄;重要函数调用地址;
子弹:shellcode起始地址
10 栈中的守护天使:GS
10.1 GS 安全编译选项的保护原理
针对缓冲区溢出时覆盖函数返回地址这一特征,微软在编译程序时使用了安全编译选项-GS。在VS2003(VS 7.0)及以后版本的Visual Studio中默认启用了这个编译选项。
VS2008(VS 9.0)中,可以通过菜单中的Project-project Properties-Configuration Properties-C/C++-Code Generation-Buffer Security Check中对GS编译选项进行设置。
.在所有函数调用发生时,向栈帧内压入一个额外的随机DWORD,
.Security Cookie位于EBP之前,系统还将在.data的内存区域中存放一个Security Cookie的副本,
.当栈中发生溢出时,Security Cookie将被首先淹没,之后才是EBP和返回地址,
.在函数返回之前,系统将执行一个额外的安全验证操作,被称做Security check,
在VS 2005 SP1起引入了一个新的安全标识:
#pragma strict_gs_check
通过添加#pragma strict_gs_check(on)可以对任意类型的函数添加Security Cookie。
Security Cookie 产生的细节:
.系统以.data节的第一个双字作为Cookie的种子,
.在栈帧初始化以后系统用ESP异或种子,作为当前函数的Cookie,
在VS 2005 及后续版本还使用了变量重排技术,在编译时根据局部变量的类型对变量在栈帧中的位置进行调整,
12 数据与程序的分水岭:DEP
12.1 DEP机制的保护原理
DEP,Data Execution Prevention,数据执行保护;溢出攻击的根源在于现代计算机对数据和代码没有明确区分这一先天缺陷,
DEP基本原理:将数据所在内存页标识为不可执行,当程序溢出成功转入shellcode时,程序会尝试在数据页面上执行指令,此时CPU就会抛出异常,而不是去执行恶意指令。
软件DEP其实就是SafeSEH;
硬件DEP才是真正意义的DEP,AMD和Intel都为此做了设计,
12.3 利用Ret2Libc挑战DEP
Ret2libc,Return to libc;
三种相对比较有效的绕过DEP的exploit方法:
1)通过跳转到ZwSetInformationProcess函数将DEP关闭后再转入shellcode执行;
2)通过跳转到VirtualProtect函数来将shellcode所在内存页设置为可执行状态,然后再转入shellcode执行;
3)通过跳转到VirtualAlloc函数开辟一段具有执行权限的内存空间,然后将shellcode复制到这段内存中执行;
一个进程的DEP设置标识保存在KPROCESS结构中的_KEXECUTE_OPTIONS上,
如果一个进程的Permanent位没有设置,当它加载DLL时,系统就会对这个DLL进行DEP兼容性检查,当存在兼容性问题时进程的DEP就会被关闭。
用OllyDbg加载调试程序。在0x7C92E257,即MOV EAX,1后边的RETN指令处暂停程序。观察堆栈可以看到此时ESP指向test函数返回地址的下方,而这个ESP指向的内存空间存放的值将是RETN指令要跳到的地址。
所以我们需要在这个位置放上0x7C93CD24以便让程序转入关闭DEP流程,我们为shellcode添加4个字节,并放置0x7C93CD24,