您现在所在的位置:首页 >> 通知公告 >> 通知公告 >>
发布日期:2014年10月21日
缓冲区溢出漏洞浅析

1.认识漏洞

 

1.1.漏洞的定义

百度:漏洞是在硬件、软件、协议的具体实现或系统安全策略上存在的缺陷,从而可以使攻击者能够在未授权的情况下访问或破坏系统。

Wikipedia:计算机系统安全方面的缺陷,使得系统或其应用数据的保密性、完整性、可用性、访问控制、监测机制等面临威胁。

王院士、罗老师:从整体上,一切影响信息系统及其服务群体正常工作,使得其基本安全属性(机密性、完整性、可用性、真实性、可控性)不能得到保障的缺陷。

 

1.2.漏洞的分类

1.从漏洞类型上

       输入验证错误、边界条件错误、缓冲区溢出、访问验证错误、异常条件错误、环境错误、配置错误、竞争条件、设计错误……

2.从漏洞利用方式上

本地攻击、远程攻击、目标访问攻击

 

1.3.相关概念

1. 0day 漏洞、1day漏洞

1day漏洞是指那些已经公布的但厂商或用户因为安全意识、时间等多种原因还未及时修补的漏洞。(因此应当及时为操作系统、应用程序打补丁,防止遭受攻击)

 

2.  Exploit(EXP)

Exploit的英文本意为“利用”。在计算机安全术语中,这个词通常表示利用漏洞渗透到脆弱系统的过程(例如,使自己编写的代码越过具有漏洞的程序的限制,从而获得敏感信息、运行权限等)。

同时它也表示一种自动检测漏洞并能在大多数情况下通过运行代码来尝试使用漏洞的程序。(比如,针对一个拒绝服务的漏洞,exploit将尝试使目标崩溃,使其无法对外提供服务;针对一个远程可利用的缓冲区溢出漏洞,exploit将尝试溢出目标并执行任意代码。)

 

3.POC

Proof of concept(概念验证)是对某些想法的一个较短而不完整的实现,以证明其可行性,示范其原理。对于漏洞来说,它通常代表并没有充分利用这个漏洞的exploit。

 

4.Shellcode

广义上:一段可执行的二进制代码(也可以是填充数据)。例如:

“\xB8\x0B\x00\x00\x00\x83\xC0\x16\xC3”,它代表如下汇编指令:

mov eax,11

add eax,22

ret

狭义上:一段利用特定漏洞的二进制代码,从而实现权限提升、任意代码执行等功能。

 

5.Payload

Shellcode 中的有效载荷,包含漏洞利用成功后恶意程序所做的有害的或恶意的动作。(运行指定的程序、开启一个后门等)

 

2.溢出漏洞原理

 

2.1.缓冲区溢出概念

首先看下面这个例子,当在命令行中输入多个1并按下回车后,程序会停止工作。通过程序崩溃时显示的信息,我们可以知道程序发生了堆栈溢出,堆栈中的函数返回地址被覆盖,使得程序无法继续执行。

图1 缓存区溢出

     因此,当向一个已分配了确定存储空间的缓冲区内复制多于该缓冲区处理能力的数据时,将发生缓冲区溢出。缓冲区溢出分为静态缓冲区溢出与动态缓冲区溢出,对应于堆栈溢出和堆溢出(堆腐烂)。

 

2.2.堆栈溢出(地毯式轰炸)

堆栈:线程相关的,每一个线程都会有一个独立的堆栈,是FILO结构,从内存高地址向内存低地址增长。

堆栈溢出:堆栈溢出是在静态缓冲区中发生的溢出,当堆栈空间的缓冲区过载时,就发生堆栈溢出。发生堆栈溢出时,返回地址可能将被覆盖,程序执行流程将被改变。利用缓冲区漏洞,通过精妙的控制,就可以获取控制权执行任意代码。

 

2.3.堆溢出(狙击)

堆:由操作系统中的堆管理系统进行管理,堆区的内存按不同大小组织成块,以堆块为单位进行标识。堆块包括块首和块身两个部分,块首用来标识堆块自身的信息(大小、状态、向前向后指针等)。块身就是最终分配给用户使用的数据区。Windows下,空闲态的堆块由堆区起始位置的堆表进行索引,占用态的堆块被使用它的进程索引。

堆溢出:堆溢出是在动态缓冲区中发生的溢出,其溢出原理与堆栈溢出类似,但是由于堆与堆栈结构的差异,导致其利用方式不同,甚至需要很多条件都满足时,才可以利用一个堆溢出漏洞。

