Vulnerability Overview
A password verification function implemented entirely in client-side JavaScript compared user input against the flag in fragments. Despite the scrambled order of substring checks, all components were visible in the public source code.
Exploitation
Viewing page source revealed a verify() function that split the expected password into 8 four-character chunks and compared them in order:
split = 4;
checkpass.substring(0, split) == 'pico' // 0
checkpass.substring(split, split*2) == 'CTF{' // 1
checkpass.substring(split*2, split*3) == 'no_c' // 2
checkpass.substring(split*3, split*4) == 'lien' // 3
checkpass.substring(split*4, split*5) == 'ts_p' // 4
checkpass.substring(split*5, split*6) == 'lz_2' // 5
checkpass.substring(split*6, split*7) == 'eb02' // 6
checkpass.substring(split*7, split*8) == 'b45}' // 7Reading them in order: picoCTF{no_clients_plz_2eb02b45}.
Key Insight: Splitting a secret into fragments in JavaScript does not obscure it — every fragment is visible in source. No amount of scrambling, encoding, or variable renaming in client-side code provides meaningful protection because the user controls and can read the execution environment. Verification must happen server-side.