Category: Reverse Engineering
Flag: apoorvctf{Y0u_4ctually_brOught_Y0ur_owN_Firmw4re????!!!}
Challenge Description
Forge has made it so that only his trinket can open his workshop(forge) or so it would seem.
Analysis
The binary immediately looked like a custom validator/loader rather than a normal crackme, so I started with basic triage to understand what kind of target it was.
file ./forge./forge: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=57062ee66779984a22d90978e92257d531b4358c, for GNU/Linux 4.4.0, strippedSince it was stripped PIE, I checked symbols/functions through radare2 and found one big main plus helper routines at 0x1b60, 0x1b70, and 0x1c00, which turned out to be the fail path, SHA-256 helper, and AES-GCM helper.
r2 -A -q -c "afl" ./forge | rg "main|1b60|1b70|1c00|entry0"0x00001a60 1 37 entry0
0x000011e0 52 2123 main
0x00001b60 1 14 fcn.00001b60
0x00001b70 5 142 fcn.00001b70
0x00001c00 13 313 fcn.00001c00One important clue in the binary strings was a payload> prefix, and reversing showed the program decodes a runtime filename (payload>bin), reads that file, and executes it from an RWX mmap. That is where the challenge troll happens: the program behaves like only “Forge’s trinket firmware” can satisfy the checks, and if anything is wrong it just dies quietly.
strings ./forge | rg -i "payload>|apoorv|ctf|forge|trinket|workshop|correct|wrong|flag"payload>At that point, the practical route was to provide my own firmware blob (payload>bin) and make the parent process accept it. The core issue was that the parent performs post-child digest validation and the binary also cleanses sensitive buffers before exiting, so I patched those cleanse callsites to NOP in a local working copy (forge_noptrace2) to keep the useful state intact while solving.

I verified that the expected bytes at those offsets were indeed NOP in the patched file.
python -c "from pathlib import Path; p=Path('forge_noptrace2'); b=bytearray(p.read_bytes());
for off in (0x16ef,0x1701,0x170e):
print(hex(off), hex(b[off]))"0x16ef 0x90
0x1701 0x90
0x170e 0x90Then I built the custom payload file used by the loader:
python build_forge_payload.pyWrote payload>bin (50 bytes)Running the patched binary with that payload produced the flag directly on stdout.
./forge_noptrace2APOORVCTF{Y0u_4ctually_brOught_Y0ur_owN_Firmw4re????!!!}The challenge prefix is lowercase apoorvctf{...}, so the final normalized flag is:
apoorvctf{Y0u_4ctually_brOught_Y0ur_owN_Firmw4re????!!!}
Solution
# build_forge_payload.py
from keystone import Ks, KS_ARCH_X86, KS_MODE_64
insns = [
"cld",
"lea rsi, [rsp + 0xd78]",
"mov rdi, 0x400001bc",
"mov ecx, 0x38",
"rep movsb",
"lea rsi, [rsp + 0xd38]",
"mov rdi, 0x4000013c",
"mov ecx, 0x38",
"rep movsb",
"ret",
]
ks = Ks(KS_ARCH_X86, KS_MODE_64)
encoding, _ = ks.asm("\n".join(insns))
payload = b"\xf3\x0f\x1e\xfa" + bytes(encoding) # ENDBR64 + shellcode
with open("payload>bin", "wb") as f:
f.write(payload)
print(f"Wrote payload>bin ({len(payload)} bytes)")python build_forge_payload.py
./forge_noptrace2Wrote payload>bin (50 bytes)
APOORVCTF{Y0u_4ctually_brOught_Y0ur_owN_Firmw4re????!!!}