Category: Reverse Engineering
Flag: apoorvctf{1_h0pe_5BR_i5_w33kly_rele4as3}
Challenge Description
You thought you had won but then events started happening for which there is no apparent cause, it seems like the program can see the future
Analysis
This binary immediately felt like a troll continuation challenge: it is a stripped 64-bit PIE Rust ELF with anti-analysis-friendly hardening choices, and it prints status text (loaded flag, then printing flag.....) before crashing. The JoJo hint about King Crimson / Gold Experience Requiem was exactly on theme with what the program does: it manipulates the observable cause/effect path so obvious-looking flags are bait.

I started with basic triage to confirm architecture and protections, because for RE challenges that tells you whether static strings are trustworthy and whether runtime state is likely important.
file ./challenge
checksec --file=./challenge./challenge: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=19bcdb7a67f34142d84c9dffb1821d095f1ae531, for GNU/Linux 4.4.0, stripped
[*] '/home/rei/Downloads/challenge'
Arch: amd64-64-little
RELRO: No RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabledA direct strings pass did show an apoorvctf{...} token, but it was a decoy (invalid submission). The same command also showed the two status strings that kept appearing before crashes, which turned out to be important markers for where runtime generation finishes.
strings ./challenge | rg -i "apoorvctf|loaded flag|printing flag"apoorvctf{wh4t_1f_k1ng_cr1ms0n_requ13m3d??}
loaded flag
printing flag.....From there I followed the generation path in r2, specifically tracking writes to the tamper byte at global offset 0x54991. Those xrefs show multiple locations that force tamper on (mov byte [...], 1), plus one read site in the byte-generation helper. That explains why debugger runs were producing garbage bytes instead of a clean flag.
r2 -e scr.color=0 -e bin.relocs.apply=true -A -q -c "axt 0x54991" ./challengefcn.0000b583 0xb5c8 [DATA:r--] mov cl, byte [0x00054991]
fcn.0000b5e3 0xb6d4 [DATA:-w-] mov byte [0x00054991], 1
fcn.0000b5e3 0xb77f [DATA:-w-] mov byte [0x00054991], 1
fcn.0000b5e3 0xb9bd [DATA:-w-] mov byte [0x00054991], 1
fcn.0000b5e3 0xbd68 [DATA:-w-] mov byte [0x00054991], 1
fcn.0000b5e3 0xc368 [DATA:-w-] mov byte [0x00054991], 1So the solve pivot was to neutralize those tamper writes in a local copy (challenge_notamper) and then dump the generated runtime buffer at the exact point where the loop reaches 0x28 bytes. At that breakpoint, tamper is clearly 0x00 and the memory dump is fully printable.
gdb -q ./challenge_notamper \
-ex 'set pagination off' \
-ex 'set disable-randomization on' \
-ex 'handle SIGSEGV nostop noprint pass' \
-ex 'break *0x55555555f6af if *(unsigned long*)0x5555555a8988==0x28' \
-ex 'run' \
-ex 'x/bx 0x5555555a8991' \
-ex 'x/40cb *(unsigned char**)0x5555555a89a0' \
-ex 'quit'Thread 2 "challenge_notam" hit Breakpoint 1, 0x000055555555f6af in ?? ()
0x5555555a8991: 0x00
0x7ffff7fb9000: 97 'a' 112 'p' 111 'o' 111 'o' 114 'r' 118 'v' 99 'c' 116 't'
0x7ffff7fb9008: 102 'f' 123 '{' 49 '1' 95 '_' 104 'h' 48 '0' 112 'p' 101 'e'
0x7ffff7fb9010: 95 '_' 53 '5' 66 'B' 82 'R' 95 '_' 105 'i' 53 '5' 95 '_'
0x7ffff7fb9018: 119 'w' 51 '3' 51 '3' 107 'k' 108 'l' 121 'y' 95 '_' 114 'r'
0x7ffff7fb9020: 101 'e' 108 'l' 101 'e' 52 '4' 97 'a' 115 's' 51 '3' 125 '}'That byte stream directly spells the flag and matches the non-tampered generator output. Once that landed, the whole challenge clicked: the “future/cause” flavor text was describing intentional anti-debug state changes that alter what you observe unless you control execution context.

Solution
# 1) Make a local working copy and patch all tamper writes (mov byte [..0x54991],1) to NOPs
cp ./challenge ./challenge_notamper
chmod +w ./challenge_notamper
r2 -w -q -c "s 0xb6d4; wx 90909090909090; s 0xb77f; wx 90909090909090; s 0xb9bd; wx 90909090909090; s 0xbd68; wx 90909090909090; s 0xc368; wx 90909090909090; q" ./challenge_notamper
# 2) Break when generation length reaches 0x28 and dump the generated bytes
gdb -q ./challenge_notamper \
-ex 'set pagination off' \
-ex 'set disable-randomization on' \
-ex 'handle SIGSEGV nostop noprint pass' \
-ex 'break *0x55555555f6af if *(unsigned long*)0x5555555a8988==0x28' \
-ex 'run' \
-ex 'x/40cb *(unsigned char**)0x5555555a89a0' \
-ex 'quit'0x7ffff7fb9000: 97 'a' 112 'p' 111 'o' 111 'o' 114 'r' 118 'v' 99 'c' 116 't'
0x7ffff7fb9008: 102 'f' 123 '{' 49 '1' 95 '_' 104 'h' 48 '0' 112 'p' 101 'e'
0x7ffff7fb9010: 95 '_' 53 '5' 66 'B' 82 'R' 95 '_' 105 'i' 53 '5' 95 '_'
0x7ffff7fb9018: 119 'w' 51 '3' 51 '3' 107 'k' 108 'l' 121 'y' 95 '_' 114 'r'
0x7ffff7fb9020: 101 'e' 108 'l' 101 'e' 52 '4' 97 'a' 115 's' 51 '3' 125 '}'
apoorvctf{1_h0pe_5BR_i5_w33kly_rele4as3}