1.漏洞利用与Exploit
漏洞从发现到产生实际危害的整个过程可分为漏洞挖掘、漏洞分析、漏洞利用三个阶段。
无论通过哪种形式进行攻击,其都有一个共同特点,即通过触发漏洞来隐蔽地执行恶意代码。而用来触发漏洞并完成恶意
操作的这个程序则通常被称为Exploit。
Exploit结构
一个经典的比喻就是把漏洞利用比作导弹发射过程:
Exploit、Payload和Shellcode可分别对应于导弹发射装置、导弹和弹头: Exploit 类似导弹发射装置,针对目标发射出导弹(Payload),导弹到达目标之后,释放实际危害的弹头(类似Shellcode)爆炸;导弹除弹头之外的其余部分用来实现对目标的定位追踪,对弹头的引爆等功能,在漏洞利用中,与之对应的则是Payload的非Shelleode部分。
漏洞利用的具体技术
- (1)修改内存变量
- (2)修改代码逻辑
- (3)修改函数返回地址
- (4)修改函数指针
- (5)攻击异常处理机制
- (6)修改P. E. B中线程同步函数的人口地址
对于缓冲区溢出这类漏洞,其漏洞利用的整个过程可归纳为三个步骤:
- ①定位溢出点;
- ②编写shellcode;
- ③修改/覆盖溢出点
使执行流程能够跳转到shellcode所在的内存起始地址。
关于最简单的堆栈溢出点的定位,基本方法有两种:
1.一种是通过不断修改输入字符串长度,通过OLLYDBG等调试器或者系统错误提示来直接分析读出溢出点;
2.另一种技巧是通过输人字符串有规律的循环来计算出溢出点的位置。
2.Shellcode 的开发
Shellcode作为一段最终将 直接运行在受害者主机的目标进程中的代码,它必须具备几个条件:
-
1.首先Shellcode通常是一段机器码,植人目标进程后CPU可以直接运行;
-
2.其次这段机器码必须具备代码重定位和API自搜索功能,不严重依赖于系统或进程;
-
3.最后,为了增强这段代码的通用性,并减小对缓冲区大小的依赖,必要时需对shellcode进行编码和压缩。
Shellcode的编写语言
从本质上说,Shellcode 就是一段机器码,因此Shellcode的编写可以使用任何编程语言,最终只需从编译后的二进制代码中提取出来即可。目前编写Shellcode时,常用的两种语言是汇编语言和C语言。
地址重定位技术
Shellcode最终是要植人到目标进程中去执行,而Shellcode无法事先知道其需要访问的数据(变量或常量等)在进程中内存空间内的地址,因此Shellcode需要通过相对地址偏移来实现对常量或变量的访问。
对于Shellcode中相对地址偏移的典型计算方法之一,示例如下:
call Next
Next:
pop EBP
- 对于call指令,它所实现的跳转是按照相对于call指令的下一条指令的偏移进行计算跳转距离的,即call实现的是相对偏移跳转,而非jmp addr_ 1 这类的绝对地址跳转;
- 其次,在call语句执行过程中,会将call指令的下一条指令的地址人栈,即保存返回地址,所以此时栈顶为Next对应的真实地址;然后,执行popebp,出栈将Next的绝对地址保存到寄存器ebp;
- 最后,当需要访问某个地址addr_ 2 时,通过计算相对于Next的偏移,再加上Next的 绝对地址即为addr_ 2的真实地址。
API函数自搜索技术
在编写Shellcode时为了实现某些功能,不可避免地要调用系统API函数,如CreatePro-cess, socket 等,这些函数的入口地址都位于系统的动态链接库中,如kernel32. dll、User32. dll等。
不同系统的dll加载的基地址不同,不同版本的ddl也可能存在差别,导致Shellcode无法工作。
因此,在实际中为了编写出通用的Shellcode, Shellcode 自身必须具备动态自动搜索所需API函数地址的能力,即API函数自搜索技术。
3.代码实现API函数自搜索技术
获取Kernel32 代码实现如下:
// Get kermel32. dll base addr
mov eax, fs: 0x30 // PEB
mov eax,[ eax+0xOc ] // PROCESS_ MODULE_ .INFO
mov esi, [ eax+0x1c] // InInitOrder. flink
lodsd // eax = InInitOrder. blink
mov ebp, [ eax+8] // ebp = kernel32. dll base address
在进行函数查找时,为了节省空间,一般不直接用函数名进行对比,而是对函数名进行处理后计算的一个值value,且value = f(API_ ,name)。
- 计算过程(f需要满足的条件是同一个dll中不同函数的value值不相同。
Shellcode在查找时,则用计算过程f依次求得API名称的value 值,并与所要查找的API进行比较。
下面是一个简单的计算API名称哈希的函数。通过控制HASH__KEY的值基本上可以保证同一dll中不同API的value各不相同。
获取函数地址 代码实现如下:
// Get function hash
static DWORD_ stdcall GetHash ( char *c )
{
DWORD h = 0;
while ( *c )
{
_ asm ror h, HASH_ KEY //HASH_ _KEY 为密钥
h+=*c++;
}
returm( h );
}
在前面的基础上,下 面这段代码实现了获取kernel32中LoadLibraryA和CetProddress地址的功能:
//假设此时ebp中保存了kernel32. dll的基地址
//此时,esi 指向的DWORD类型的数组中保存了用上述哈希计算方法求的API名称哈希值
//在这里分别是LoadLibraryA和GetProcAddress的哈希值,结果保存在edi指向的数组中;
push 2 // functions need to retrieve in kernel32
pop ecx
GetF uncInKernel32 :
call GetProcAddress_ fun
loop GetFuncInKernel32
...
...
GetProcAddress_ fun:
push ecx
push
mov esi, [ ebp+0x3C] // e_ _lfanew
mov esi, [ esi+ebp+0x78 ] // ExportDirectory RVA
add esi, ebp // rva2va
push esi
mov esi, [ esi+0x20 ] // AddressOfNames RVA
add esi, ebp // rva2va
xor ecx, ecx
dec ecx
find_start:
inc ecx
lodsd
add eax, ebp
xor ebx, ebx
hash_loop:
movsx edx, byte ptr [ eax ]
cmp dl, dh
jz short find_ _addr
ror ebx, HASH_ KEY // hash key
add ebx, edx
inc eax
jmp short hash_ loop
find_addr:
cmp ebx, [ edi] // compare to hash
jnz short find_ start
pop esi // ExportDirectory
mov ebx, [ esi+0x24] // AddressOfNameOrdinals RVA
按照上面的方法,即可获得kernel32. dll中的两个丽数LoadLibrary( )和GetProcAddress( )的地址。
接下来可以利用这两个API来获得任何API函数的地址:通过LoadLibrary 加载API所在的动态链接库,然后调用GetProcAddress根据API名来获取函数地址。
Shellcode 典型功能
在漏洞利用中,Shellcode 是- .段能够在目标主机上执行的代码,从本质上说,只要Shelleode运行得足够稳定,则和一般程序没有太大区别,因此其实现的功能也可以很丰富。
但一般而言,从漏洞利用的角度来看,Shellcode的典型功能大致上包括四种:
- (1)正向连接 :正向连接类的Shellcode在目标主机运行后再打开-一个监听端口,等待攻击者主动连接,因此在配置的时候只需要填入目标主机的IP和目标程序的端口号即可。
- (2)反向连接 :为了绕过防火墙,Shellcode采取目标主机反向连接攻击主机的方式,配置的时候需要填入目标主机和攻击主机的IP和端口号。
前两种方式建立连接的目的是为了通过获得的Shell来进行进一步的攻击。
- (3)下载程序并执行: 这一类型的Shellcode运行后会自动到指定的URL去下载一个指定的文件(通常为exe程序)并运行,这类Shellcode在网页挂马类漏洞的漏洞利用中相当广泛。
- (4)生成可执行文件并运行: 攻击者直接将可执行文件嵌人到Shellcode中,Shelleode 的目的就是将其释放出来并运行。这种Shellcode在文档捆绑类漏洞的漏洞利用中非常普遍。
后两种方式植人的通常都是病毒、木马或后门。
上述四种只是最基本的几类Shellcode功能。