[Challenge]: Javascript Encryption Nr. 3 MEDIUM @hack.me


Challenge:  Javascript Encryption Nr. 3 MEDIUM
Author: VeNoMouS
Website: hack.me
Link: https://hack.me/103049/javascript-encryption-nr-3-medium.html
Description: Since my last Hackmes got a lot of appreciation I decided to create a new one.
This one is going to be a little bit more difficult than my easy ones. If you want a really easy Hackme I’d recommend checking out my last 2 Hackmes since they got a lot of positive feedback. This Hackme isn’t too hard though. It’s mainly for fun and wasting a little bit of time. I’m hoping that you will have fun with this!
Hint: There are a lot of flaws in the security of this Hackme and it is still kinda easy to complete.
PS: There is a cheating easter egg.

Solution 1 – Reversing:

This challenge uses a javascript code to validate the login, no HTTP requests are present. For this reason, I analyze the obfuscated inline javascript code:

var _0xf554=['\x56\x47\x68\x70\x63\x79\x42\x70\x63\x79\x42\x6a\x61\x47\x56\x68\x64\x47\x6c\x75\x5a\x79\x41\x2b\x4c\x6a\x34\x3d','\x64\x47\x56\x34\x64\x45\x4e\x76\x62\x6e\x52\x6c\x62\x6e\x51\x3d','\x64\x58\x4e\x6c\x63\x6d\x35\x68\x62\x57\x55\x3d','\x5a\x32\x56\x30\x52\x57\x78\x6c\x62\x57\x56\x75\x64\x45\x4a\x35\x53\x57\x51\x3d','\x63\x47\x46\x7a\x63\x33\x64\x76\x63\x6d\x51\x3d','\x63\x33\x56\x69\x62\x57\x6c\x30','\x59\x58\x52\x30\x5a\x57\x31\x77\x64\x48\x4d\x3d','\x64\x6d\x46\x73\x64\x57\x55\x3d','\x56\x33\x4a\x76\x62\x6d\x63\x67\x64\x58\x4e\x6c\x63\x6d\x35\x68\x62\x57\x55\x75','\x62\x6b\x5a\x57','\x4c\x55\x73\x35\x55\x56\x35\x7a\x4a\x79\x51\x3d','\x56\x33\x4a\x76\x62\x6d\x63\x67\x63\x47\x46\x7a\x63\x33\x64\x76\x63\x6d\x51\x75','\x64\x6c\x6c\x49','\x51\x57\x4e\x6a\x5a\x58\x4e\x7a\x49\x47\x64\x79\x59\x57\x35\x30\x5a\x57\x51\x68\x44\x51\x70\x55\x61\x47\x46\x75\x61\x79\x42\x35\x62\x33\x55\x67\x5a\x6d\x39\x79\x49\x47\x4e\x76\x62\x58\x42\x73\x5a\x58\x52\x70\x62\x6d\x63\x67\x62\x58\x6b\x67\x53\x47\x46\x6a\x61\x32\x31\x6c\x49\x44\x77\x7a','\x57\x57\x39\x31\x49\x47\x68\x68\x64\x6d\x55\x67\x62\x6d\x38\x67\x64\x48\x4a\x70\x5a\x58\x4d\x67\x62\x47\x56\x6d\x64\x43\x34\x67\x56\x48\x4a\x35\x49\x47\x46\x6e\x59\x57\x6c\x75\x44\x51\x70\x54\x62\x33\x4a\x79\x65\x53\x41\x36\x4b\x41\x3d\x3d','\x5a\x47\x6c\x7a\x59\x57\x4a\x73\x5a\x57\x51\x3d','\x52\x48\x70\x46'];(function(_0x5e576c,_0x226287){var _0x28e3a0=function(_0x4cfa3b){while(--_0x4cfa3b){_0x5e576c['\x70\x75\x73\x68'](_0x5e576c['\x73\x68\x69\x66\x74']());}};_0x28e3a0(++_0x226287);}(_0xf554,0x68));var _0x4f55=function(_0x33d2fa,_0x273cb1){_0x33d2fa=_0x33d2fa-0x0;var _0x4684e9=_0xf554[_0x33d2fa];if(_0x4f55['\x69\x6e\x69\x74\x69\x61\x6c\x69\x7a\x65\x64']===undefined){(function(){var _0x66323b=Function('\x72\x65\x74\x75\x72\x6e\x20\x28\x66\x75\x6e\x63\x74\x69\x6f\x6e\x20\x28\x29\x20'+'\x7b\x7d\x2e\x63\x6f\x6e\x73\x74\x72\x75\x63\x74\x6f\x72\x28\x22\x72\x65\x74\x75\x72\x6e\x20\x74\x68\x69\x73\x22\x29\x28\x29'+'\x29\x3b');var _0x40cacf=_0x66323b();var _0x129429='\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x2b\x2f\x3d';_0x40cacf['\x61\x74\x6f\x62']||(_0x40cacf['\x61\x74\x6f\x62']=function(_0x459478){var _0x317703=String(_0x459478)['\x72\x65\x70\x6c\x61\x63\x65'](/=+$/,'');for(var _0x5ea563=0x0,_0xb5801d,_0x1a56fc,_0x1d5ca9=0x0,_0x5eaef2='';_0x1a56fc=_0x317703['\x63\x68\x61\x72\x41\x74'](_0x1d5ca9++);~_0x1a56fc&&(_0xb5801d=_0x5ea563%0x4?_0xb5801d*0x40+_0x1a56fc:_0x1a56fc,_0x5ea563++%0x4)?_0x5eaef2+=String['\x66\x72\x6f\x6d\x43\x68\x61\x72\x43\x6f\x64\x65'](0xff&_0xb5801d>>(-0x2*_0x5ea563&0x6)):0x0){_0x1a56fc=_0x129429['\x69\x6e\x64\x65\x78\x4f\x66'](_0x1a56fc);}return _0x5eaef2;});}());_0x4f55['\x62\x61\x73\x65\x36\x34\x44\x65\x63\x6f\x64\x65\x55\x6e\x69\x63\x6f\x64\x65']=function(_0x1df773){var _0x672477=atob(_0x1df773);var _0x40d94a=[];for(var _0x2b7db8=0x0,_0x5017fe=_0x672477['\x6c\x65\x6e\x67\x74\x68'];_0x2b7db8<_0x5017fe;_0x2b7db8++){_0x40d94a+='\x25'+('\x30\x30'+_0x672477['\x63\x68\x61\x72\x43\x6f\x64\x65\x41\x74'](_0x2b7db8)['\x74\x6f\x53\x74\x72\x69\x6e\x67'](0x10))['\x73\x6c\x69\x63\x65'](-0x2);}return decodeURIComponent(_0x40d94a);};_0x4f55['\x64\x61\x74\x61']={};_0x4f55['\x69\x6e\x69\x74\x69\x61\x6c\x69\x7a\x65\x64']=!![];}if(_0x4f55['\x64\x61\x74\x61'][_0x33d2fa]===undefined){_0x4684e9=_0x4f55['\x62\x61\x73\x65\x36\x34\x44\x65\x63\x6f\x64\x65\x55\x6e\x69\x63\x6f\x64\x65'](_0x4684e9);_0x4f55['\x64\x61\x74\x61'][_0x33d2fa]=_0x4684e9;}else{_0x4684e9=_0x4f55['\x64\x61\x74\x61'][_0x33d2fa];}return _0x4684e9;};var a=0x3;function validate(){var _0xcfdfd3={'\x56\x49\x4f':function _0x4e2f71(_0x4d7bae,_0x232d6f){return _0x4d7bae<_0x232d6f;},'\x6e\x46\x56':function _0x505670(_0xc00fbb,_0x4ffb9e){return _0xc00fbb!=_0x4ffb9e;},'\x51\x45\x66':function _0x206a33(_0x19b7d2,_0x4ed2a3){return _0x19b7d2(_0x4ed2a3);},'\x76\x59\x48':function _0x4638b2(_0x469ff1,_0xcc7fd3){return _0x469ff1(_0xcc7fd3);},'\x44\x7a\x45':function _0x372598(_0x2b4e3c,_0x44f0d0){return _0x2b4e3c(_0x44f0d0);}};u=document['\x67\x65\x74\x45\x6c\x65\x6d\x65\x6e\x74\x42\x79\x49\x64'](_0x4f55('0x0'));p=document[_0x4f55('0x1')](_0x4f55('0x2'));b=document[_0x4f55('0x1')](_0x4f55('0x3'));v=document[_0x4f55('0x1')](_0x4f55('0x4'));_0xcfdfd3['\x56\x49\x4f'](0x0,a)?('\x72\x6f\x6f\x74'!=u[_0x4f55('0x5')]?(alert(_0x4f55('0x6')),a--):_0xcfdfd3[_0x4f55('0x7')](_0x4f55('0x8'),p[_0x4f55('0x5')])?(_0xcfdfd3['\x51\x45\x66'](alert,_0x4f55('0x9')),a--):_0xcfdfd3[_0x4f55('0xa')](alert,_0x4f55('0xb')),0x0>=a&&(a=0x0,_0xcfdfd3[_0x4f55('0xa')](alert,_0x4f55('0xc')),u['\x64\x69\x73\x61\x62\x6c\x65\x64']=!0x0,p['\x64\x69\x73\x61\x62\x6c\x65\x64']=!0x0,b[_0x4f55('0xd')]=!0x0)):(_0xcfdfd3[_0x4f55('0xe')](alert,_0x4f55('0xf')),a=-0x539);v[_0x4f55('0x10')]=a;};

