Copyright Brian Starkey 2012-2014
Valid HTML 4.01 Transitional

Valid CSS!
iconGCHQ: Can You Find It? (3)max, min, close
23rd October 2013
Challenge 3(top)
The third challengeThe third challenge

Challenge 3 is a pretty nondescript series of characters. It's clearly a hexadecimal string - the numbers 0-9 and letters A-F representing values between 0 and 15.


First things first - how long is it?

$ echo "2910404C21CF8BF4CC93B7D4A518BABF34B42A8AB0047627998D633E653AF63A873C\
840D141B8535AB560BDCBDE8A67A09B7C97CB2FA308DFFBAD9F9" | wc -c

257 characters, including one for EOF, so that's 256 characters, or if you convert the hex to binary (2 characters per byte) that's 128 bytes. Hmmm, I'm sure we've seen 128 bytes somewhere before.

As soon as I noticed this I knew exactly what to do - the 128 bytes were meant to be decrypted with the key from Challenge 2; they just had to be! Unfortunately I got hung up on a few details, and the actual decryption took me forever.


Remember how the web address in Challenge 2 was all mixed up? My first attempts to decrypt the clue failed with similar messages to the ones I was getting when I tried before: "data greater than mod len", "no inverse" and so on and so forth.

I was still (rightly) convinced that the ciphertext was encrypted using the RSA key, but I thought that maybe the ciphertext had been jumbled - just like the web address before. So I tried all kinds of endianness conversion, switching bytes round, switching nibbles round, reversing the byte order in 32 and 64 bit chunks, reversing the whole string and all of the permuations of the above.

This took forever, and I had made absolutely no progress. Most of this came from the fact that I know very little about RSA encryption, so I was flying blind with regards to what should work.

RSA Key Check(top)

Eventually, I learnt about some options for openssl which would let me inspect the contents of the key. (That would have been really useful for Challenge 2 -_-). With the -check option, we get all sorts of interesting stuff:

Private-Key: (1022 bit)
publicExponent: 65537 (0x10001)
RSA key error: p not prime
RSA key error: n does not equal p q
RSA key error: d e not congruent to 1
RSA key error: dmp1 not congruent to d

I've truncated the output, except for prime2... The 77s at the start and the 20s at the end looked a little too convenient to be a big prime number. Lo and behold, if you convert it to an ASCII string: ww.whtsisilguoectsrehsri.eocu./klbtehcel y, which is of course the web address I found by hand in Challenge 2. That really would have been useful before... none of this hacking around with base64. Anyway, I digress.

So the key check told us that the RSA key is garbage - the primes aren't primes, the products don't add up... it's a mess. That's OK though, Wikipedia says "The private key consists of the modulus n and the private (or decryption) exponent d," the other bits are just "nice to haves" to aid in the encryption/decrpytion/key generation process.

The Solution(top)

So, now we know what's wrong with our key, we need to find something which can decrypt using the modulus and the private exponent. A little googling turned up a Python app which was just the ticket.

Success! That string in
            the bottom box is our clueSuccess! That string in the bottom box is our clue

So, it decrypted to: 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x77 0x77 0x2e 0x77 0x68 0x74 0x72 0x65 0x67 0x65 0x73 0x69 0x65 0x74 0x2e 0x72 0x6f 0x63 0x75 0x2e 0x2f 0x6b 0x6e 0x65 0x67 0x69 0x61 0x6d 0x30 0x32 0x33 0x31 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20
If you do this sort of thing a lot, it's easy to recognise 0x20 as the character code for space, and 0x77 as 'w'... Another web address!

A few lines of Python:

>>> s = "0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x77 0x77 0x2e 0x77 0x68 0x74 0x72 0x65 0x67 0x65 0x73 0x69 0x65 0x74 0x2e 0x72 0x6f 0x63 0x75 0x2e 0x2f 0x6b 0x6e 0x65 0x67 0x69 0x61 0x6d 0x30 0x32 0x33 0x31 0x20 0x20 0x20 0x20 0x20 0x20 0x20 0x20"
>>> chars = s.split()
>>> plaintext = ''
>>> for c in chars:
...   plaintext = plaintext + chr(int(c, 16))
>>> plaintext
'        ww.whtregesiet.rocu./knegiam0231        '
Again, correct for endianness and we get, so there's the next clue, and enigma2013 is a safe bet for the third answer. The Bletchley park/Turing/code breaking theme is strong... Bish, bash, bosh! On to Challenge 4