Catacombs
Challenge
Imported from local notes.md.
Solution
Original Notes
Catacombs
Challenge Summary
- Given: a single ELF64 executable named
catacombsand the terse promptgood luck. - Goal: recover the flag in the format
CIT{example_flag}. - Constraints: no source, no remote service, and the binary exposes an intentionally interactive trace harness.
Initial Recon / Triage
- Observations: the binary is an unstripped 64-bit executable with stack canary and NX, no PIE, and an RWX region used by a hidden validation stub.
- File identification: the program is an interactive state-machine puzzle built around syscall names and node transitions.
- Entry points:
help,status,hint,step <syscall>,script ...,submit,bait, andnotice-checkare the available runtime commands.
Hypotheses & Approach
- Hypothesis 1: the visible
CIT{3R2rA2J0PdFH}string might be a decoy because the binary also exposes a_ZL15FLAG_BAIT_LABELsymbol and a separatebaitcommand. - Hypothesis 2: the real solve path is a constrained 10-step syscall trace, with the final
submitpath validating a trace blob and a state token derived from the runtime.
Execution Steps (Reproducible)
Stage 1
Commands:
cd /root/cit2026CTF/Catacombs
file starting_files/catacombs
checksec --file=starting_files/catacombs
strings -a -n 6 starting_files/catacombs | rg 'CIT\{|hook|step|script|submit|bait|notice'
printf 'help\nstatus\nhint\nhint\nhint\n' | ./starting_files/catacombs
Results:
- The binary advertises itself as a
syscall-lit ossuary trace harness. - The hint text exposes the required syscall multiset and partial ordering constraints:
openat x2, read x2, mmap x1, ioctl x2, futex x1, clone x1, close x1, withclonebracketed by the twoopenatcalls andclose()only accepted when entered fromsysproxy. - Single-step probing shows the program walking named nodes such as
mouth,ossuary,sysproxy,sepulcher,ringbuf,cistern,lockdep, andsanctum.
Stage 2
Commands:
cd /root/cit2026CTF/Catacombs/artifacts
python3 solve_catacombs.py > solver_run.txt
cd /root/cit2026CTF/Catacombs
python3 - <<'PY'
from pathlib import Path
p = Path('starting_files/catacombs').read_bytes()
for addr in [0x5874ec, 0x5874fe, 0x58750f]:
off = 0x186000 + (addr - 0x586000)
print(hex(addr), p[off:off+128].split(b'\x00')[0])
PY
Results:
artifacts/solve_catacombs.pymodels the runtime transition graph, enforces the hint-derived syscall counts and ordering constraints, and generates candidate traces for local checking.- Static inspection of the adjacent read-only strings at the recovered addresses yields:
0x5874ec -> CIT{3R2rA2J0PdFH}0x5874fe -> ACCESS GRANTED:0x58750f -> ACCESS DENIED
- After manual submission, the live platform marks Catacombs as solved for the current account.
Artifacts Produced
artifacts/solve_catacombs.py: local solver/model for the syscall-trace search space.artifacts/candidate_search.txt: candidate sequences generated from the hint constraints.artifacts/solver_run.txt: captured solver output while enumerating and testing constrained candidates.artifacts/accepted_flag.txt: final recorded flag.
Flag
CIT{3R2rA2J0PdFH}