堆溢出的利用有多种形式,常见的一种形式是利用堆结构本身的特点,在堆溢出时溢出覆盖“下一个堆块”的块首,改写其向前向后指针(堆区中的所有堆块在线性内存空间中是连续的,即便其中某些堆块已经被分配了出去),因此当分配、释放、合并等操作发生时就可能会获得一次向任意内存地址写入任意数据的机会。如同标红的NODE堆块已经被溢出,其FLINK与BLINK已经被改写,当它从空闲堆块链表中出链时,就会发生一次任意地址空间的读写:NODE -> BLINK -> FLINK = NODE -> FLINK

 

图2 空闲堆链

 

2.4.整数溢出(先头部队)

整数:在计算机中,所有整数类型都用一个固定的位数表示,所以其表示范围有限,当值超出最大或最小值范围时,就会发生“回绕”。

整数溢出:当一个整数值大于或者小于其范围时,就会产生整数溢出。整数溢出漏洞常常是攻击者致使缓冲区溢出的起点。因为许多情况下,缓冲区溢出都与数字有关,因为内存分配量、字符串操作长度都是用整数表示的。

当使用可能会发生回绕的变量来分配内存、限制字符串操作范围、或作为指向缓冲区的指针时,可能会发生缓冲区溢出。下图为一个存在整数溢出漏洞的函数示例,当Len为一个负数时,将绕过长度判断,从而实现缓存区拷贝(memcpy的参数3是一个无符号数)。

 

图3 整数溢出隐患

 

3.堆栈溢出漏洞Exploit

 

3.1.堆栈与函数调用

1.堆栈

<– ESP

局部变量
EBP
返回地址
参数1
参数2
参数3
参数4

图4 堆栈

2. 函数调用约定

调用约定规定了进行一次函数调用所采用的参数传递,返回值处理以及清理调用堆栈的方式。是在编译时,由编译器根据编译选项决定的。Windows下有多种调用约定,分别是__cdecl、__stdcall、__fastcall、__thiscall、__clrcall、__vectorcall。在x86架构下,所有调用约定都使用eax传递返回值,64位返回值则使用edx:eax传递返回值。

__cdecl:C语言默认的函数调用约定,它可以处理可变参数。函数参数从右至左依次入栈,由函数调用者(父函数)清理堆栈。

__stdcall:Win32 API中最常见的函数调用约定,所有不可变参数的API调用都使用这个约定。函数参数从右至左依次入栈,由被调用函数(子函数)自行清理堆栈。

__fastcall:快速函数调用约定,第一个和第二个DWORD参数(或者尺寸更小的)通过ecx和edx传递,其他参数通过从右向左的顺序压栈,由被调用函数(子函数)自行清理堆栈。

__thiscall:C++成员函数的默认调用约定,函数参数从右向左依次入栈,ecx保存this指针,由被调用函数(子函数)自行清理堆栈。注意thiscall不是关键词,因此不能被程序员指定。

表1 Calling Conventions

Keyword

Stack cleanup

Parameter passing

__cdecl

Caller

Pushes parameters on the stack, in reverse order (right to left)

__stdcall

Callee

Pushes parameters on the stack, in reverse order (right to left)

__fastcall

Callee

Stored in registers, then pushed on stack

__thiscall

Callee

Pushed on stack; this pointer stored in ECX

__clrcall

n/a

Load parameters onto CLR expression stack in order (left to right).

__vectorcall

Callee

Stored in registers, then pushed on stack in reverse order (right to left)

 

3.2.堆栈溢出漏洞远程利用示例

堆栈溢出:通过前面对堆栈及函数调用的介绍,我们已经可以理解堆栈溢出的原理,通过溢出堆栈低地址处的局部变量可以修改高地址处的返回地址,从而修改程序流程。

示例请参考《0day安全:软件漏洞分析技术》

 

4.安全编程

 

4.1.使用Windows安全机制

在编译链接程序时,你可以通过IDE中的编译、链接选项为程序设置如下保护,通过它们可以极大的增强程序的安全性(注意:没有绝对的安全,目前已经有多种技术可以绕过它们)。

  •  Stack cookies (/GS)                                      缓冲区安全检查
  •  SafeSEH (/EHsc)                                          异常处理器保护
  •  Data Execution Prevention (DEP)    (/NXCOMPAT)           数据执行保护
  •  Address Space Layout Randomization (ASLR)                随机基址

 

4.2.使用安全语言

安全语言:可以自动执行运行时检查,以预防程序超出所分配内存边界的编程语言。具备两种特性:内存安全&类型安全。

不安全的语言:C、C++ …

相对安全的语言:Java、C# …

(Note:这些相对安全的编程语言或多或少“消除”了缓冲区溢出的可能性,但是它们提供的缓冲区溢出保护措施也是以相当高的代价换来的。)

 

4.3.安全编程意识

  •  严格把关所有输入(字符串、整数等数据);
  •  重视编译器警告,且不要为了编译通过,而更改警告等级;
  •  注意动态内存的管理(分配与释放等);
  •   ……