Without indentation, the code is unreadable. To mitigate the obfuscation I use JSNICE to make the code more comprehensible. This is the result:

/** @type {Array} */
var _0xf554 = ["VGhpcyBpcyBjaGVhdGluZyA+Lj4=", 
               "dGV4dENvbnRlbnQ=", 
               "dXNlcm5hbWU=",
               "Z2V0RWxlbWVudEJ5SWQ=", 
               "cGFzc3dvcmQ=", 
               "c3VibWl0", 
               "YXR0ZW1wdHM=", 
               "dmFsdWU=", 
               "V3JvbmcgdXNlcm5hbWUu", 
               "bkZW", 
               "LUs5UV5zJyQ=", 
               "V3JvbmcgcGFzc3dvcmQu", 
               "dllI",              
               "QWNjZXNzIGdyYW50ZWQhDQpUaGFuayB5b3UgZm9yIGNvbXBsZXRpbmcgbXkgSGFja21lIDwz",
               "WW91IGhhdmUgbm8gdHJpZXMgbGVmdC4gVHJ5IGFnYWluDQpTb3JyeSA6KA==",                         "ZGlzYWJsZWQ=", 
               "RHpF"];
(function(paths, opt_attributes) {
  /**
   * @param {number} val
   * @return {undefined}
   */
  var setter = function(val) {
    for (;--val;) {
      paths["push"](paths["shift"]());
    }
  };
  setter(++opt_attributes);
})(_0xf554, 104);
/**
 * @param {string} i
 * @param {?} dataAndEvents
 * @return {?}
 */
