Faultline
Challenge
Imported from local notes.md.
Solution
Original Notes
Faultline
Challenge Summary
- Given: a single static ELF64 binary named
faultlineand a minimal description:good luck!. - Goal: recover the local acceptance pair and extract the flag in the format
CIT{...}. - Constraints: the binary is statically linked, unstripped, and native execution on this host triggers
SIGILL, so analysis and execution needed an emulated runner.
Initial Recon / Triage
- File identification:
starting_files/faultlineis a statically linked, non-PIE, unstripped x86-64 ELF. - Runtime surface: running it showed subcommands
notes,score,trace,token,compare,nudge, andsubmit. - Key observation:
qemu-x86_64could execute the scoring paths cleanly even though direct native execution faulted with an illegal instruction. - Built-in notes exposed the intended model: stress uses adjacent symbols on a
2:3wheel, shear uses xor across distance two, grain folds positionsi,i+1, andi+3, and the target score is exactly2026.
Hypotheses & Approach
- Hypothesis 1: the profile alphabet is a 16-symbol ring and the score is a sum of penalties/rewards against hidden target traces.
- Hypothesis 2: because the binary is unstripped, the named routines
parseProfile,computeFaultlineScoreVisible, andbuildSurveyTokenVisiblewould be enough to reconstruct both the profile solver and token builder without relying on the hiddenvalidatebody. - Approach: extract the embedded target trace arrays from
.rodata, rebuild the scoring and token math in Python, solve the profile, then use the binary's own localsubmitcommand underqemu-x86_64to reveal the flag.
Execution Steps (Reproducible)
Stage 1
Commands:
cd /root/cit2026CTF/Faultline
file starting_files/faultline
checksec --file=starting_files/faultline
qemu-x86_64 ./starting_files/faultline
qemu-x86_64 ./starting_files/faultline notes
Results:
- The binary reported the usable command surface and the restricted alphabet
BCDFGHJKLMNPQRSTwith profile length12. - The notes command described the stress, shear, grain, load, and seal scoring families.
Stage 2
Commands:
cd /root/cit2026CTF/Faultline
python3 - <<'PY'
from pathlib import Path
p = Path('starting_files/faultline').read_bytes()
ro_vaddr = 0x579000
ro_off = 0x179000
for name, addr, count in [('OBS_STRESS', 0x579600, 11), ('OBS_SHEAR', 0x579640, 10), ('OBS_GRAIN', 0x579680, 9)]:
off = ro_off + (addr - ro_vaddr)
values = [int.from_bytes(p[off + i * 4: off + i * 4 + 4], 'little', signed=True) for i in range(count)]
print(name, values)
PY
Results:
- Extracted target arrays:
OBS_STRESS = [2, 5, 11, 10, 5, 1, 13, 4, 3, 3, 14]OBS_SHEAR = [5, 5, 15, 8, 5, 6, 7, 4, 5, 5]OBS_GRAIN = [3, 11, 3, 4, 14, 4, 5, 6, 1]
- Disassembly showed that stress alone induces a recurrence over a 16-element ring, so the search space collapses to just
16candidate seeds.
Stage 3
Commands:
cd /root/cit2026CTF/Faultline
python3 artifacts/solve_faultline.py
python3 artifacts/solve_faultline.py --submit
Results:
- The solver found a unique valid profile:
SDPKGTCMJRFL. - The reconstructed token builder produced
Z2L-2F5-BUBP. - Running the local submit path with that pair revealed the flag
CIT{12z4PXVTa3x3}.
Artifacts Produced
artifacts/solve_faultline.py: local reimplementation of the recovered scoring and token-generation logic.artifacts/solution.txt: recovered profile, token, score, and final flag.
Flag
CIT{12z4PXVTa3x3}