Pollys Key

BearcatCTFby smothy

Polly's Key - Rev

Points: 428 | Flag: BCCTF{Th3_P05h_9oLly61Ot_p4rr0t} | Solved by: Smothy @ 0xN1umb

parrot pirate

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 \n0 to key due to a .chomp polyglot trick (Ruby calls method, Perl concatenates)
  • operator precedence makes $c.hex eval 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:

python
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 ✌️