var _0x4f55 = function(i, dataAndEvents) {
  /** @type {number} */
  i = i - 0;
  var member = _0xf554[i];
  if (_0x4f55["initialized"] === undefined) {
    (function() {
      var templateFunc = Function("return (function () " + '{}.constructor("return this")()' + ");");
      var html = templateFunc();
      /** @type {string} */
      var classNames = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
      if (!html["atob"]) {
        /**
         * @param {?} opt_message
         * @return {?}
         */
        html["atob"] = function(opt_message) {
          var pagerNum = String(opt_message)["replace"](/=+$/, "");
          /** @type {number} */
          var bc = 0;
          var bs;
          var buffer;
          /** @type {number} */
          var _0x1d5ca9 = 0;
          /** @type {string} */
          var optsData = "";
          for (;buffer = pagerNum["charAt"](_0x1d5ca9++);~buffer && (bs = bc % 4 ? bs * 64 + buffer : buffer, bc++ % 4) ? optsData += String["fromCharCode"](255 & bs >> (-2 * bc & 6)) : 0) {
            buffer = classNames["indexOf"](buffer);
          }
          return optsData;
        };
      }
    })();
    /**
     * @param {?} blob
     * @return {?}
     */
    _0x4f55["base64DecodeUnicode"] = function(blob) {
      var data = atob(blob);
      /** @type {Array} */
      var sign = [];
      /** @type {number} */
      var i = 0;
      var length = data["length"];
      for (;i < length;i++) {
        sign += "%" + ("00" + data["charCodeAt"](i)["toString"](16))["slice"](-2);
      }
      return decodeURIComponent(sign);
    };
    _0x4f55["data"] = {};
    /** @type {boolean} */
    _0x4f55["initialized"] = !![];
  }
  if (_0x4f55["data"][i] === undefined) {
    member = _0x4f55["base64DecodeUnicode"](member);
    _0x4f55["data"][i] = member;
  } else {
    member = _0x4f55["data"][i];
  }
  return member;
};
/** @type {number} */
var a = 3;
/**
 * @return {undefined}
 */
