网络通信 频道

Heap/BSS 溢出机理分析

虽然基于Heap(堆)/BSS的溢出现在是相当普遍的,但并没有多少介绍它的资料。
  本文将帮你理解什么是Heap溢出,也介绍了几种常用的攻击方法,同时给出了一些可
  能的解决方案。阅读本文,您需要了解一些汇编,C语言以及堆栈溢出的基本知识。
  
  一.为什么Heap/BSS溢出很重要?
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  
   堆栈溢出的问题已经广为人知,越来越多的操作系统商家增加了不可执行堆栈的补
  丁,一些个人也提供了自己的补丁,象著名的Solar Designer提供的针对Linux的不可
  执行堆栈的kernel patch(目前已经推出了用于2.2.13内核的patch),也有一些人开发
  了一些编译器来防止堆栈溢出,象Crispin Cowan等开发的StackGuard等等。这些方法
  都一定程度上可以减少由堆栈溢出导致的安全问题,但是并却不能防止Heap/BSS的溢出。
  在大多数的操作系统中,Heap和BSS段都是可写可执行的。这就使得Heap/BSS的溢出成
  为可能。
  
  
  大部分的基于heap的溢出都是不依赖于系统和硬件结构的,这将在后面进一步介绍。
  
  二.一些概念
  ~~~~~~~~~~~
  
   一个可执行的文件(比如常见的ELF--Executable and Linking
  Format格式的可执行
  文件)通常包含多个段,比如:PLT(过程连接表),GOT(全局偏移表),init(包含在初始化
  时执行的指令),fini(包含程序终止时要执行的指令),以及ctors和dtors(包含一些全
  局构造指令和析构指令)
  
  所谓HEAP,就是由应用程序动态分配的内存区。在这里,"由应用程序"来分配是值得特别注
  意的,因为在一个好的操作系统中,大部分的内存区实际上是在内核一级被动态分配的,而
  Heap段则是由应用程序来分配的。它在编译的时候被初始化。
  BSS段包含未被初始化的数据,在程序运行的时候才被分配。在被写入数据前,它始终保持
  全零(至少从应用程序的角度看是这样的)
  
  在大部分的系统中,Heap段是向上增长的(向高址方向增长)。因此,当我们说"X在Y的
  下面"时,就是指"X的地址低于Y的地址"。
  
  
  注意:下面提到的"基于heap的溢出"既包含HEAP段的溢出,也包含BSS段的溢出。
  
  三.Heap/BSS溢出攻击
  
  
  在这一部分中我们将介绍几种不同的利用Heap/BSS溢出的方法。大部分的例子都是针对
  x86
  Unix系统的。做一些适当的改变,也可以用于DOS和Windows系统。我们也介绍了几种专
  门针对DOS/Windows的攻击方法。
  
  注意:
  
  在本文中,为了简单起见,我们使用了精确的偏移量。偏移量必须与实际的值相等,攻
  击程序才能工作。当然你也可以象通常的堆栈攻击方法那样,通过提供多个返回地址及插入
  空指令等方法以增加成功的机率。
  
   下面的这个例子是给那些不熟悉Heap溢出的人看的,我会做一些简单的解释:
  -----------------------------------------------------------------------------
   /* 演示在heap段(已初始化的数据)发生的动态缓冲区溢出*/
  
   #include
   #include
   #include
   #include
  
   #define BUFSIZE 16
   #define OVERSIZE 8 /* 我们将覆盖buf2的前OVERSIZE个字节 */
  
   int main()
   {
   u_long diff;
   char *buf1 = (char *)malloc(BUFSIZE), *buf2 = (char *)malloc(BUFSIZE);
  
   diff = (u_long)buf2 - (u_long)buf1;
   printf("buf1 = %p, buf2 = %p, diff = 0x%x (%d)bytes\n", buf1, buf2,
  diff, diff);
  
   memset(buf2, ''A'', BUFSIZE-1), buf2[BUFSIZE-1] = ''\0'';/*
  将buf2用''A''填充 */
  
   printf("before overflow: buf2 = %s\n", buf2);
   memset(buf1, ''B'', (u_int)(diff + OVERSIZE)); /*
  用diff+OVERSIZE个''B''填充buf1 */
   printf("after overflow: buf2 = %s\n", buf2);
  
   return 0;
   }
  -----------------------------------------------------------------------------
  当我们运行它后,得到下面的结果:
  [warning3@testserver basic]$ ./heap1 8
  buf1 = 0x8049858, buf2 = 0x8049870, diff = 0x18 (24)bytes
  before overflow: buf2 = AAAAAAAAAAAAAAA
  after overflow: buf2 = BBBBBBBBAAAAAAA
  
  我们看到buf2的前8个字节被覆盖了。这是因为往buf1中填写的数据超出了它的边界进入了
  buf2的范围。由于buf2的数据仍然在有效的heap区内,程序仍然可以正常结束。另外我们
  可以注意到,虽然buf1和buf2是相继分配的,但他们并不是紧挨着的,而是有8个字节的间
  距,这个间距可能随不同的系统环境而不同。
  
  buf1 间距buf2
  覆盖前:[xxxxxxxxxxxxxxxx][xxxxxxxx]  低址 -----------------------------------> 高址
  覆盖后:[BBBBBBBBBBBBBBBB][BBBBBBBB][BBBBBBBBAAAAAAA]
  
  注意:
  
  一个阻止heap溢出的可能的方法就是在heap段的所有变量之间放一个"canary"值(就象
   StackGuard中所做的那样),若这个值在执行中被改变,就认为发生了溢出。
  
  为了解释BSS段的溢出,我们来看下面这个例子:
  -----------------------------------------------------------------------------
  /* 演示在BSS段(未被初始化的数据)的静态缓冲区溢出 */
  
  #include
  #include
  #include
  #include
  #include
  
  #define ERROR -1
  #define BUFSIZE 16
  
  int main(int argc, char **argv)
  {
   u_long diff;
  
   int oversize;
   static char buf1[BUFSIZE], buf2[BUFSIZE];
  
   if (argc <= 1)
   {
   fprintf(stderr, "Usage: %s \n", argv[0]);
   fprintf(stderr, "[Will overflow static buffer by ]\n");
  
   exit(ERROR);
   }
  
   diff = (u_long)buf2 - (u_long)buf1;
  
   printf("buf1 = %p, buf2 = %p, diff = 0x%x (%d) bytes\n\n",
   buf1, buf2, diff, diff);
  
   memset(buf2, ''A'', BUFSIZE - 1), memset(buf1, ''B'', BUFSIZE - 1);
   buf1[BUFSIZE - 1] = ''\0'', buf2[BUFSIZE - 1] = ''\0'';
  
   printf("before overflow: buf1 = %s, buf2 = %s\n", buf1, buf2);
  
   oversize = diff + atoi(argv[1]);
   memset(buf1, ''B'', oversize);
  
   buf1[BUFSIZE - 1] = ''\0'', buf2[BUFSIZE - 1] = ''\0'';
   printf("after overflow: buf1 = %s, buf2 = %s\n\n", buf1, buf2);
  
   return 0;
  }
  -----------------------------------------------------------------------------
  当我们运行它后,得到下面的结果:
  [warning3@testserver basic]$ ./heap2 8
  buf1 = 0x8049874, buf2 = 0x8049884, diff = 0x10 (16) bytes
  
  before overflow: buf1 = BBBBBBBBBBBBBBB, buf2 = AAAAAAAAAAAAAAA
  after overflow: buf1 = BBBBBBBBBBBBBBB, buf2 = BBBBBBBBAAAAAAA
  
  和heap溢出类似,buf2的前8个字节也被覆盖了。我们也可以注意到,buf1和buf2是紧挨着
  的,这意味着我们可以不用猜测buf1和buf2之间的间距.
  
  buf1buf2
  覆盖前:[BBBBBBBBBBBBBBBB]  低址 ----------------------> 高址
  覆盖后:[BBBBBBBBBBBBBBBB][BBBBBBBBAAAAAAA]
  
  从上面两个简单的例子,我们可以应该已经了解Heap/BSS溢出的基本方式了。我们能用它
  来覆盖一个文件名,口令或者是保存的uid等等...
  下面这个例子演示了一个指针是如何被覆盖的:
  -----------------------------------------------------------------------------
   /* 演示在BSS段(未被初始化的数据)中的静态指针溢出 */
  
   #include
   #include
   #include

文章转载地址:http://www.cnpaf.net/Class/hack/06101110491730717240.html

0
相关文章