NoConName prequals - 2013

During the last weekend the first NoConName (NcN) prequals were performed, this is a short writeup of Level1 because it was the one with most interesting issues. Maybe further levels will come in the future.

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:

  1. Level 1: Obfuscated Javascript with some maths behind
  2. Level 2: An APK to analyze.
  3. 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.
> 16612 << 6
1063168
>
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