Category: Reverse Engineering
Flag: SCSC26{ai_g3n3r4tr3d_r3v3rsing_ch4ll_mu5tb_easy_r1ght}
Description: good luck
The file was a stripped static x86-64 ELF. checksec also showed no PIE, so addresses from disassembly could be used directly.
file '/home/rei/Downloads/SCSC2026Final/challenge'
/home/rei/Downloads/SCSC2026Final/challenge: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, BuildID[sha1]=8d422e6806a5d87a895358f01f3f2d639ab73ff0, for GNU/Linux 3.2.0, stripped
checksec --file='/home/rei/Downloads/SCSC2026Final/challenge'
[*] '/home/rei/Downloads/SCSC2026Final/challenge'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
Strings gave the program flow. It asks for a passphrase, rejects bad input, and prints Correct! Flag: %s on success.
strings '/home/rei/Downloads/SCSC2026Final/challenge' | rg -i "flag|ctf|scsc|\{[^}]+\}|key|secret|password|BEGIN|correct|wrong|input|luck" --max-columns=200 || true
Find the secret passphrase and get the hidden flag.
Wrong key, try again.
Correct! Flag: %s
A quick run with dummy input confirmed the passphrase gate.
printf 'AAAA\n' | timeout 3 '/home/rei/Downloads/SCSC2026Final/challenge' || true
== Intermediate Reverse Engineering Challenge ==
Find the secret passphrase and get the hidden flag.
Enter passphrase: Wrong key, try again.
The interesting validator was at 0x401970. It checks a 24-byte input, transforms each byte, updates a 32-bit state, compares the low byte of rol32(state, 5) against the table at 0x47cb40, and finally requires the state before that last rotate to equal 0x262bd9e5. The table bytes were:
f6 c5 bb 5f 24 91 31 34 9d a8 05 b0 2a 5d a7 48 04 88 07 76 f6 ce c4 a4
That made the check small enough for bit-vector solving. This script models the byte operations and asks Z3 for printable input.
from pathlib import Path
from z3 import *
b = Path('/home/rei/Downloads/SCSC2026Final/challenge').read_bytes()
base = 0x400000
exp = list(b[0x47cb40 - base:0x47cb40 - base + 24])
cs = [BitVec(f'c{i}', 8) for i in range(24)]
edi = BitVecVal(0xb16b00b5, 32)
s = Solver()
for c in cs:
s.add(c >= 0x20, c <= 0x7e)
for i, c in enumerate(cs):
r8 = (7 + 3 * i) & 0xffffffff
edx = ZeroExt(24, c ^ BitVecVal(r8 & 0xff, 8)) + BitVecVal(0x4d, 32)
edx = edx & BitVecVal(0xff, 32)
al = RotateLeft(c, 2) ^ BitVecVal(0xa5, 8)
eax = ZeroExt(24, al)
eax = (eax << (((i + 1) & 3) * 8)) | (edx << ((i & 3) * 8))
eax = eax ^ edi
edi = RotateLeft(eax, 5)
s.add(Extract(7, 0, edi) == exp[i])
s.add(eax == 0x262bd9e5)
s.check()
m = s.model()
print(bytes([m[c].as_long() for c in cs]))
b'BUr7z2s}tX7q/mi4ll3ng3!!'
Feeding that passphrase to the binary printed the flag body. The challenge prefix wrapped it as scsc26{...}.
printf 'BUr7z2s}tX7q/mi4ll3ng3!!\n' | timeout 3 '/home/rei/Downloads/SCSC2026Final/challenge'
== Intermediate Reverse Engineering Challenge ==
Find the secret passphrase and get the hidden flag.
Enter passphrase: Correct! Flag: ai_g3n3r4tr3d_r3v3rsing_ch4ll_mu5tb_easy_r1ght