With more time spent at home due to COVID-19 restrictions, I worked on a MI5 cryptography challenge called Can You Solve This Puzzle. The premise is that simply given an image (below), can we identify the hidden message?
With image-based challenges, the solution usually revolves around finding the right steganography encryption technique(s) like the LSB obfuscation. To get some hint, I poked into its exif metadata for some reconnaissance.
~$ exiftool puzzle.png 2>&1 | tee puzzle.exif ExifTool Version Number : 10.80 File Name : puzzle.png Directory : mi5 File Size : 1497 bytes ... Pixels Per Unit Y : 3779 Pixel Units : meters Comment : As I read, numbers I see. 'Twould be a shame not to count this art among the great texts of our time Image Size : 92x163 Megapixels : 0.015
Which was very lucky because
Comment gives us a clue:
As I read, numbers I see. 'Twould be a shame not to count this art among the great texts of our time
Sadly I hit a dead end for a while as I looked into its color spectrum, pixel distributions, negatives, decoder scans, etc. Having already tried the common decoding techniques to no avail, I decided to change my strategy and start observing the physical characteristics of the puzzle itself.
Striped patterns stretching from top to bottom, left to right. This gave me an idea that it was somewhat boolean.
No (pixel) row is ever populated with the same color. This was the strangest characteristic. Every row there is a switch between two colors. If no row is ever completely filled with a consistent single color, then does this mean that there is some sort of limit to the length of the contiguous pattern?
Only “two” color variations. This puzzle had either pink or blue colors.
I re-visited the clue and read it more carefully; and I noticed that the puzzle authors had used some strange choice of words: numbers, count, and text. Piecing together the observations from earlier, I realized they wanted us to count the contiguous colored pixels and, very likely, that will return some 8-bit ASCII characters.
itertools to quickly offload some implementation logics, but the idea is very simple: load the puzzle into memory and parse out its pixels, remove alpha channel since we only care about RGB, calculate the “median color” so that our solution can be more flexible against gradients, group the colors into counts, and finally decode.
And once we run the decoder against the image, we get the following message.
Congratulations, you solved the puzzle! Why dont you apply to join our team? mi5.gov.uk/careers