Skip to main content

Catacombs

Challenge

Imported from local notes.md.

Solution

Original Notes

Catacombs

Challenge Summary

  • Given: a single ELF64 executable named catacombs and the terse prompt good 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, and notice-check are 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_LABEL symbol and a separate bait command.
  • Hypothesis 2: the real solve path is a constrained 10-step syscall trace, with the final submit path 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, with clone bracketed by the two openat calls and close() only accepted when entered from sysproxy.
  • Single-step probing shows the program walking named nodes such as mouth, ossuary, sysproxy, sepulcher, ringbuf, cistern, lockdep, and sanctum.

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.py models 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}