401 words
2 minutes
SCSC2026 Final - meng AI - Reverse Engineering Writeup

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
SCSC2026 Final - meng AI - Reverse Engineering Writeup
https://blog.rei.my.id/posts/156/scsc2026-final-meng-ai-reverse-engineering-writeup/
Author
Reidho Satria
Published at
2026-05-16
License
CC BY-NC-SA 4.0