Solution to Part Three of the GCHQ ‘’

Download the exe found in the previous part:

Load it up in Olly and we immediately find we need cygcrypt-0.dll.  To get this dll, download cygwin and install.  This dll is not installed by default – in the installation, search and select all references to ‘ssh’ and ‘crypt’.

Now run the exe. We’re told:

usage: keygen.exe hostname

So, set the execution argument in Olly to  Next, let’s analyse the behaviour of the program – it’s small and basic!

Here’s some pseudo code detailing its operation:

Read first line of file 'license.txt' into buffer
Are the first four characters gchq? If not, error.
Compute crypt(buffer+4,"hqDTK7b8K2rvw")
Does result of crypt equal hqDTK7b8K2rvw? If not, error.
Create request string /hqDTK7b8K2rvw/AAAAAAAA/BBBBBBBB/CCCCCCCC/key.txt
(AA/BB/CC are the hex representations of the next three groups of four characters)
Perform HTTP request using above string, accessing host provided by the first argument (which we set to

So, license.txt should contain:

Note that we don’t yet know password.  However, let’s just bypass the check of the crypted password:

MOV EAX,DWORD PTR DS:[402000]     ;ptr to string hqDTK7b8K2rvw
MOV DWORD PTR SS:[ESP+4],EAX      ;move onto stack
LEA EAX,DWORD PTR SS:[EBP-38]     ;pointer to gchqPASSWORDabcdefghijkl
ADD EAX,4                         ;eax now points to PASSWORDabcdefghijkl
MOV DWORD PTR SS:[ESP],EAX        ;move onto stack
CALL 0040142C                     ;  <JMP.&cygcrypt-0.crypt> (call crypt)
MOV DWORD PTR SS:[ESP+4],EAX      ;put ptr to hqDTK7b8K2rvw onto stack
MOV DWORD PTR SS:[ESP],EDX        ;put str to crypt result onto stack
CALL 00401530                     ;strcmp
JNZ SHORT 004011A5                ;keygen.004011A5

So, NOP out this last jump and PASSWORD can be anything. Now, given this input, the following request string is made:
GET /hqDTK7b8K2rvw/64636261/68676665/6c6b6a69/key.txt HTTP/1.0

(Note that 0×61 = ‘a’, 0×62 = ‘b’ etc)

Clearly, GCHQ have missed a trick here! Why on earth they didn’t make the first part of the result the plaintext password (requiring the crypt to actually be cracked), is beyond me.  This makes the use of crypt completely redundant.  However, for a bit of fun I decided to crack the hash anyway.

The cygwin doc explains:

It provides a static library libcrypt.a as well as a shared library
cygcrypt-0.dll together with a link lib libcrypt.dll.a, which export
the functions


The passwords created by crypt(3) are 56 bit DES encrypted and are
100% identical to those created by the Linux crypt().

So, we have ourselves an oldschool unix crypt hash.  The crypt.h header file tells us:

char * _EXFUN(crypt, (const char *key, const char *salt));

So, hqDTK7b8K2rvw is our salt string.  Note, that crypt(3) only uses the first two characters of the salt, and returns (salt[2]+hash[11]).  So our actual salt is ‘hq’ and ‘DTK7b8K2rvw’ is the actual hash.

Time to release John the Ripper!

Stick our hash in a unix format passwd file:


And run the following command:

john-mmx --format=DES -i=alpha passwd

format=DES specifies the use of the crypt(3) algorithm, i=alpha means try all alpha combinations.  Note that crypt(3) only uses the first 8 characters, so this means from to ‘a’ to ‘zzzzzzzz’.  Here’s what John tells us after a total of 35 minutes (note I was testing the ‘restore’ functionality here):

So our plaintext is ‘cyberwin’.  We now have the first 12 characters of license.txt:

But we still need to figure out the last bit.  The hint is actually the text here:

004011A5  |> \C70424 A82040>MOV DWORD PTR SS:[ESP],4020A8            ; ||ASCII "loading stage1 license key(s)...
004011AC  |.  E8 A7030000   CALL 00401558                            ; |\printf
004011B1  |.  8B45 D4       MOV EAX,DWORD PTR SS:[EBP-2C]            ; load first DWORD
004011B4  |.  8945 B8       MOV DWORD PTR SS:[EBP-48],EAX            ; |
004011B7  |.  C70424 CC2040>MOV DWORD PTR SS:[ESP],4020CC            ; |ASCII "loading stage2 license key(s)...

004011BE  |.  E8 95030000   CALL 00401558                            ; \printf
004011C3  |.  8B45 D8       MOV EAX,DWORD PTR SS:[EBP-28]
004011C6  |.  8945 BC       MOV DWORD PTR SS:[EBP-44],EAX        ;load second DWORD
004011C9  |.  8B45 DC       MOV EAX,DWORD PTR SS:[EBP-24]
004011CC  |.  8945 C0       MOV DWORD PTR SS:[EBP-40],EAX      ;load third DWORD

(scroll right to see the comments above)

‘Stage’ was reffered to in VM javascript (“stage 2 of 3″), so it seems we need 1 DWORD from stage one and two DWORDs from stage two.  The redundant firmware variable in stage two immediately comes to mind:

firmware: [0xd2ab1f05, 0xda13f110]

We need to go back and have another look at stage one to get any further.  Right at the start of the executable code of stage one, we see:

00401000 > $ EB 04          JMP SHORT test.00401006
00401002     AF             DB AF
00401003     C2             DB C2
00401004     BF             DB BF
00401005     A3             DB A3
00401006   > 81EC 00010000  SUB ESP,100

This is a bit fishy… the JMP and four bytes (a DWORD) are completely redundant! So why don’t we try AF C2 BF A3?

Fiddling around with the endianness results in the following valid request:

Finally, key.txt contains the final password: Pr0t3ct!on#cyber_security@12*12.2011+

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>