First of all, I must say
Congratulations! to the organization, it was a pretty good contest to be the first time. Not only binary exploiting and reversing as
Defcon does. Next time it should be longer to have more fun!
All levels were initially located in the official site
ctf.noconname.org ( Probably not working now - see below ):
As shown in the screenshot, there are three different levels. Here you have a short description of each one:
- Level 1: Obfuscated Javascript with some maths behind
- Level 2: An APK to analyze.
- Level 3: An ELF x64 binary to analyze.
You will find a copy - mirror - of the whole set of tests and environment in this site at
NcN CTF mirror.
Level 1 - Obfuscated Javascript
When you access to the first level, you will find a form requesting a key. You need to put a special string to retrieve the final
token to pass:
Looking at the source-code there is a pair of interesting sentences, both bold-marked in the following snippet:
<!DOCTYPE html>
<html>
<head>
<title>NcN 2013 Registration Quals</title>
<link rel="stylesheet" href="../res/main.css" type="text/css" media="screen"/>
<link href='../res/UbuntuMono.css' rel='stylesheet' type='text/css'>
<meta content="Javier Marcos @javutin" name="author" />
<script type="text/javascript" src="crypto.js"></script>
</head>
<body>
<div id="level">
<center>
<h2 style="color: white">Discover the buried valid key:</h2>
<form action="login.php" method="POST" onsubmit="return encrypt(this);">
<table border=0 align="center">
<tr>
<td><label style="color: white" for="key"><b>Key: </b></label></td>
<td><input type="text" name="password" id="password" class="input"></td>
<input type="hidden" name="key" id="key" value="">
<input type="hidden" name="verification" id="verification" value="yes">
</tr>
<tr>
<td colspan="2" align="center"><p><input type="submit" name="send" class="button" value="Send"></p></td>
</tr>
</table>
</form>
</center>
</div>
</body>
</html>
As code shows,
encrypt function is called when key is specified in the form, the code of this function is located in a script loaded in the first marked code
crypt.js.
Let's see the content of the included
JavaScript resource:
var _0x52ae=["\x66\x20\x6F\x28\x38\x29\x7B\x63\x20\x69\x2C\x6A\x3D\x30\x3B\x6B\x28\x69\x3D\x30\x3B\x69\x3C\x38\x2E\x6C\x3B\x69\x2B\x2B\x29\x7B\x6A\x2B\x3D\x28\x38\x5B\x69\x5D\x2E\x73\x28\x29\x2A\x28\x69\x2B\x31\x29\x29\x7D\x67\x20\x74\x2E\x75\x28\x6A\x29\x25\x76\x7D\x66\x20\x70\x28\x68\x29\x7B\x68\x3D\x68\x2E\x71\x28\x30\x29\x3B\x63\x20\x69\x3B\x6B\x28\x69\x3D\x30\x3B\x69\x3C\x77\x3B\x2B\x2B\x69\x29\x7B\x63\x20\x35\x3D\x69\x2E\x78\x28\x79\x29\x3B\x6D\x28\x35\x2E\x6C\x3D\x3D\x31\x29\x35\x3D\x22\x30\x22\x2B\x35\x3B\x35\x3D\x22\x25\x22\x2B\x35\x3B\x35\x3D\x7A\x28\x35\x29\x3B\x6D\x28\x35\x3D\x3D\x68\x29\x41\x7D\x67\x20\x69\x7D\x66\x20\x6E\x28\x38\x29\x7B\x63\x20\x69\x2C\x61\x3D\x30\x2C\x62\x3B\x6B\x28\x69\x3D\x30\x3B\x69\x3C\x38\x2E\x6C\x3B\x2B\x2B\x69\x29\x7B\x62\x3D\x70\x28\x38\x2E\x71\x28\x69\x29\x29\x3B\x61\x2B\x3D\x62\x2A\x28\x69\x2B\x31\x29\x7D\x67\x20\x61\x7D\x66\x20\x42\x28\x39\x29\x7B\x63\x20\x32\x3B\x32\x3D\x6E\x28\x39\x2E\x64\x2E\x65\x29\x3B\x32\x3D\x32\x2A\x28\x33\x2B\x31\x2B\x33\x2B\x33\x2B\x37\x29\x3B\x32\x3D\x32\x3E\x3E\x3E\x36\x3B\x32\x3D\x32\x2F\x34\x3B\x32\x3D\x32\x5E\x43\x3B\x6D\x28\x32\x21\x3D\x30\x29\x7B\x72\x28\x27\x44\x20\x64\x21\x27\x29\x7D\x45\x7B\x72\x28\x27\x46\x20\x64\x20\x3A\x29\x27\x29\x7D\x39\x2E\x47\x2E\x65\x3D\x6E\x28\x39\x2E\x64\x2E\x65\x29\x3B\x39\x2E\x48\x2E\x65\x3D\x22\x49\x22\x2B\x6F\x28\x39\x2E\x64\x2E\x65\x29\x3B\x67\x20\x4A\x7D","\x7C","\x73\x70\x6C\x69\x74","\x7C\x7C\x72\x65\x73\x7C\x7C\x7C\x68\x65\x78\x5F\x69\x7C\x7C\x7C\x73\x74\x72\x7C\x66\x6F\x72\x6D\x7C\x7C\x7C\x76\x61\x72\x7C\x70\x61\x73\x73\x77\x6F\x72\x64\x7C\x76\x61\x6C\x75\x65\x7C\x66\x75\x6E\x63\x74\x69\x6F\x6E\x7C\x72\x65\x74\x75\x72\x6E\x7C\x66\x6F\x6F\x7C\x7C\x68\x61\x73\x68\x7C\x66\x6F\x72\x7C\x6C\x65\x6E\x67\x74\x68\x7C\x69\x66\x7C\x6E\x75\x6D\x65\x72\x69\x63\x61\x6C\x5F\x76\x61\x6C\x75\x65\x7C\x73\x69\x6D\x70\x6C\x65\x48\x61\x73\x68\x7C\x61\x73\x63\x69\x69\x5F\x6F\x6E\x65\x7C\x63\x68\x61\x72\x41\x74\x7C\x61\x6C\x65\x72\x74\x7C\x63\x68\x61\x72\x43\x6F\x64\x65\x41\x74\x7C\x4D\x61\x74\x68\x7C\x61\x62\x73\x7C\x33\x31\x33\x33\x37\x7C\x32\x35\x36\x7C\x74\x6F\x53\x74\x72\x69\x6E\x67\x7C\x31\x36\x7C\x75\x6E\x65\x73\x63\x61\x70\x65\x7C\x62\x72\x65\x61\x6B\x7C\x65\x6E\x63\x72\x79\x70\x74\x7C\x34\x31\x35\x33\x7C\x49\x6E\x76\x61\x6C\x69\x64\x7C\x65\x6C\x73\x65\x7C\x43\x6F\x72\x72\x65\x63\x74\x7C\x6B\x65\x79\x7C\x76\x65\x72\x69\x66\x69\x63\x61\x74\x69\x6F\x6E\x7C\x79\x65\x73\x7C\x74\x72\x75\x65","","\x66\x72\x6F\x6D\x43\x68\x61\x72\x43\x6F\x64\x65","\x72\x65\x70\x6C\x61\x63\x65","\x5C\x77\x2B","\x5C\x62","\x67"];eval(function (_0x7038x1,_0x7038x2,_0x7038x3,_0x7038x4,_0x7038x5,_0x7038x6){_0x7038x5=function (_0x7038x3){return (_0x7038x3<_0x7038x2?_0x52ae[4]:_0x7038x5(parseInt(_0x7038x3/_0x7038x2)))+((_0x7038x3=_0x7038x3%_0x7038x2)>35?String[_0x52ae[5]](_0x7038x3+29):_0x7038x3.toString(36));} ;if(!_0x52ae[4][_0x52ae[6]](/^/,String)){while(_0x7038x3--){_0x7038x6[_0x7038x5(_0x7038x3)]=_0x7038x4[_0x7038x3]||_0x7038x5(_0x7038x3);} ;_0x7038x4=[function (_0x7038x5){return _0x7038x6[_0x7038x5];} ];_0x7038x5=function (){return _0x52ae[7];} ;_0x7038x3=1;} ;while(_0x7038x3--){if(_0x7038x4[_0x7038x3]){_0x7038x1=_0x7038x1[_0x52ae[6]]( new RegExp(_0x52ae[8]+_0x7038x5(_0x7038x3)+_0x52ae[8],_0x52ae[9]),_0x7038x4[_0x7038x3]);} ;} ;return _0x7038x1;} (_0x52ae[0],46,46,_0x52ae[3][_0x52ae[2]](_0x52ae[1]),0,{}));
Wow! It's a bit confusing all this one-line script... Let's use
jsbeautifier.org to auto-indent our code:
var _0x52ae = [ "\x66\x20 ... \x67"];
eval(function (_0x7038x1, _0x7038x2, _0x7038x3, _0x7038x4, _0x7038x5, _0x7038x6) {
_0x7038x5 = function (_0x7038x3) {
return (_0x7038x3 < _0x7038x2 ? _0x52ae[4] : _0x7038x5(parseInt(_0x7038x3 / _0x7038x2))) + ((_0x7038x3 = _0x7038x3 % _0x7038x2) > 35 ? String[_0x52ae[5]](_0x7038x3 + 29) : _0x7038x3.toString(36));
};
if (!_0x52ae[4][_0x52ae[6]](/^/, String)) {
while (_0x7038x3--) {
_0x7038x6[_0x7038x5(_0x7038x3)] = _0x7038x4[_0x7038x3] || _0x7038x5(_0x7038x3);
};
_0x7038x4 = [
function (_0x7038x5) {
return _0x7038x6[_0x7038x5];
}
];
_0x7038x5 = function () {
return _0x52ae[7];
};
_0x7038x3 = 1;
};
while (_0x7038x3--) {
if (_0x7038x4[_0x7038x3]) {
_0x7038x1 = _0x7038x1[_0x52ae[6]](new RegExp(_0x52ae[8] + _0x7038x5(_0x7038x3) + _0x52ae[8], _0x52ae[9]), _0x7038x4[_0x7038x3]);
};
};
return _0x7038x1;
}(_0x52ae[0], 46, 46, _0x52ae[3][_0x52ae[2]](_0x52ae[1]), 0, {}));
Well.. still confusing... So, this code is clearly obfuscated. The large buffer defined at the beginning is used to generate and complete snippets of code in the script. Making the static analysis the hardest choice.
Let's try to modify the code in order to get, in an easy way, the final code actually executed in a human readable format. To do so, I used the
NodeJS interpreter and changed
eval function to
console.log in order to print the final code into the terminal, replacement with
alert should be useful as well.
The following figure shows the whole process, the output has been marked in
italic and
eval replacement in bold:
# node
> _0x52ae=["\x66\x20\x6F\x28\x38\x29\x7B\x63\x20\x69\x2C\x6A\x3D\x30\x3B\x6B\x28\x69\x3D\x30\x3B\x69\x3C\x38\x2E\x6C\x3B\x69\x2B\x2B\x29\x7B\x6A\x2B\x3D\x28\x38\x5B\x69\x5D\x2E\x73\x28\x29\x2A\x28\x69\x2B\x31\x29\x29\x7D\x67\x20\x74\x2E\x75\x28\x6A\x29\x25\x76\x7D\x66\x20\x70\x28\x68\x29\x7B\x68\x3D\x68\x2E\x71\x28\x30\x29\x3B\x63\x20\x69\x3B\x6B\x28\x69\x3D\x30\x3B\x69\x3C\x77\x3B\x2B\x2B\x69\x29\x7B\x63\x20\x35\x3D\x69\x2E\x78\x28\x79\x29\x3B\x6D\x28\x35\x2E\x6C\x3D\x3D\x31\x29\x35\x3D\x22\x30\x22\x2B\x35\x3B\x35\x3D\x22\x25\x22\x2B\x35\x3B\x35\x3D\x7A\x28\x35\x29\x3B\x6D\x28\x35\x3D\x3D\x68\x29\x41\x7D\x67\x20\x69\x7D\x66\x20\x6E\x28\x38\x29\x7B\x63\x20\x69\x2C\x61\x3D\x30\x2C\x62\x3B\x6B\x28\x69\x3D\x30\x3B\x69\x3C\x38\x2E\x6C\x3B\x2B\x2B\x69\x29\x7B\x62\x3D\x70\x28\x38\x2E\x71\x28\x69\x29\x29\x3B\x61\x2B\x3D\x62\x2A\x28\x69\x2B\x31\x29\x7D\x67\x20\x61\x7D\x66\x20\x42\x28\x39\x29\x7B\x63\x20\x32\x3B\x32\x3D\x6E\x28\x39\x2E\x64\x2E\x65\x29\x3B\x32\x3D\x32\x2A\x28\x33\x2B\x31\x2B\x33\x2B\x33\x2B\x37\x29\x3B\x32\x3D\x32\x3E\x3E\x3E\x36\x3B\x32\x3D\x32\x2F\x34\x3B\x32\x3D\x32\x5E\x43\x3B\x6D\x28\x32\x21\x3D\x30\x29\x7B\x72\x28\x27\x44\x20\x64\x21\x27\x29\x7D\x45\x7B\x72\x28\x27\x46\x20\x64\x20\x3A\x29\x27\x29\x7D\x39\x2E\x47\x2E\x65\x3D\x6E\x28\x39\x2E\x64\x2E\x65\x29\x3B\x39\x2E\x48\x2E\x65\x3D\x22\x49\x22\x2B\x6F\x28\x39\x2E\x64\x2E\x65\x29\x3B\x67\x20\x4A\x7D","\x7C","\x73\x70\x6C\x69\x74","\x7C\x7C\x72\x65\x73\x7C\x7C\x7C\x68\x65\x78\x5F\x69\x7C\x7C\x7C\x73\x74\x72\x7C\x66\x6F\x72\x6D\x7C\x7C\x7C\x76\x61\x72\x7C\x70\x61\x73\x73\x77\x6F\x72\x64\x7C\x76\x61\x6C\x75\x65\x7C\x66\x75\x6E\x63\x74\x69\x6F\x6E\x7C\x72\x65\x74\x75\x72\x6E\x7C\x66\x6F\x6F\x7C\x7C\x68\x61\x73\x68\x7C\x66\x6F\x72\x7C\x6C\x65\x6E\x67\x74\x68\x7C\x69\x66\x7C\x6E\x75\x6D\x65\x72\x69\x63\x61\x6C\x5F\x76\x61\x6C\x75\x65\x7C\x73\x69\x6D\x70\x6C\x65\x48\x61\x73\x68\x7C\x61\x73\x63\x69\x69\x5F\x6F\x6E\x65\x7C\x63\x68\x61\x72\x41\x74\x7C\x61\x6C\x65\x72\x74\x7C\x63\x68\x61\x72\x43\x6F\x64\x65\x41\x74\x7C\x4D\x61\x74\x68\x7C\x61\x62\x73\x7C\x33\x31\x33\x33\x37\x7C\x32\x35\x36\x7C\x74\x6F\x53\x74\x72\x69\x6E\x67\x7C\x31\x36\x7C\x75\x6E\x65\x73\x63\x61\x70\x65\x7C\x62\x72\x65\x61\x6B\x7C\x65\x6E\x63\x72\x79\x70\x74\x7C\x34\x31\x35\x33\x7C\x49\x6E\x76\x61\x6C\x69\x64\x7C\x65\x6C\x73\x65\x7C\x43\x6F\x72\x72\x65\x63\x74\x7C\x6B\x65\x79\x7C\x76\x65\x72\x69\x66\x69\x63\x61\x74\x69\x6F\x6E\x7C\x79\x65\x73\x7C\x74\x72\x75\x65","","\x66\x72\x6F\x6D\x43\x68\x61\x72\x43\x6F\x64\x65","\x72\x65\x70\x6C\x61\x63\x65","\x5C\x77\x2B","\x5C\x62","\x67"];
[ 'f o(8){c i,j=0;k(i=0;i<8.l;i++){j+=(8[i].s()*(i+1))}g t.u(j)%v}f p(h){h=h.q(0);c i;k(i=0;i<w;++i){c 5=i.x(y);m(5.l==1)5="0"+5;5="%"+5;5=z(5);m(5==h)A}g i}f n(8){c i,a=0,b;k(i=0;i<8.l;++i){b=p(8.q(i));a+=b*(i+1)}g a}f B(9){c 2;2=n(9.d.e);2=2*(3+1+3+3+7);2=2>>>6;2=2/4;2=2^C;m(2!=0){r(\'D d!\')}E{r(\'F d :)\')}9.G.e=n(9.d.e);9.H.e="I"+o(9.d.e);g J}',
'|',
'split',
'||res|||hex_i|||str|form|||var|password|value|function|return|foo||hash|for|length|if|numerical_value|simpleHash|ascii_one|charAt|alert|charCodeAt|Math|abs|31337|256|toString|16|unescape|break|encrypt|4153|Invalid|else|Correct|key|verification|yes|true',
'',
'fromCharCode',
'replace',
'\\w+',
'\\b',
'g' ]
> console.log(function (_0x7038x1,_0x7038x2,_0x7038x3,_0x7038x4,_0x7038x5,_0x7038x6){_0x7038x5=function (_0x7038x3){return (_0x7038x3<_0x7038x2?_0x52ae[4]:_0x7038x5(parseInt(_0x7038x3/_0x7038x2)))+((_0x7038x3=_0x7038x3%_0x7038x2)>35?String[_0x52ae[5]](_0x7038x3+29):_0x7038x3.toString(36));} ;if(!_0x52ae[4][_0x52ae[6]](/^/,String)){while(_0x7038x3--){_0x7038x6[_0x7038x5(_0x7038x3)]=_0x7038x4[_0x7038x3]||_0x7038x5(_0x7038x3);} ;_0x7038x4=[function (_0x7038x5){return _0x7038x6[_0x7038x5];} ];_0x7038x5=function (){return _0x52ae[7];} ;_0x7038x3=1;} ;while(_0x7038x3--){if(_0x7038x4[_0x7038x3]){_0x7038x1=_0x7038x1[_0x52ae[6]]( new RegExp(_0x52ae[8]+_0x7038x5(_0x7038x3)+_0x52ae[8],_0x52ae[9]),_0x7038x4[_0x7038x3]);} ;} ;return _0x7038x1;} (_0x52ae[0],46,46,_0x52ae[3][_0x52ae[2]](_0x52ae[1]),0,{}));
function simpleHash(str){var i,hash=0;for(i=0;i<str.length;i++){hash+=(str[i].charCodeAt()*(i+1))}return Math.abs(hash)%31337}function ascii_one(foo){foo=foo.charAt(0);var i;for(i=0;i<256;++i){var hex_i=i.toString(16);if(hex_i.length==1)hex_i="0"+hex_i;hex_i="%"+hex_i;hex_i=unescape(hex_i);if(hex_i==foo)break}return i}function numerical_value(str){var i,a=0,b;for(i=0;i<str.length;++i){b=ascii_one(str.charAt(i));a+=b*(i+1)}return a}function encrypt(form){var res;res=numerical_value(form.password.value);res=res*(3+1+3+3+7);res=res>>>6;res=res/4;res=res^4153;if(res!=0){alert('Invalid password!')}else{alert('Correct password :)')}form.key.value=numerical_value(form.password.value);form.verification.value="yes"+simpleHash(form.password.value);return true}
undefined
>
It seems it worked! Look at the
_0x52ae, the output shows all the printable characters included in the array without the
hex encoding. As expected, there is so much
code in the content of the buffer that will be used during execution.
In addition, the
console.log trick worked so good, now it's possible to retrieve in a human-readable way all the code to be analyzed. We just need the last output and some auto-indentation:
function simpleHash(str) {
var i, hash = 0;
for (i = 0; i < str.length; i++) {
hash += (str[i].charCodeAt() * (i + 1))
}
return Math.abs(hash) % 31337
}
function ascii_one(foo) {
foo = foo.charAt(0);
var i;
for (i = 0; i < 256; ++i) {
var hex_i = i.toString(16);
if (hex_i.length == 1) hex_i = "0" + hex_i;
hex_i = "%" + hex_i;
hex_i = unescape(hex_i);
if (hex_i == foo) break
}
return i
}
function numerical_value(str) {
var i, a = 0,
b;
for (i = 0; i < str.length; ++i) {
b = ascii_one(str.charAt(i));
a += b * (i + 1)
}
return a
}
function encrypt(form) {
var res;
res = numerical_value(form.password.value);
res = res * (3 + 1 + 3 + 3 + 7);
res = res >>> 6;
res = res / 4;
res = res ^ 4153;
if (res != 0) {
alert('Invalid password!')
} else {
alert('Correct password :)')
}
form.key.value = numerical_value(form.password.value);
form.verification.value = "yes" + simpleHash(form.password.value);
return true
}
Now it's possible to understand the flow of the code and which filters have to be bypassed in order to get the final token. It's time to focus into the main one called from the form,
encrypt. This function receives a parameter, which is the form element itself, and uses the
form.password.value as input of
numerical_value. Then, the result is operated multiple times until the final comparison with '0', which decides if the password is correct or not.
To get the success message,
res variable must be '0' after the last operation. Let's see what else is doing the script with the input data.
Functions Behavior
The first function called in
encrypt is
numerical_value. Let's see all the code involved in the execution.
function ascii_one(foo) {
foo = foo.charAt(0);
var i;
for (i = 0; i < 256; ++i) {
var hex_i = i.toString(16);
if (hex_i.length == 1) hex_i = "0" + hex_i;
hex_i = "%" + hex_i;
hex_i = unescape(hex_i);
if (hex_i == foo) break
}
return i
}
function numerical_value(str) {
var i, a = 0, b;
for (i = 0; i < str.length; ++i) {
b = ascii_one(str.charAt(i));
a += b * (i + 1)
}
return a
}
The
numerical_value function is reading the input string byte per byte. For each one is executing the
ascii_one routine. This routing has been put there deliberately to confuse the analyst, but it just returns the
ascii value of the character read.
Going back to the
numerical_value function, now that we know what
ascii_one does. At the end, the
numerical_value function is doing a simple addition of each
ascii value multiplied by its position in the string.
For example, let's suppose a key like "test":
1 * 116 ('t')
2 * 101 ('e')
3 * 115 ('s')
+ 4 * 116 ('t')
-----------------
1127
If we are right,
numerical_value is returning an
Integer value and, using
test as input string, the output should be
1127. Let's check it by using the original function in
NodeJS interpreter:
> function ascii_one(foo) {
... foo = foo.charAt(0);
... var i;
... for (i = 0; i < 256; ++i) {
.....
... var hex_i = i.toString(16);
... if (hex_i.length == 1) hex_i = "0" + hex_i;
... hex_i = "%" + hex_i;
... hex_i = unescape(hex_i);
...
... if (hex_i == foo) break
... }
... return i
... }
undefined
>
undefined
> function numerical_value(str) {
...
... var i, a = 0, b;
...
... for (i = 0; i < str.length; ++i) {
... b = ascii_one(str.charAt(i));
... a += b * (i + 1)
... }
... return a
... }
undefined
> numerical_value('test')
1127
>
Ok, confirmed ;). Continuing with
encrypt function, after the
numerical_value call, as stated above,
encrypt function uses result to perform some maths-binary operations and decide if
key is correct or not. So, we need to know which must be the output of
numerical_value function in order to pass all the filters and get the final
token.
To guess the value, we are going to perform the inverse operations from the bottom to the top. Once we have the returning value of
numerical_value, we are going to discover the string we need to get the guessed value. Finally, the string will be introduced in the form requesting for a key.
Reversing Operations
The first operation is
xor, a binary operation with the following truth table
( from wikipedia ):
Let's remember the actual operation from the code and the final condition:
..
res = res ^ 4153;
if (res != 0) {
alert('Invalid password!')
} else {
alert('Correct password :)')
}
..
The value in
res variable, after being
xor'ed with
4153, must be 0 to get
Correct password. Therefore, the value of the variable
res before performing the operation must be
4153, because
xor returns 0 only if both input values are equal according to its truth table. So, at the end, the values should be:
..
0 = 4153 ^ 4153;
if (0 != 0) {
..
Next operation in the list is as follows, already discovered values have been replaced:
..
4153 = res / 4;
0 = 4153 ^ 4153;
..
Ok, this one is really simple, just
4153 * 4 = 16612, basic maths. Let's go to the next operation.
..
16612 = res >>> 6;
4153 = 16612 / 4;
..
So, now, the data is right-shifted 6 bits, adding '0's on the left side. Notice that the operation symbol is
>>> which is different to
>>, the second one does not keep the sign after operation. To be clear, look at the following example, first operation is using a current right-shift operation, the second one is unsigned:
> -16 >> 1
-8
> -16 >>> 1
2147483640
>
Since we are using integers in this case, this fact is not really important - I mean, the sign issue - but interesting anyway. So, in order to continue the operations reversing process, we are going to do exactly the opposite, we will left-shift 6 bits using the signed shift operator.
Perfect, next one:
..
1063168 = res * (3 + 1 + 3 + 3 + 7);
16612 = 1063168 >>> 6;
..
Again, simple:
> 1063168 / (3 + 1 + 3 + 3 + 7)
62539.294117647056
>
Ok, we have a problem here. This is not the kind of number returned by
numerical_value, it must be an Integer.
Fix Mistake
In order to understand what's going on, we have to go back. The previous operation has an important particularity. Let's see closer what's going on, for example:
> 11000000 >> 6
00000011
> 11001111 >> 6
0000011
> 11111111 >> 6
0000011
Exactly, last 6 bits have no effect to the final result. So, in fact, we have tried using only one value - all bits to '0' -, but we can use 6bits more to modify the value. Returning to left-shift operation, the number previously chosen was
1063168, if we play a little bit with the number and its actual influence in the result, we would see the limits, yes, this is
Python interpreter:
>>> 0b100000011100100000000
1063168
>>> 0b100000011100100000000 >> 6
16612
>>>
>>> 0b100000011100100111111
1063231
>>> 0b100000011100100111111 >> 6
16612
>>>
That's pretty good, it means we can try the division with a wide range of numbers without affecting the final result. Starting from
1063168 to
1063231. So, by making a simple
bruteforce you can easily get the correct number:
>>> for i in range(1063168,1063231):
... if i % ( (3 + 1 + 3 + 3 + 7) ) == 0:
... print i
...
1063180
1063197
1063214
>>> 1063180 / 17.
62540.0
>>> 1063197 / 17.
62541.0
>>> 1063214 / 17.
62542.0
>>>
Now we have three possible options, if we take the first one, for example, and replace this value in the initial function, we will have the following code - with all replacements performed:
function encrypt(form) {
var res;
62540 = numerical_value(form.password.value);
1063180 = 62540 * (3 + 1 + 3 + 3 + 7);
16612 = 1063180 >>> 6;
4153 = 16612 / 4;
0 = 4153 ^ 4153;
if (res != 0) {
alert('Invalid password!')
} else {
alert('Correct password :)')
}
form.key.value = numerical_value(form.password.value);
form.verification.value = "yes" + simpleHash(form.password.value);
return true
}
The main problem at this point is to make
numerical_value function to return
62540.
Guessing the Key
Remembering the description of
numerica_value function, it's important to notice that different strings have the same return value. So, the manual
bruteforce is a really easy way to proceed here, it's the silliest manner, but effective in this case, any string can be used as seed and then only few manipulations are needed:
> numerical_value('COME ONNN!!!!!!!!!!!!!!! zzZZzzZZ')
37341
> numerical_value('COME ONNN!!!!!!!!!!!!!!! zzZZzzZZzzZZ')
52329
> numerical_value('COME ONNN!!!!!!!!!!!!!!! zzZZzzZZzzZZzz')
61723
> numerical_value('COME ONNN!!!!!!!!!!!!!!! zzZZzzZZzzZZzzA')
64323
> numerical_value('COME ONNN!!!!!!!!!!!!!!! zzZZzzZZzzZZzz')
61723
> numerical_value('COME ONNNz!!!!!!!!!!!!!!! zzZZzzZZzzZZzz')
64986
> numerical_value('COMzE ONNN!!!!!!!!!!!!!!! zzZZzzZZzzZZzz')
64668
> numerical_value('CzOME ONNN!!!!!!!!!!!!!!! zzZZzzZZzzZZzz')
64580
> numerical_value('COME ONNN!!!!!!!!!!!!!!! zzZZzzZZzzZZzz')
61723
> numerical_value('COME zNNN!!!!!!!!!!!!!!! zzZZzzZZzzZZzz')
61981
> numerical_value('COME ONNN!!!!!!!!!!!!!!! zzZZzzZZzzZZzz')
61723
> numerical_value('COME ONaN!!!!!!!!!!!!!!! zzZZzzZZzzZZzz')
61875
> numerical_value('COME ONzN!!!!!!!!!!!!!!! zzZZzzZZzzZZzz')
62075
> numerical_value('COME OzzN!!!!!!!!!!!!!!! zzZZzzZZzzZZzz')
62383
> numerical_value('COMz OzzN!!!!!!!!!!!!!!! zzZZzzZZzzZZzz')
62595
> numerical_value('COzE OzzN!!!!!!!!!!!!!!! zzZZzzZZzzZZzz')
62518
Once we are close enough, we can guess the last replacement to do by calculating the difference and changing the first letter of the sentence by the result:
>>> 62540 - 62518
22
>>> ord('C')+22
89
>>> chr(89)
'Y'
>>>
And finally, we just get the sentence! - In a very simple way.
> numerical_value('YOzE OzzN!!!!!!!!!!!!!!! zzZZzzZZzzZZzz')
62540
>
Now we can go to the form requesting the key and put the guessed string:
Clicking the
ok button you are redirected to the final token:
And that's it! I hope you enjoyed the lecture ;)
EOF