Okay, it is in the title, but it’s worth saying again. IF YOU HAVE NOT SOLVED THIS AND DON’T WANT TO KNOW HOW, DON’T READ ON.
On Valentine’s Day, US Cyber Command put out a fun set of 11 puzzles for cryptography enthusiasts to sink their brains into. I’ve been working on them since, and have managed to solve five so far. I have had help along the way, mind you, a nudge here and there from other puzzle-solvers. I appreciate the way the community gives hints but not solutions when you’re trying to figure it out on your own. Which is why I am again warning anyone who doesn’t want the answer just given to them to LEAVE NOW. Because I am about to describe the solution to puzzle #9. Which was an onion inside a rabbit hole inside an onion. I learned a lot doing it, and I want to share.
The link to puzzle #9 goes to this adorable .png file.

Daww. Corgo hab a heart. I first examined the upper left and lower right corners to see if any pixels looked like they weren’t uniform, which can indicate hidden data. Nope. Next, I opened it in a hex editor. HexEd.it, to be exact. An earlier challenge had some plain text encoded into the .png file (I will talk about that one later) and so I hoped for a similar buried clue. No plain text. I scrolled AAALLLLL the way down, just to be sure.
And I noticed something funny. All the ASCII characters after a certain point looked…weird. Sort of uniformly weird. And maybe it was nothing, but I scrolled up to see where the change happened from normal .png garblebabble to wierd-lookin’ garbledygook.

It happened right after the IEND. Which in a .png file, is supposed to be the end of file marker. Huh.
So I google “.png IEND hidden data.” And lo and behold–that’s a steganography technique. File that knowledge away for future CTFs. COOL! I was on the trail! Now I uh…Now I–
I didn’t know what the heck to do. I knew where the data WAS. I didn’t know what to do with it. A more technical person pointed out that the hex editor I was using wasn’t really able to do what I needed to do, which was delete out all the legitimate .png data, leaving only the hex for the hidden file. After downloading the recommended hex editor, I was able to actually manipulate the data, and ended up with just the hex string that was after the file. This is a technique used in digital forensics to identify and access hidden data. As I moved my cursor around the hex, it became apparent that this was lots and lots of emoji. (It was apparent because the hex editor had a UTF-8 field, and the emoji showed up there one at a time as I moved my cursor to the accompanying hex code.)

That made sense. Puzzle #9 came with a Key.

Okay! So I had pulled the file out of the original .png, and now I could decode it! I started right away with my trusty notebook and pencil. Until I realized something super obvious about four emoji in. There were a LOT of emoji. So. Many. Emoji. Hundreds–nay, thousands. There was no way this was as simple as a substitution cypher right into plaintext. The clue was only 30 characters long. There were 64 emoji on the key. There were pages and pages of emoji in my hex editor.
Okay. So I found an online hex-to-UTF-8 converter. Copied the hex from my editor, and pasted it in the little window. Hit convert. BAM! A huge long string of emoji. Pages and pages and pages of them. I copied and pasted them into a Google doc.

I got to work. The find and replace function and I became best friends as I manually did 64 find and replaces, one for each of the emoji. And some of those things were REALLY similar. An emoji that was a smiley with round eyes vs. an emoji what was a smiley with oval eyes. Luckily, I spotted that one before I caught myself replacing the wrong character! Look at “u” and “v” on the key up there. Whew. Finally, it was done. I had a string of 64 characters instead of smileys.
It ended with an = sign, and that combined with the fact that there were 64 characters could mean only one thing: these emoji had turned into a Base64 file. (Thanks to a friend for helping me make connections on this one!) Base64 is an encoding method that takes binary data and turns it into ASCII characters. It is useful when you need to send binary data across channels designed for text content. Emails make heavy use of it. An = at the end is used to pad out a string that needs to be a certain length.
Okay, so it was definately Base64 here. I toodled over to this MAGIC website called base64.guru. Go take a look. Seriously, it’s glorious. And I tried to convert the base 64 into ASCII. It gave me an error saying that this was a gzip file type.
Iiiiiinteresting! I had literally never heard of a gzip file before. I assumed it was some kind of compressed file format, and looked it up on Google. Turns out it is a very old–as the internet goes–file type and software for compressing data. Its still in use today even though there are more efficient compression algorithms, because it uses less processing power. It is used in HTTP compression. It first was invented in 1992!
Okay, so luckily that same website I was using could convert base64 strings into gzip files. I did so, and downloaded the result.
How to open a gzip file? With 7zip. But how to open the contents of the file that was unzipped? Only a few applications were offered. One of them was Firefox. I opened it. It was hearts. Hundreds of thousands of black and white hearts.

Okay, so clearly on the right track, but what was the point of this? I zoomed out a bit to see if there was any order to the chaos. It…did look like something was up. I tried narrowing my window to get the rows to line up, but was never successful.
So I zoomed out even farther.

I still couldn’t make out what it was supposed to be, however. I had tried opening it in notepad with the same results. Couldn’t get far enough out. Then someone on reddit (Thank you /r cryptography!) suggested I try a text editor instead. Opening it in VSCode gave me:

That’s a QR code!!!
But how to see the whole thing? This part took me a really long time, I have to admit. Later, I asked a friend who also solved it how he did it, and he basically deleted all the whitespace and replaced the black hearts with a smaller character. I, however, didn’t think of that. I went oldschool.
Opening the file in Notepad++ (thank you to my partner who introduced me to this little gem of a program), I was able to zoom way out and get screenshots–6 of them–that covered the whole QR code.

Then I actually went into MS Paint and PAINSTAKINGLY cut and pasted them together. The result was fuzzy, and I spent more time than I care to admit making it pretty with pixel tools. I was unaware, you see, of how forgiving these codes are. I will not show a picture of the finished code here, but suffice it to say that my MS Paint skills were still sharp enough to make this work. I read the QR Code using an app on my phone. It resolved into a message:
Heartbroken? Have you tried subtracting your hearbreak 💔 ?
And then there was ANOTHER string of emoji. WordPress will not let me paste them in without putting each one as a separate paragraph, so I won’t bother. Most of them had to do with AV stuff: Speakers🔈🔉🔊🔇, phones 📵, videocasettes 📼, camcorders 📹, cameras 📷, DVD’s 📀, and–for some reason–yen💴? And a couple of magnifying glasses🔍 and reversal arrow things 🔂🔃. Immediately I detected the typo “hearbreak” and thought that maybe the muted speaker emoji 🔇 would be a “hearbreak.” But poring over reddit for ideas later on, someone said they solved it without noticing the typo. Hmm. So much for that theory.
It honestly took me a few days to make progress on this one. I tried subtracting the heartbreak emoji from the original long Base64 file. I didn’t have much hope of this being the right direction–it’s unlikely that there would be an entire huge file that didn’t use the letter Q. As expected, it rendered the data useless. Also, it didn’t make sense to give new emoji if I was just going to ignore them.
I put the message in my trusty Hex editor, and tried subtracting any hex pair that made up the heartbreak emoji. This pretty much caused the remaining data to be useless again. Just subtracting the hex for the heartbreak emoji just cut the single emoji from the message, of course. How did one subtract heartbreak then, in a way that would alter the code to help reveal something?

I actually resorted to just analyzing the emoji themselves–I saw a pattern that repeated itself. Clearly, there was a simple substitution code at play here. But with no way of knowing what letters they were, or where word breaks were, I was pretty well stuck.
Finally, some kind soul on /r cryptography gave me the hint that tipped my brain into the right direction. I had actually awakened myself from slumber an hour early with an idea about subtracting the heartbreak emoji’s UTF-8 code from the UTF-8 code of the others, to see what I got. But with a groggy brain, I realized with dismay that I couldn’t subtract letters.
Yeah. It had been a long week. I teach algebra and should know better. The kind hinting fellow basically confirmed what I had been thinking, and suddenly my palm hit my forehead at high speed. I couldn’t subtract the UTF-8 encoding IN ITS CURRENT FORM. Hello, my friend RapidTables.

I converted the UTF-8 hex string into decimal for the heartbreak. Then I did the same for the first emoji, which was an outbox 📤. 1F04E4-1F494 in hex is 128228-128148 in decimal. And it equals 80. I hopped onto another tab of RapidTables, and started converting hex to ASCII. 80 is a “p.” Now we were cooking with gas. I calculated each emoji by hand as I went.
And I got it.
But the phrase it generated wasn’t 30 characters and five words long, as the original solution was supposed to be. It was more than twice as long. Egads, another puzzle?!
I googled the phrase I got. It turns out it was from a book called The Light In The Heart. Not bothering to count letters, I was excited to note that the title had five words! I had done it! I entered the title into the URL that would take me to the reward if it were correct–and it was wrong. Ugh. Grr.
I carefully read the decoded phrase again. “Pursue what catches your heart, not what catches your eye…” Well. Um. The first half of it was five words. I tried it.
VICTORY! I almost cut up bits of paper so I could throw confetti on myself.
This one was a bear. It took me a couple of weeks and some nudges from friends to really get there, and I learned so much along the way. Like, for example, that Base64 strings are padded with equal signs. How to unzip a gzip file. Notepad++ is way better than regular Notepad. And I stumbled upon (and was guided towards) several new tools that I have found invaluable: HxD, Notepad++, RapidTables, and Base64.guru. I have used them to solve at least three other challenges since. I am so grateful to the game designers for this super fun opportunity to practice and expand my skills!