There is no description available… but: find the key
No information was given about this challenge, but we immediatly found it inside the scoreboard javascript, scoreboard-1.1.js
.
Here is the pertinent js code:
// Hidden challenge
if( window.addEventListener )
{
var keys = [];
window.addEventListener
(
"keydown",
function( e )
{
keys.push( e.keyCode );
if( keys.length >= 10 )
{
keys = keys.slice(-10);
// konami
challenge = [71, 74, 77, 90, 81, 15, 2, 68, 45, 47, 65, 84, 73, 92, 86, 6, 121, 73, 27, 46, 83, 6, 91, 71, 73, 81, 64, 67, 98, 53, 78, 67, 8, 64, 76, 67, 65, 66, 44, 97, 69, 78, 73, 68, 73, 66, 75, 64, 39, 97, 71, 72, 76, 8, 77, 70, 83, 66, 98, 51, 67, 69, 77, 65, 83, 66, 65, 7, 35, 47, 6, 67, 80, 92, 87, 70, 5, 75, 43, 55, 67, 6, 18, 1, 121, 73, 86, 72, 46, 52, 82, 79, 71, 70, 31, 7, 67, 68, 114, 120, 66, 23, 31, 29, 22, 67, 22, 23, 123, 39, 71, 20, 17, 29, 70, 30, 67, 30, 101, 104, 29];
decrypted = "";
for( i = 0;i < challenge.length; i++ )
{
decrypted += String.fromCharCode( challenge[i] ^ keys[i%10] );
}
try
{
eval( decrypted );
}
catch( e ) {}
keys = [];
}
},
true
);
};
The key is provided by typing the keyboard. The array challenge is XORed with the key, and the resulting string is assigned to the variable decrypted. After that, an eval(decrypted)
is executed.
Now it's a matter of finding the right key: there are two ways of doing so.
Decypt the ciphertext
Being some js code, the value of decrypted may start with alert(
. If our assumption turns out to be true, finding more than half of the key is just a matter of performing a XOR between alert(
and the first 6 chars of the ciphertext. To do so, we use the following code:
#!/usr/bin/python
# Author: Marco Squarcina
# Description: Script for solving the hidden challenge of hack.lu CTF 2011
challenge = [71, 74, 77, 90, 81, 15, 2, 68, 45, 47, 65, 84, 73, 92, 86, 6, 121, 73, 27, 46, 83, 6, 91, 71, 73, 81, 64, 67, 98, 53, 78, 67, 8, 64, 76, 67, 65, 66, 44, 97, 69, 78, 73, 68, 73, 66, 75, 64, 39, 97, 71, 72, 76, 8, 77, 70, 83, 66, 98, 51, 67, 69, 77, 65, 83, 66, 65, 7, 35, 47, 6, 67, 80, 92, 87, 70, 5, 75, 43, 55, 67, 6, 18, 1, 121, 73, 86, 72, 46, 52, 82, 79, 71, 70, 31, 7, 67, 68, 114, 120, 66, 23, 31, 29, 22, 67, 22, 23, 123, 39, 71, 20, 17, 29, 70, 30, 67, 30, 101, 104, 29];
decrypted = ""
plaintext = "alert("
key = range(10)
for i in range(len(plaintext)):
key[i] = (challenge[i] ^ ord(plaintext[i]))
for j in range(len(challenge)):
decrypted += chr(challenge[j] ^ key[j%10])
if (j+1)%10 == 0:
decrypted += '\n'
print("Key: %s\n" % str(key) + "\nPlaintext:\n%s" % decrypted)
This script yields the following results:
0 $ ./decrypt.py
Key: [38, 38, 40, 40, 37, 39, 6, 7, 8, 9]
Plaintext: alert(C%&grats!N'u solvFDje :)\nPO&=tion: ECzqd1753ds.a295c9Ema;
That makes a lot of sense. We just need to complete the missing part of the guessed plaintext "alert(****grats!" in order to get the whole key: there are only 4 reasonable substitutions:
- alert("Con
- alert("con
- alert('Con
- alert('con
We try these 4 strings until we get a perfect decryption for the entire ciphertext and finally:
0 $ ./decrypt.py
Key: [38, 38, 40, 40, 37, 39, 37, 39, 66, 65]
Plaintext: alert('congrats!\nYou solved the hidden challenge and have received an extra live :)\nsolution: fc09d1753d309fa295c9f9');
Done!
Use the konami hint
During the CTF we didn't really care about the comment // konami
inside scoreboard-1.1.js, but it would have saved us some time.
"Konami code" looks interesting because the first result is the wikipedia entry for Konami Code. Now, guess what the combination
↑ ↑ ↓ ↓ ← → ← → B A
is? Yes, the key 😛
TAA-DAAN! 🙂