Polly's Key - Rev
Points: 428 | Flag: BCCTF{Th3_P05h_9oLly61Ot_p4rr0t} | Solved by: Smothy @ 0xN1umb

what we got
a single file pollys_key - no binary, turns out its a polyglot that runs as both Ruby AND Perl simultaneously. the "pirate" speaks Perl, the "parrot" speaks Ruby. both gotta approve your key.
So ye wants t' get yer hands on me treasure? Then ye'd better 'ave a key!
SQUAWK! POLLY HAS TREASURE SQUAWK! GIVE POLLY KEY SQUAWK!
the solve
the file uses =begin/=end (Ruby multi-line comments) and =cut (Perl POD) to hide code from each language. traced through both execution paths:
Ruby side:
- key must be 50 unique printable chars (33-126, no caret
^) - each char transformed:
(char - 16) % 257 - every transformed value must be a primitive root mod 257
- 257 is prime, 256 = 2^8, so primitive root =
pow(g, 128, 257) != 1
turns out exactly 50 chars in range satisfy this. character set is locked.
Perl side:
- appends
\n0to key due to a.chomppolyglot trick (Ruby calls method, Perl concatenates) - operator precedence makes
$c.hexeval as($_ - $c) . hex($_)- completely different transform lmao - runs insertion sort on transformed array, checks exact swap counts against hardcoded
sArray
the sArray swap counts are basically an inversion table - you can undo the insertion sort to recover the original ordering:
sorted_arr = sorted(all_52_transformed_values)
original = [None] * 52
for i in range(50, -1, -1):
pos = (i + 1) - sArray[i]
original[i + 1] = sorted_arr.pop(pos)
original[0] = sorted_arr[0]7 duplicate pairs in the perl transform get resolved by ordering constraints from both languages (e.g. Ruby says c before F, Perl says ` before 8).
map everything back to original chars, MD5 the 50-char key, XOR with encTreasure array, get flag.
flag
BCCTF{Th3_P05h_9oLly61Ot_p4rr0t}
The Posh Polyglot Parrot. ngl this was clean af
smothy out ✌️