Files
rea/website/evidence/ctf-masked-squares.md
T
Ysopwnable a4d77f0ff0 docs(website): add a CTF showcase and FAQ (#1077)
* docs(website): add a worked CTF reverse-engineering showcase

* docs(website): add a dedicated FAQ page
2026-10-08 22:30:10 +08:00

7.5 KiB

DownUnderCTF masked-squares evidence

Fresh inspection and process captures on 8 October 2026, using the published REA 4.1.0 package with Ghidra 12.1.4 on Linux x64. The challenge is masked squares flag checker, by joseph, tagged easy in the official DownUnderCTF 2023 repository.

Target

  • Original handout: publish/ms_flag_checker, stripped Linux x86-64 ELF.
  • Size: 15,248 bytes.
  • SHA-256: dcd3bec4f608e11f3a12ce461100aaf5f621851ee63a1f870d7c90d4e3dd51ae.
  • Upstream revision: b48f83db2984bc7fa2ec7879a32d7efb1be8d333.
  • Git blob: af95d9e109b5b700b3285e7e9f3b341dc716d34a.
  • REA analysis-profile digest: 7cb87eeb3886b20e336087d762d35caac496279cf9839dcc5e741b973627c1ba.

The original challenge was downloaded from GitHub's contents API and decoded from its base64 representation. The decoded length matches the API's size; REA's target digest matches the downloaded bytes. The byte digest was checked again after analysis and execution. The page links to the original handout; the executable is not copied into the website.

The supplied AGENTS.md and website writing/design guidelines were followed in an isolated worktree. Raw results and local target paths stay in private scratch storage; the public page uses a shortened target name in its example request.

Discovery through REA

The following requests preceded reading the organizers' source, solution or flag metadata. Searching the challenge README established its name/difficulty, and the handout was analyzed directly.

Operation Observed result Evidence ID
search_strings, regex flag|correct|wrong|input Prompt at 0x102004; failure at 0x10201c; success at 0x102027 ev_375cf2c47d9cd666eeb5527e13863ce8d01ca86d11a6957fde79227aab1266c8
xrefs, address 0x102004 Reference at 0x10126d ev_31fb7e370e0ce8bccdf981167f49ad9196f67ac06c88eb2c1a19e3cb367e22f3
resolve_containing_procedure, address 0x10126d Function at 0x10124d, inclusive body through 0x10134f, 259 bytes ev_0627cfd459c1d9255ae1b03355987a93a0827e658a7ff40abcbf2cb1cfd8e613
analyze_function, procedure 0x10124d Input conversion, two helper calls, mask/target pointers, success/failure branch ev_4ba6d97358da28a23dc69cb6fde01cc929f28fc14f3a3661f4c65bbb6d86ec29
analyze_function, procedure 0x101189 Signed-byte run-length mask expansion ev_9e4f339895223a21cbf5bcd008ba8b5890e2e804675a11cb286f7af19246f766
analyze_function, procedure 0x101217 Add input integers at selected mask positions ev_d89261458e3faddd85110185bcd97f0f3c87e0992edc64cb3b2a1ed44846c3e9
read_bytes, address 0x104060, length 144 Target numbers followed by padding and the start of the mask table ev_f8a3529dd5d583b06177be5ce76ca6de36f3242ac90ab30f9a3a96159ff84e53
read_bytes, address 0x1040e0, length 624 All 26 mask records, 24 bytes each ev_944d9664d6167bf46c72b9fce662e573ea4e017c5a8ebbc305b1d943a1623301

The first 104 bytes of the target read hold the 26 little-endian integers. The displayed read_bytes requests preserve the actual lengths; the page selects the seventh entries from those complete results.

Interpretation

CMP EDI,0x24 terminates the input conversion at 36 characters. Its inner loop groups six integer cells per row. The checking loop walks from 0x1040e0 to 0x104350 in 24-byte steps: 0x270 / 0x18 = 26 masks.

In the mask helper, MOVSX loads each byte with its sign. Positive runs write ones, negative runs write zeros, and a zero terminates a record. All extracted records decode to exactly 36 cells. In the summing helper, a zero mask cell skips the addition; a nonzero cell adds the corresponding input integer. The caller compares the returned sum against a stored target.

The seventh record starts at 0x104170 and begins eb 01 f2 00, meaning -21, +1, -14, stop. It selects only zero-based input position 21. Its target at 0x104078 is 37 00 00 00, decimal 55, the code for 7. The SVG is calculated from this record, with only cell 21 highlighted.

The first mask selects 16 cells and its target is 1441. In the recovered flag, changing position 8 from z (122) to y (121) changes that sum to 1440.

The website's C is an analyst-written summary of the sum helper and caller, with a flattened 36-cell loop and descriptive names. Its selected assembly is transcribed from the returned dossiers. The mask/target addresses are analysis-image addresses; the executable's PIE runtime load address can differ.

Solver and source cross-check

The downloadable Python script embeds the REA-extracted masks and targets, expands their run lengths and gives the 26 sums to Z3. It asks for 36 character codes using a conventional DUCTF{...} prefix/suffix and an alphabet of letters, digits, underscores and braces. Those format restrictions are analyst assumptions, distinguished from the observed binary checks in the script and page caption.

The solver returned DUCTF{ezzzpzzz_07bcda7bfe81faf43caa} and all 26 extracted equations match that value. A separate second-model search reached its timeout; the completed verification concerns the returned candidate.

After obtaining and executing the candidate, the organizers' source confirmed the decoding/summing interpretation, all masks and all targets. The challenge metadata records the same flag.

Original-program verification

Two capture_process_scenario requests launched the original handout in its scratch directory, supplied one line of input and retained terminal output.

Input Output Exit Evidence ID
DUCTF{ezzzpzzz_07bcda7bfe81faf43caa} Correct! 0 ev_99f724502d14c798959f510d274f3de0f554a27b5df5dd6cb322acfa3e90a452
DUCTF{ezyzpzzz_07bcda7bfe81faf43caa} Incorrect! 255 ev_2d164f7f4c7eb139e5df8be21c1e52dc34f0ad238aea384e407a2bd6277024cb

Both captures completed without truncation. REA verified its owned process-group cleanup and removed the capture temporary roots. Filesystem observation paths were not requested. The native analysis session was closed afterward.

The challenge's original code and metadata are credited to DownUnderCTF/joseph. The page, readable C summary, equation figure and Python solver are new work based on this inspection.