Solution to GCHQ competition

Note:

The domain that hosted this competition appears to have been taken over by malware pushers, and the pages hosted on other sites around the web have all been deleted. Where possible, I have replaced the links in this article to pages stored at the Internet Archive. The archived home page of this competition can be seen here.

Stage 1

stage1
The first thing you see on the competition page is the following code:

AWVLI QIQVT QOSQO ELGCV IIQWD LCUQE EOENN WWOAO
LTDNU QTGAW TSMDO QTLAO QSDCH PQQIQ DQQTQ OOTUD
BNIQH BHHTD UTEET FDUEA UMORE SQEQE MLTME TIREC
LICAI QATUN QRALT ENEIN RKG

This is a simple transposition cipher. When arranged in 11 lines of 13 characters, the message appears in the columns from left to right:

A   W   V   L   I   Q   I   Q   V   T   Q   O   S
Q   O   E   L   G   C   V   I   I   Q   W   D   L
C   U   Q   E   E   O   E   N   N   W   W   O   A
O   L   T   D   N   U   Q   T   G   A   W   T   S
M   D   O   Q   T   L   A   O   Q   S   D   C   H
P   Q   Q   I   Q   D   Q   Q   T   Q   O   O   T
U   D   B   N   I   Q   H   B   H   H   T   D   U
T   E   E   T   F   D   U   E   A   U   M   O   R
E   S   Q   E   Q   E   M   L   T   M   E   T   I
R   E   C   L   I   C   A   I   Q   A   T   U   N
Q   R   A   L   T   E   N   E   I   N   R   K   G

This reveals the URL www.metro.co.uk/turing, which is the location of stage 2.

The answer to stage 1 is the tail end of this URL: turing

Stage 2

stage2
At stage 2, you are presented with a link to a private encryption key. Opening the file in a text editor shows its contents to be as follows

-----BEGIN RSA PRIVATE KEY-----
MIIC2gIBAAKBgDfABK8+joDLdbFTDJ+y3PTTzkqCi1L2qEjgxdg1iyZshJTeKUck
SYVyKBeOBtB3FwwqXVa6iNEHJeLFewFE6ulEOIcatVp11Zg0ibMfnqTivbd6t8/z
3KzqrFksg9xQiicMactmTqFkm8ro5ODc2NTQzMjEwLy4tLCslBOCOVHxAgMBAAEC
gYATW12FB2BtQbecmSxh6rWjYENZRZhgdvoZS8oF9xlYfwdNtRF5/RR1/BwFia++
BAuBktgTu/KzORsjcNPzrd0uTCbTG6hW8YPK2ROVOOeAMHek8Nl3+SW5wdePKuWw
MdjDDjqxXDns+ZC1d2Cpz5V+x+2znOYL0bsEKei0sWl7LQKBgDfABK8+joDLdbFT
DJ+y3PTTzkqCi1L2qEjgxdg1iyZshJTeKUckSYVyKBeOBtB3FwwqXVa6iNEHJeLF
ewFE6uhVSior5HGPArFhsOQ0v9ob1NCV7P8M99qN4XplmX/xs05HgQCVh9aMWtio
pKCcmJSQjIiEgHx4dHBsU9JB+TvkAkB3dy53aHRzaXNpbGd1b2VjdHNyZWhzcmku
ZW9jdS4va2xidGVoY2VsIHkgICAgICAgICAgICAgICAgICAgIAuPAkATpSSd/C5S
IEAbUPk+ZYAdt7OYVzay7ViAiaukhkt+/sJG+m8GmHnAKyLf9ohx3/aIcd/2iHHf
9ohx3/ayirJPAkAIefJYEpdAoRjJQCHPGUpOVjLiyQMyPcnsutG+ctAGGU8lZTDU
yUim9V7iwqTE4sKkxOLCpMTiwqTE4sKkxOFNAoGAFInzTsAOkauW3crd1XfxMhxi
tUkapdQqlwvFhZuouNIybfEOfW6WkjtghBDyqf50cEFWXMJ7Vk8mr6cwTosPvYKU
VXKUCblretLTeU95TlbkprizPky++5b7pQuSi3mpLMi+6VgvcjTthfXPYNg2JjJp
gmteC4felYL/2FTAmT8=
-----END RSA PRIVATE KEY-----

After running the contents of this file through binhex and hexdump utilities, it’s possible to see a bit of plain text in the middle of the data:

...
0190: d2 41 f9 3b e4 02 40 77  77 2e 77 68 74 73 69 73  .A.;..@ww.whtsis
01a0: 69 6c 67 75 6f 65 63 74  73 72 65 68 73 72 69 2e  ilguoectsrehsri.
01b0: 65 6f 63 75 2e 2f 6b 6c  62 74 65 68 63 65 6c 20  eocu./klbtehcel 
01c0: 79 20 20 20 20 20 20 20  20 20 20 20 20 20 20 20  y               
01d0: 20 20 20 20 20 0b 8f 02  40 13 a5 24 9d fc 2e 52       ...@..$...R
...

