Sitting at home yesterday morning, watching cartoons with the kids and checking my Twitter feed, I saw a tweet from Georgia Weidman with a picture of the badge from BSides Phoenix. It looked like an awesome badge, made out of hefty chrome and with an integrated bottle opener. It also had a puzzle on it. There goes the rest of my morning….

As always, if you’d like to try to solve this yourself, then STOP now, as the rest of this post is full of spoilers. If you’d like to see just the images needed to solve the puzzle, click here: BSidesPHX 2012 Images.

The badge has a hexadecimal string all around the edge. And also some other text – SNOW, and the con name:

SNOW 0E071A1F4D495C09001848140240462117025EF  BSIDES PHX 2012

The first thing I did was to count the hex digits. There were 39. Really, 39? That’s an odd number – usually hex is presented in two-digit pairs (each pair representing a single byte). Why the extra digit?

I looked at two ways to split the string up. Either the trailing “F” was unnecessary (or at least unnecessary for the time being), or the leading “0” was. Splitting the digits up the first way, I got:

0E 07 1A 1F 4D 49 5C 09 00 18 48 14 02 40 46 21 17 02 5E F

Grouping them like this, the first digit of each pair is one of only 5 numbers: 0, 1, 2, 4, or 5. This actually looks very promising, as most ASCII text is limited to only a few initial digits as well (all printible characters being in the 2x-7x range). If this is the right arrangement, then it’s extremely likely that this is a simple XOR code.

But just to be sure, let’s look at it the other way:

E0 71 A1 F4 D4 95 C0 90 01 84 81 40 24 04 62 11 70 25 EF

In this case, the leading character is much more random, being chosen from 13 of 16 possible values: [01246789acdef]. Again, this tells me that the first arrangement was right, and that the string is ASCII, but with bits flipped via XOR.

Naturally, I have a tool that does this for me. The first thing I tried was obvious keys from the badge itself: SNOW, or BSIDES, etc. My tool took each byte in the key and used the eXclusive OR (XOR) operation against corresponding byte(s) in the ciphertext. It occurrs to me that I’ve never explained how this works, so here’s a quick digression.

Take the ciphertext stream 0E 07 1A 1F and look at each
as bits (I'll just do 0E):

    0000 1110  (hex 0E, 1st byte of ciphertext)

Now take the first character of your key and write it
underneath the ciphertext bits. I'll start with "B" 
for "BSides":

    0000 1110  (hex 0E, 1st byte of ciphertext)
    0100 0010  (hex 42, "B" in ASCII)

Now, for each bit position, we XOR the key bit
against the ciphertext bit. This works like a 
normal logical OR operation, except for the 
exclusivity requirement: If BOTH input bits 
are 1, then the output is 0. That is (here I'll 
use ^ to represent XOR), 0^0 = 0, 0^1 = 1, 
1^0 = 1, and 1^1 = 0.  So the result, written 
below, would be:

    0000 1110  (hex 0E, 1st byte of ciphertext)
    0100 0010  (hex 42, "B" in ASCII)
    0100 1100  (hex 4C, or "L")

Repeat this with the next ciphertext byte, and the 
next key byte. When you reach the end of the key, 
start over with the first byte of the key, and keep going.

Trying “BSides” as the key, I get “LTs{(” as the beginning of the decryption. Doesn’t look right. SNOW isn’t much better: “]IUH”. And they both have bunches of control characters and other non-printable bytes in the output. After trying a few different keys, I tried a few different plaintexts. The neat thing about an XOR code is that it works both ways. If you try a plaintext as the key, the result is actually the key that would generate that plaintext. So I tried “WWW” and “bit.ly” and a few other URL shorteners, but got nothing that looks like a real key.

Now I’m wondering about the non-printable characters, and I begin kind of staring at the 1st character (or nybble) in each byte. They seem to be not randomly distributed, and I kind of separate them like this:

0011 445 001 4 10 44 2 10 5

This looks sort of interesting. But I can’t think of how a key might produce such a pattern – or what kind of plaintext would match it. Four characters in one block (like all caps) then 3 in another block (maybe lowercase?) etc. xxxx-yyy-xxx-y-xx-yy-z-xx-y. In fact, it looks like an all-lowercase key guarantees me all printable characters, but that still doesn’t help. This is getting me nowhere, and the kids are clamoring for lunch. So I make lunch, while staring at the code, and as we’re eating, I cast about on a key hunt.

While looking at the BSides PHX home page, I saw an image with the stenciled, painted words “F SNOW” (the page referenced Snowmageddon from 2010, and how there’d be no snow in Phoenix). So I tried FSNOW as the key, and got this:

HTTP...GOO.GL..gDL.

Success! goo.gl/gDL. No. Didn’t work. I’m still missing something in those unprintable characters… What if…the first “.” after “HTTP” is supposed to be a “:”? What key letter would produce that? Changing my key to “FSNO:”, I get:

HTTPw..GO".GL.|gDL

So I should use a lowercase w instead of uppercase? Wait, didn’t I already determine that the key should be all lowercase anyway? Stupid me. Let’s try “fsnow”:

http://goo.gl/1Gdl1

There we go, much better. It also fits that “xxxx-yyy-xxx-y-xx” pattern I noticed earlier. Also, I should note that I’d tried forcing keys based on a plaintext that started with www or bit.ly or other similar URLs, but never tried “http”. If I’d tried that, I would have had the key like 5 minutes into the game.

The URL redirects my browser to a photo of Caesar’s Palace in the snow. Cool, snow in Vegas. Is there something hidden in the image? Using “strings” I find, at the very end of the image file, the following string:

neupqebtjpafadstkbtqzmqo7o8n6625480731on2731r378pp109paftfyx

Rot-13? Nope. But it almost has to be a simple shift – after all, it was a picture of Caesar. And turns out ROT-14 was the answer. This would correspond to an encryption offset of 12, which might represent 2012, or might just be a coincidence. At any rate, now I have a new URL to visit:

bsidesphxdotorghyphenaec7c8b6625480731cb2731f378dd109dothtml
    or
bsidesphx.org-aec7c8b6625480731cb2731f378dd109.html

Hm. Hyphen? That’s weird. And what do I do with the hex? A little playing didn’t get me anywhere, so I tried it as-is, only changed the ‘-’ to ‘/’ because otherwise it wouldn’t do anything.

This loads up a web page that says, simply, “congrats!” with a link. Clicking that pops up a mail window to a gmail address, with the subject “I win” pre-loaded. Rapidly, I fill out the email (worried that someone else might be beating me AT THIS VERY MOMENT) and ask, is there a prize? (I also mentioned that I’m perfectly happy allowing any prize to go to someone who’s actually AT the con, but at any rate, if they have any extras I’d love one of the awesome badges).

I got a response from Skyler Bingham a few minutes later, saying that I was the first to solve it, and that, certainly, he’d mail me a badge. Cool!!

I announced my victory on Twitter, but quickly followed up with a message that people at the con should keep trying, as it was an easy and fun puzzle. I don’t know if anyone else solved it, but did see a couple people tweeting about it. Of course, this was probably a pretty small event, so I don’t think there were that many people even playing.

But… What about that extra “F” at the end of the hex? I never needed that for anything, did I? Well, about 5 minutes after I’d heard from Skyler, it hit me: The F wasn’t part of the hex, it was part of “SNOW” at the top of the badge. That’s right, it wasn’t “SNOW (hex) F,” it was “F SNOW (hex).” The key was right on the badge all the time. Sneaky!

Thanks for the fun puzzle, Skyler!