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.
<!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>
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,{}));
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.
# 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 >
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 }
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.
1 * 116 ('t') 2 * 101 ('e') 3 * 115 ('s') + 4 * 116 ('t') ----------------- 1127
> 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 >
.. 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 >
> 16612 << 6 1063168 >
.. 1063168 = res * (3 + 1 + 3 + 3 + 7); 16612 = 1063168 >>> 6; ..Again, simple:
> 1063168 / (3 + 1 + 3 + 3 + 7) 62539.294117647056 >
> 11000000 >> 6 00000011 > 11001111 >> 6 0000011 > 11111111 >> 6 0000011
>>> 0b100000011100100000000 1063168 >>> 0b100000011100100000000 >> 6 16612 >>> >>> 0b100000011100100111111 1063231 >>> 0b100000011100100111111 >> 6 16612 >>>
>>> 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 >>>
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.
> 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
>>> 62540 - 62518 22 >>> ord('C')+22 89 >>> chr(89) 'Y' >>>
> numerical_value('YOzE OzzN!!!!!!!!!!!!!!! zzZZzzZZzzZZzz') 62540 >