After switching around each pair of letters (some sort of endianness issue, perhaps?), we obtain the URL www.thisisgloucestershire.co.uk/bletchley.

Stage 2 answer: bletchley

Stage 3

stage3
For stage 3, all we are given is a 128-byte hexadecimal number:

2910404C21CF8BF4CC93B7D4A518BABF34B42A8AB0047627998D633E653AF63A873C\
8FABBE8D095ED125D4539706932425E78C261E2AB9273D177578F20E38AFEF124E06\
8D230BA64AEB8FF80256EA015AA3BFF102FE652A4CBD33B4036F519E5899316A6250\
840D141B8535AB560BDCBDE8A67A09B7C97CB2FA308DFFBAD9F9

It appears that this has to be decrypted using the private key from stage 2. But OpenSSL refuses to do this:

$ openssl rsautl -decrypt -in level3.bin -inkey comp1.key -out level3.txt
RSA operation error
2685662596:error:0306E06C:bignum routines:BN_mod_inverse:no inverse:bn_gcd.c:491:

The error messages from OpenSSL aren’t very clear, but the reason why it’s refusing to work is because the private key is invalid:

$ openssl rsa -in comp1.key -check
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

So let’s take a closer look at this private key:

$ openssl rsa -in comp1.key -text -noout
Private-Key: (1022 bit)
modulus:
    37:c0:04:af:3e:8e:80:cb:75:b1:53:0c:9f:b2:dc:
    f4:d3:ce:4a:82:8b:52:f6:a8:48:e0:c5:d8:35:8b:
    26:6c:84:94:de:29:47:24:49:85:72:28:17:8e:06:
    d0:77:17:0c:2a:5d:56:ba:88:d1:07:25:e2:c5:7b:
    01:44:ea:e9:44:38:87:1a:b5:5a:75:d5:98:34:89:
    b3:1f:9e:a4:e2:bd:b7:7a:b7:cf:f3:dc:ac:ea:ac:
    59:2c:83:dc:50:8a:27:0c:69:cb:66:4e:a1:64:9b:
    ca:e8:e4:e0:dc:d8:d4:d0:cc:c8:c4:c0:bc:b8:b4:
    b0:ac:94:13:82:39:51:f1
publicExponent: 65537 (0x10001)
privateExponent:
    13:5b:5d:85:07:60:6d:41:b7:9c:99:2c:61:ea:b5:
    a3:60:43:59:45:98:60:76:fa:19:4b:ca:05:f7:19:
    58:7f:07:4d:b5:11:79:fd:14:75:fc:1c:05:89:af:
    be:04:0b:81:92:d8:13:bb:f2:b3:39:1b:23:70:d3:
    f3:ad:dd:2e:4c:26:d3:1b:a8:56:f1:83:ca:d9:13:
    95:38:e7:80:30:77:a4:f0:d9:77:f9:25:b9:c1:d7:
    8f:2a:e5:b0:31:d8:c3:0e:3a:b1:5c:39:ec:f9:90:
    b5:77:60:a9:cf:95:7e:c7:ed:b3:9c:e6:0b:d1:bb:
    04:29:e8:b4:b1:69:7b:2d
prime1:
    37:c0:04:af:3e:8e:80:cb:75:b1:53:0c:9f:b2:dc:
    f4:d3:ce:4a:82:8b:52:f6:a8:48:e0:c5:d8:35:8b:
    26:6c:84:94:de:29:47:24:49:85:72:28:17:8e:06:
    d0:77:17:0c:2a:5d:56:ba:88:d1:07:25:e2:c5:7b:
    01:44:ea:e8:55:4a:2a:2b:e4:71:8f:02:b1:61:b0:
    e4:34:bf:da:1b:d4:d0:95:ec:ff:0c:f7:da:8d:e1:
    7a:65:99:7f:f1:b3:4e:47:81:00:95:87:d6:8c:5a:
    d8:a8:a4:a0:9c:98:94:90:8c:88:84:80:7c:78:74:
    70:6c:53:d2:41:f9:3b:e4
prime2:
    77:77:2e:77:68:74:73:69:73:69:6c:67:75:6f:65:
    63:74:73:72:65:68:73:72:69:2e:65:6f:63:75:2e:
    2f:6b:6c:62:74:65:68:63:65:6c:20:79:20:20:20:
    20:20:20:20:20:20:20:20:20:20:20:20:20:20:20:
    20:20:0b:8f
exponent1:
    13:a5:24:9d:fc:2e:52:20:40:1b:50:f9:3e:65:80:
    1d:b7:b3:98:57:36:b2:ed:58:80:89:ab:a4:86:4b:
    7e:fe:c2:46:fa:6f:06:98:79:c0:2b:22:df:f6:88:
    71:df:f6:88:71:df:f6:88:71:df:f6:88:71:df:f6:
    b2:8a:b2:4f