function validate() {
  var webshims = {
    /**
     * @param {(boolean|number|string)} attrs
     * @param {(boolean|number|string)} truncatedState
     * @return {?}
     */
    "VIO" : function validate(attrs, truncatedState) {
      return attrs < truncatedState;
    },
    /**
     * @param {number} expected
     * @param {number} actual
     * @return {?}
     */
    "nFV" : function notEqual(expected, actual) {
      return expected != actual;
    },
    /**
     * @param {?} callback
     * @param {?} error
     * @return {?}
     */
    "QEf" : function validate(callback, error) {
      return callback(error);
    },
    /**
     * @param {?} callback
     * @param {?} error
     * @return {?}
     */
    "vYH" : function validate(callback, error) {
      return callback(error);
    },
    /**
     * @param {?} callback
     * @param {?} error
     * @return {?}
     */
    "DzE" : function validate(callback, error) {
      return callback(error);
    }
  };
  u = document["getElementById"](_0x4f55("0x0"));
  p = document[_0x4f55("0x1")](_0x4f55("0x2"));
  b = document[_0x4f55("0x1")](_0x4f55("0x3"));
  v = document[_0x4f55("0x1")](_0x4f55("0x4"));
  if (webshims["VIO"](0, a)) {
    if ("root" != u[_0x4f55("0x5")]) {
      alert(_0x4f55("0x6"));
      a--;
    } else {
      if (webshims[_0x4f55("0x7")](_0x4f55("0x8"), p[_0x4f55("0x5")])) {
        webshims["QEf"](alert, _0x4f55("0x9"));
        a--;
      } else {
        webshims[_0x4f55("0xa")](alert, _0x4f55("0xb"));
      }
    }
    if (0 >= a) {
      /** @type {number} */
      a = 0;
      webshims[_0x4f55("0xa")](alert, _0x4f55("0xc"));
      /** @type {boolean} */
      u["disabled"] = true;
      /** @type {boolean} */
      p["disabled"] = true;
      /** @type {boolean} */
      b[_0x4f55("0xd")] = true;
    }
  } else {
    webshims[_0x4f55("0xe")](alert, _0x4f55("0xf"));
    /** @type {number} */
    a = -1337;
  }
  v[_0x4f55("0x10")] = a;
}
;

The obfuscator online tools, generally, obfuscate the strings with base64 encoding. By analyzing the code I see the _0xf554 array that contains the strings. To decode the content I write a little piece of javascript code inside my javascript console debug:

for (i = 0; i < _0xf554.length; i++) { console.log(atob(_0xf554[i]));}

Great! I got the decoded array!

username
getElementById
password
submit
attempts
value
Wrong username.
nFV
-K9Q^s'$
Wrong password.
vYH
Access granted!
 
Thank you for completing my Hackme .>
textContent

As you can see, the array contains javascript functions names and application strings. Now the code has more sense!

The obfuscated code contains a function called validate(). Probably this function verifies the password.

To retrieve its implementation I write, inside the javascript console, the command validate.toString():

function validate() {
    var _0xcfdfd3 = {
        '\x56\x49\x4f': function _0x4e2f71(_0x4d7bae, _0x232d6f) {
            return _0x4d7bae < _0x232d6f;
        },
        '\x6e\x46\x56': function _0x505670(_0xc00fbb, _0x4ffb9e) {
            return _0xc00fbb != _0x4ffb9e;
        },
        '\x51\x45\x66': function _0x206a33(_0x19b7d2, _0x4ed2a3) {
            return _0x19b7d2(_0x4ed2a3);
        },
        '\x76\x59\x48': function _0x4638b2(_0x469ff1, _0xcc7fd3) {
            return _0x469ff1(_0xcc7fd3);
        },
        '\x44\x7a\x45': function _0x372598(_0x2b4e3c, _0x44f0d0) {
            return _0x2b4e3c(_0x44f0d0);
        }
    };
    u = document['\x67\x65\x74\x45\x6c\x65\x6d\x65\x6e\x74\x42\x79\x49\x64'](_0x4f55('0x0'));
    p = document[_0x4f55('0x1')](_0x4f55('0x2'));
    b = document[_0x4f55('0x1')](_0x4f55('0x3'));
    v = document[_0x4f55('0x1')](_0x4f55('0x4'));
    _0xcfdfd3['\x56\x49\x4f'](0x0, a) ? ('\x72\x6f\x6f\x74' != u[_0x4f55('0x5')] ? (alert(_0x4f55('0x6')), a--) : _0xcfdfd3[_0x4f55('0x7')](_0x4f55('0x8'), p[_0x4f55('0x5')]) ? (_0xcfdfd3['\x51\x45\x66'](alert, _0x4f55('0x9')), a--) : _0xcfdfd3[_0x4f55('0xa')](alert, _0x4f55('0xb')), 0x0 >= a && (a = 0x0, _0xcfdfd3[_0x4f55('0xa')](alert, _0x4f55('0xc')), u['\x64\x69\x73\x61\x62\x6c\x65\x64'] = !0x0, p['\x64\x69\x73\x61\x62\x6c\x65\x64'] = !0x0, b[_0x4f55('0xd')] = !0x0)) : (_0xcfdfd3[_0x4f55('0xe')](alert, _0x4f55('0xf')), a = -0x539);
    v[_0x4f55('0x10')] = a;
}

Below my consideration on the content of this function:

  • p = identifies the password HTML field( document[_0x4f55(‘0x1’)](_0x4f55(‘0x2’)) is the obfuscated code of document[“getElementById”](“password”))
  • _0x4f55(‘0x5’) = identifies the value property ( p[_0x4f55(‘0x5’)] corresponds to p[“value”] )

The part of codes that uses the value property of password fields is:

_0xcfdfd3[_0x4f55(‘0x7’)](_0x4f55(‘0x8’), p[_0x4f55(‘0x5’)]) -> nFV(“-K9Q^s’$”, p[“value”])

Then, the password is -K9Q^s’$