After solving the first crypto challenge of PoliCTF 2015, I moved onto the 100 point challenge.
The download contains a text file with base64-encoded data, which becomes a .gz archive. After decompressing, I obtained a text file with biblical text. Not my cup of tea, but I immediately saw that certain sentences were duplicated. I wrote a python script to count the occurences of lines:
...snip...
Ye shall not eat anything with the blood: neither shall ye use enchantments, nor practise augury.
21
And ye shall keep my statutes, and do them: I am Jehovah who sanctifieth you.
23
And if a man lie with a beast, he shall surely be put to death: and ye shall slay the beast.
26
And when he hath made an end of atoning for the holy place, and the tent of meeting, and the altar, he shall present the live goat:
47
A total of 30 distinct strings were found. I guessed these strings represented letters, so I extended the python script a bit and started puzzling:
12345678910111213141516171819202122
d={}withopen('text-file')asf:lines=f.readlines()forlinlines:iflind:d[l]+=1else:d[l]=1forwinsorted(d,key=d.get):printw,d[w]printlen(d)x=dict(zip(sorted(d,key=d.get,reverse=True),' etaoinsrhldcubkfgjmpqvwxyz012'))# transpose the strings to letters and print out the messageout=""forlinlines:out+=x[l]printout
I guessed that the most common string was a space, which indeed yielded word- and sentence-like output:
Slowly but surely, I translated all the letters, and the words slowly emerged (I love that!):
1234567
HEoonqTHISgHAooEldEISVERYEASYISljTIT1IjkTRYIldTnWRITEAonldkESSAdESnSTATISTIgSWIooHEopYnfWITHTHEoETTERmRExfElgIESbufTIjklnTREAooYdnncATTHATbn0THATjSElnfdHqAHAHqIjkIlonVEWITHSIkpoEonWERgASEmoAdSWITHnfTSpAgESAlcSTRAldESYkunoSbmoAdzouHTuddfVmSYlTuuxWun2HELLoqTHISCHALLENGEISVERYEASYISN'T IT? I'kTRYINGToWRITEALoNGkESSAGESoSTATISTICSWILLHELpYofWITHTHELETTERmRExfENCIESbufTI'k NoT REALLY Good AT THATb oj THAT'SENofGHqAHAHqI'k IN LoVE WITH SIkpLE LoWERCASE mLAGS WITHofT SpACES ANd STRANGE SYkuoLSb mLAGzLuHTuGGfVmSYNTuuxWuo2HELLOqTHISCHALLENGEISVERYEASYISN'T IT? I'MTRYINGTOWRITEALONGMESSAGESOSTATISTICSWILLHELPYOUWITHTHELETTERkRExUENCIESbuUTI'M NOT REALLY GOOd AT THATb Oj THAT'SENOUGHqAHAHqI'M IN LOVE WITH SIMPLE LOWERCASE kLAGS WITHOUT SPACES ANd STRANGE SYMuOLSb kLAGzLuHTuGGUVkSYNTuuxWuO2HELLO!THISCHALLENGEISVERYEASYISN'T IT? I'MTRYINGTOWRITEALONGMESSAGESOSTATISTICSWILLHELPYOUWITHTHELETTERFREQUENCIES,BUTI'M NOT REALLY GOOD AT THAT, OK THAT'SENOUGH!AHAH!I'M IN LOVE WITH SIMPLE LOWERCASE FLAGS WITHOUT SPACES AND STRANGE SYMBOLS, FLAG{LBHTBGGUVFSYNTBBQWBO}
The final translation dictionary was: x = dict(zip(sorted(d, key=d.get, reverse=True), " TESILAOHGNRYBWMUC'FP!,VQD{K?}"))
The challenge already said that the flag needed a bit more work. Indeed, flag{lbhtbgguvfsyntbbqwbo} was not accepted. What then? Bitvijays suggested that the flag was another “ciphertext”, so I thought of Caesar cipher. The easiest is rot13 and indeed, the flag was flag{yougotthisflagoodjob}.