exponent2:
    08:79:f2:58:12:97:40:a1:18:c9:40:21:cf:19:4a:
    4e:56:32:e2:c9:03:32:3d:c9:ec:ba:d1:be:72:d0:
    06:19:4f:25:65:30:d4:c9:48:a6:f5:5e:e2:c2:a4:
    c4:e2:c2:a4:c4:e2:c2:a4:c4:e2:c2:a4:c4:e2:c2:
    a4:c4:e1:4d
coefficient:
    14:89:f3:4e:c0:0e:91:ab:96:dd:ca:dd:d5:77:f1:
    32:1c:62:b5:49:1a:a5:d4:2a:97:0b:c5:85:9b:a8:
    b8:d2:32:6d:f1:0e:7d:6e:96:92:3b:60:84:10:f2:
    a9:fe:74:70:41:56:5c:c2:7b:56:4f:26:af:a7:30:
    4e:8b:0f:bd:82:94:55:72:94:09:b9:6b:7a:d2:d3:
    79:4f:79:4e:56:e4:a6:b8:b3:3e:4c:be:fb:96:fb:
    a5:0b:92:8b:79:a9:2c:c8:be:e9:58:2f:72:34:ed:
    85:f5:cf:60:d8:36:26:32:69:82:6b:5e:0b:87:de:
    95:82:ff:d8:54:c0:99:3f

The last byte of prime1 is 0xE4, which means it’s an even number and obviously not prime. But we don’t actually need this number. To decrypt a ciphertext c into its corresponding plaintext p, we only need to calculate p = ce mod m where e and m are the private exponent and modulus. This can be done easily enough using a long integer library, or online tools such as the RSA decryptor created by Nathan Michaels. Here’s a short program that does this calculation using the GMP library:

#include <gmp.h>

int main() {
  mpz_t base, exp, mod, result;
  mpz_init_set_str(base,
        "2910404C21CF8BF4CC93B7D4A518BABF34B42A8AB0047627998D"
        "633E653AF63A873C8FABBE8D095ED125D4539706932425E78C26"
        "1E2AB9273D177578F20E38AFEF124E068D230BA64AEB8FF80256"
        "EA015AA3BFF102FE652A4CBD33B4036F519E5899316A6250840D"
        "141B8535AB560BDCBDE8A67A09B7C97CB2FA308DFFBAD9F9",
        16);
  mpz_init_set_str(exp,
        "135B5D8507606D41B79C992C61EAB5A360435945986076FA194B"
        "CA05F719587F074DB51179FD1475FC1C0589AFBE040B8192D813"
        "BBF2B3391B2370D3F3ADDD2E4C26D31BA856F183CAD9139538E7"
        "803077A4F0D977F925B9C1D78F2AE5B031D8C30E3AB15C39ECF9"
        "90B57760A9CF957EC7EDB39CE60BD1BB0429E8B4B1697B2D",
        16);
  mpz_init_set_str(mod,
        "37C004AF3E8E80CB75B1530C9FB2DCF4D3CE4A828B52F6A848E0"
        "C5D8358B266C8494DE29472449857228178E06D077170C2A5D56"
        "BA88D10725E2C57B0144EAE94438871AB55A75D5983489B31F9E"
        "A4E2BDB77AB7CFF3DCACEAAC592C83DC508A270C69CB664EA164"
        "9BCAE8E4E0DCD8D4D0CCC8C4C0BCB8B4B0AC9413823951F1",
        16);
  mpz_init(result);
  mpz_powm (result, base, exp, mod);
  gmp_printf("b: 0x%Zx\ne: 0x%Zx\nm: 0x%Zx\nb^e mod m: 0x%Zx\n",
             base, exp, mod, result);
  return 0;
}

When converted to ASCII characters, the hexadecimal output from this program corresponds to the text string “ww.whtregesiet.rocu./knegiam0231” (padded with spaces). After swapping bytes around like we did in stage 2, we obtain the following URL: www.theregister.co.uk/enigma2013.

Stage 3 answer: enigma2013

Stage 4

stage4
Stage 4 was much easier. The puzzle consisted of an old black and white photo of a Colossus codebreaking computer. Although “Colossus” is accepted as the answer to this level, it doesn’t provide us with a URL for the next level.

For that, you need to look inside the JPEG file, which contains another hidden image starting at an offset of 52180 bytes containing the URL www.eveningstandard.co.uk/colossus:

stage3.extra
Stage 4 answer: colossus

Stage 5

This stage was a bit of a disappointment. A couple of seconds after loading the web page, the answer appears all by itself:

Following the link to canyoufindit.co.uk/secured just takes you back to the competition’s home page. And as far as I can tell, that’s all there is to it.

Stage 5 answer: secured

Tagged with: