Nishcheta
Nishcheta
Platform: CUSTOM | Category: Hardware | Type: Challenge | Difficulty: Medium | OS: N/A | **Author: XESXOR Techniques: framebuffer_tracing, multi_tap_decoding, signal_forcing, verilog_instrumentation
Summary
Walkthrough of the CUSTOM Hardware Nishcheta. Task: a keypad-and-display hardware simulation hid the flag behind synthesized Verilog logic and SMS-style input. Solution: instrument the netlist, force one-cycle key commits, decode the winning mult
Short Challenge Summary
We were given peace_6ce0fda.tar.gz, containing a hardware-style keypad/display simulation. The goal was to understand how the 4x4 phone keypad fed the synthesized Verilog design and then recover the flag that occasionally appeared on the 128x64 display.
Recon / File Analysis
The useful files were:
peace.py— a cocotb harness for interacting with the device in simulationpeace.v— a large Yosys-generated Verilog netlistDockerfileanddocker-compose.yml— environment helpers
This immediately suggested that the intended path was not manual clicking alone. peace.py made simulation easy, while peace.v exposed the real behavior of the device, including the internal buffers and display writes.
The most important early observation was that several false leads existed around hidden selector logic. Instead of spending too much time guessing what hidden branch might unlock the flag, the reliable approach was to inspect what the Verilog actually wrote into the display/output pages after a committed input.
Input Model: Multi-Tap Keypad
The keypad logic uses old-phone multi-tap input. Repeated presses on one key select a specific symbol for that key's group, and the character is only useful once the tap is cleanly committed.
The character mapping needed for the solve was established from the simulation:
9999 -> y4 -> 422 -> a66 -> m
So the winning input groups were exactly:
text read-only • copy allowed9999 4 22 66
These four committed groups produced plaintext bytes corresponding to:
text read-only • copy allowedy4am
A key optimization during solving was to force the internal key-detect signals for a single cycle. That emulated one clean committed tap group, avoided timing noise from manual interaction, and made it much easier to test candidate inputs reproducibly.
Reverse-Engineering Findings in the Verilog
The heavy lifting came from instrumenting peace.v rather than treating it as a black box.
Important findings:
- the archive combined a user-facing cocotb harness with a much larger synthesized netlist
- the interesting state was not just the visible keypad behavior, but the internal display/output pages
- tracing the writes into the display buffer pages associated with
_M_00324exposed readable ASCII chunks after the correct committed input
This was the decisive pivot. Once the correct groups were entered, the relevant page data clearly contained pieces of the flag. At that point, the solve became an extraction and reconstruction problem rather than a search for more hidden branching logic.
How the Winning Input Was Identified
The successful workflow was:
- Use the cocotb setup to drive the keypad through controlled grouped taps.
- Force the internal key-detect path for one cycle so each group is committed cleanly.
- Validate the resulting plaintext bytes in the internal input buffer.
- Trace the Verilog writes into the
_M_00324display pages. - Reassemble the visible ASCII chunks.
The winning groups were:
text read-only • copy allowed9999, 4, 22, 66
These decode to y4am in the internal buffer. After that committed input, the display/output pages revealed the following visible chunks:
text read-only • copy allowedalfa{niw hta_or_p okoy_dec ided_in_ silicon_ 6fb9b724 9ff14993 1f8b959}
Concatenating the rows reconstructs the full flag.
Solution Script
The final verification script below records the solved tap groups and reconstructs the flag from the traced display rows:
python read-only • copy allowed#!/usr/bin/env python3 TAP_GROUPS = ["9999", "4", "22", "66"] TOKEN_TO_CHAR = { "9999": "y", "4": "4", "22": "a", "66": "m", } DISPLAY_ROWS = [ "alfa{niw", "hta_or_p", "okoy_dec", "ided_in_", "silicon_", "6fb9b724", "9ff14993", "1f8b959}", ] def decode_groups(groups): return "".join(TOKEN_TO_CHAR[g] for g in groups) def reconstruct_flag(rows): return "".join(rows) if __name__ == "__main__": plaintext = decode_groups(TAP_GROUPS) flag = reconstruct_flag(DISPLAY_ROWS) print(f"winning groups: {TAP_GROUPS}") print(f"decoded plaintext: {plaintext}") print(f"flag: {flag}") assert plaintext == "y4am" assert flag == "alfa{REDACTED}"
Final Flag
text read-only • copy allowedalfa{REDACTED}
Key Takeaways / Lessons
- Mastering framebuffer_tracing, multi_tap_decoding, signal_forcing, verilog_instrumentation was key to this challenge.
- Always check for common misconfigurations and verify assumptions.
Crafted by XESXOR Research • 2026-04-25 • CUSTOM • Nishcheta • Difficulty: Medium