ben-sb / obfuscator-io-deobfuscator

A deobfuscator for scripts obfuscated by Obfuscator.io
https://obf-io.deobfuscate.io
Apache License 2.0
275 stars 61 forks source link

Feature request: Remove canary traps/tamper proofing/self defense #34

Open anka-213 opened 1 week ago

anka-213 commented 1 week ago

The obfuscator can add a trap that checks if the code has been auto-formatted by checking for newlines in a few function definitions. For example, in the payload for the recent polyfill dot io supply chain attack there is a snippet that with better variable names looks like this:

        const canary_checker_object = function (_0x51f4b1) {
          this.cvuinS = _0x51f4b1;
          this.fyrkaT = [1, 0, 0];
          // This is the canary that is checked for newlines and spaces
          this.canary = function(){return'newState';};
          // This is the regex that checks it
          this.IZqJeb = "\\w+ *\\(\\) *{\\w+ *";
          this.kmcauN = "['|\"].+['|\"];? *}";
        };
        canary_checker_object.prototype.check_canary = function () {
          const _0x551808 = new RegExp(this.IZqJeb + this.kmcauN);
          const _0xec967d = _0x551808.test(this.canary.toString()) ? --this.fyrkaT[1] : --this.fyrkaT[0];
          return this.KiEDhy(_0xec967d);
        };
        canary_checker_object.prototype.KiEDhy = function (_0xf8c99b) {
          if (!Boolean(~_0xf8c99b)) {
            return _0xf8c99b;
          }
          return this.canary_trap(this.cvuinS);
        };
        canary_checker_object.prototype.canary_trap = function (_0xe722ea) {
          let _0x149a72 = 0;
          // This is an infinitely allocating infinite loop
          for (let _0x3fe735 = this.fyrkaT.length; _0x149a72 < _0x3fe735; _0x149a72++) {
            this.fyrkaT.push(Math.round(Math.random()));
            _0x3fe735 = this.fyrkaT.length;
          }
          return _0xe722ea(this.fyrkaT[0]);
        };
        new canary_checker_object(decode_string).check_canary();

which checks if the function canary contains any newlines and if it doesn't, it causes an infinite loop that expands the array fyrkaT indefinitely with random numbers.

After removing dead code, most of the code remaining are just different traps for causing infinite loops:

Full code with the entire trap but the actual payload removed ```javascript // This function calls the a0_0xa0b8 function which checks for tampering and causes infinite loops or incorrect values // If the incorrect value is returned, this function loops infinitely (function (_0x10c19b, _0x5753e1) { const _0x208c16 = _0x10c19b(); while (true) { try { const _0x2ba9d2 = -parseInt(a0_0xa0b8(314, 'c@Hn')) / 1 * (-parseInt(a0_0xa0b8(275, 'OZos')) / 2) + parseInt(a0_0xa0b8(295, 'p6OJ')) / 3 * (parseInt(a0_0xa0b8(441, '!(Jy')) / 4) + parseInt(a0_0xa0b8(475, 'c@Hn')) / 5 + parseInt(a0_0xa0b8(418, 'Lvkz')) / 6 + -parseInt(a0_0xa0b8(428, 'cWDm')) / 7 * (parseInt(a0_0xa0b8(545, 'OZos')) / 8) + parseInt(a0_0xa0b8(669, 'llA^')) / 9 + -parseInt(a0_0xa0b8(480, 'OZos')) / 10; if (_0x2ba9d2 === _0x5753e1) { break; } else { _0x208c16.push(_0x208c16.shift()); } } catch (_0x4f2418) { _0x208c16.push(_0x208c16.shift()); } } })(a0_0x5173, 550813); // Removed some actual payload function a0_0xa0b8(_0x6db2f9, _0x437aca) { const _0x58e91a = a0_0x5173(); a0_0xa0b8 = function (_0xf2c0d2, _0x1eeda0) { _0xf2c0d2 = _0xf2c0d2 - 257; let _0x530d4e = _0x58e91a[_0xf2c0d2]; if (a0_0xa0b8.UWwKby === undefined) { var _0x562627 = function (_0x2686a4) { let _0x370cb1 = ''; let _0x58f4ed = ''; // This is the source of the current function. It is checked for newlines let _0x6790dd = _0x370cb1 + _0x562627; let _0x4e439b = 0; let _0x273cb0; let _0x418acb; for (let _0xa86a1c = 0; _0x418acb = _0x2686a4.charAt(_0xa86a1c++); ~_0x418acb && (_0x273cb0 = _0x4e439b % 4 ? _0x273cb0 * 64 + _0x418acb : _0x418acb, _0x4e439b++ % 4) ? _0x370cb1 += _0x6790dd.charCodeAt(_0xa86a1c + 10) - 10 !== 0 ? String.fromCharCode(255 & _0x273cb0 >> (-2 * _0x4e439b & 6)) : _0x4e439b : 0) { _0x418acb = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/='.indexOf(_0x418acb); } let _0x166c6e = 0; for (let _0x3e61bd = _0x370cb1.length; _0x166c6e < _0x3e61bd; _0x166c6e++) { _0x58f4ed += '%' + ('00' + _0x370cb1.charCodeAt(_0x166c6e).toString(16)).slice(-2); } return decodeURIComponent(_0x58f4ed); }; const _0x47f1dd = function (_0xc5bde6, _0x44884f) { let _0x421af5 = []; let _0x705d3f = 0; let _0x16ba4e; let _0x4f31ee = ''; _0xc5bde6 = _0x562627(_0xc5bde6); let _0x29df4f; for (_0x29df4f = 0; _0x29df4f < 256; _0x29df4f++) { _0x421af5[_0x29df4f] = _0x29df4f; } for (_0x29df4f = 0; _0x29df4f < 256; _0x29df4f++) { _0x705d3f = (_0x705d3f + _0x421af5[_0x29df4f] + _0x44884f.charCodeAt(_0x29df4f % _0x44884f.length)) % 256; _0x16ba4e = _0x421af5[_0x29df4f]; _0x421af5[_0x29df4f] = _0x421af5[_0x705d3f]; _0x421af5[_0x705d3f] = _0x16ba4e; } _0x29df4f = 0; _0x705d3f = 0; for (let _0x235057 = 0; _0x235057 < _0xc5bde6.length; _0x235057++) { _0x29df4f = (_0x29df4f + 1) % 256; _0x705d3f = (_0x705d3f + _0x421af5[_0x29df4f]) % 256; _0x16ba4e = _0x421af5[_0x29df4f]; _0x421af5[_0x29df4f] = _0x421af5[_0x705d3f]; _0x421af5[_0x705d3f] = _0x16ba4e; _0x4f31ee += String.fromCharCode(_0xc5bde6.charCodeAt(_0x235057) ^ _0x421af5[(_0x421af5[_0x29df4f] + _0x421af5[_0x705d3f]) % 256]); } return _0x4f31ee; }; a0_0xa0b8.WEMegZ = _0x47f1dd; _0x6db2f9 = arguments; a0_0xa0b8.UWwKby = true; } const _0x18b3a9 = _0x58e91a[0]; const _0x491855 = _0xf2c0d2 + _0x18b3a9; const _0x125d04 = _0x6db2f9[_0x491855]; if (!_0x125d04) { if (a0_0xa0b8.OJUoMm === undefined) { const _0x2e77ae = function (_0x51f4b1) { this.cvuinS = _0x51f4b1; this.fyrkaT = [1, 0, 0]; this.rPgZEm = function () { return 'newState'; }; this.IZqJeb = "\\w+ *\\(\\) *{\\w+ *"; this.kmcauN = "['|\"].+['|\"];? *}"; }; _0x2e77ae.prototype.sxNFHR = function () { const _0x551808 = new RegExp(this.IZqJeb + this.kmcauN); const _0xec967d = _0x551808.test(this.rPgZEm.toString()) ? --this.fyrkaT[1] : --this.fyrkaT[0]; return this.KiEDhy(_0xec967d); }; _0x2e77ae.prototype.KiEDhy = function (_0xf8c99b) { if (!Boolean(~_0xf8c99b)) { return _0xf8c99b; } return this.aMpklD(this.cvuinS); }; _0x2e77ae.prototype.aMpklD = function (_0xe722ea) { let _0x149a72 = 0; for (let _0x3fe735 = this.fyrkaT.length; _0x149a72 < _0x3fe735; _0x149a72++) { this.fyrkaT.push(Math.round(Math.random())); _0x3fe735 = this.fyrkaT.length; } return _0xe722ea(this.fyrkaT[0]); }; new _0x2e77ae(a0_0xa0b8).sxNFHR(); a0_0xa0b8.OJUoMm = true; } _0x530d4e = a0_0xa0b8.WEMegZ(_0x530d4e, _0x1eeda0); _0x6db2f9[_0x491855] = _0x530d4e; } else { _0x530d4e = _0x125d04; } return _0x530d4e; }; return a0_0xa0b8(_0x6db2f9, _0x437aca); } // Removed some actual payload // This is just used for a checksum to cause infinite loops on tampering function a0_0x5173() { const _0x1b091d = ['WPL9tgvD', 'rSkQESkIxq', 'W6ZcP8oaEhO', 'W6mYb2bA', 'dHDnimo6', 'xmouaaldSa', 'umk2ASk+Aa', 'su4YWOxcGq', 'tHddKCkblW', 'W6JcI8oXD3O', 'oejaWPXe', 'ccnenei', 'W5ZdH8o7W4i6', 'W53cMhBcLSkQ', 'uKODWPxcKa', 'acj7oCo5', 'W4PQmfyT', 'wmk7W67dMmo9', 'W4xcG8oBpI8', 'dCk6xmoBwa', 'W7JdRYGgbW', 'W5XIAwOF', 'WQpcTxiIDq', 'qNJcRMhcOa', 'W4NcU8oiW5rK', 'W4/dK8oXW4G9', 'W6NdSCkMW4Se', 'W5eZdSk6va', 'W4JdK8oFxau', 'WOhdN13dNI8', 'iSk9x8oJmb3dGSk5r8oTs8oLpG', 'oMmxjcVdKHHa', 'W6NcHCkta8oO', 'WQmmgXldUW', 'W6SSe2LB', 'nCkBbfrA', 'sImjdIy', 'WQtdJmkmomkr', 'mNRcIConFa', 'W6iiba', 'W7hdIbCbhq', 'BmoAkZPG', 'W5tdJ8oCW6Wr', 'vK7dLKlcMW', 'WOpdO8opWPWI', 'vZisW78', 'W6pcOmogCxK', 'DSoLedZdHa', 'ruGQ', 'WRJdN8oMWRL+', 'AZqthZe', 'W7qMW6b/W6NcTLHAW7ysW6y', 'WQpcVmkLWOb6', 'WPZcUfiFvq', 'gCkxBCoYDG', 'p8oqWOtcG8kW', 'EmoHArLS', 'e03cVf5E', 'WO3cPemhsG', 'ACoCcMyc', 'W7K5aNDb', 'ytuA', 'itjWd8of', 'tmoFBqvd', 'W7ddSmoOBGe', 'xSkSxG', 'ut7dLCkfkG', 'x8k/wSopxG', 'jvT/dmoZ', 'CSoHCtnH', 'bsxdIIddPHJcGmkDmCkeDW', 'W6/cG8ksgmoU', 'ae7cQSofFW', 'zmk0W73dOhC', 'bCkqy8oXCG', 'tCkoW4pcH8ke', 'WR7dI0ldTYW', 'ax7cT8ooxG', 'W5dcMCowsfK', 'W7WPkwzd', 'W5rPaeaU', 'W6m6mHuI', 'CdhdO8ktdW', 'W67dLmoGW4mP', 'WPpdKSkyWQSS', 'W4JdJHWEfq', 'tbNdLSkqoW', 'nLnQn8or', 'W6ddTmoLW4S0', 'W7/dUSoJW7af', 'W6uUdG', 'ruS7WOJcSa', 'u8o9WQvnuq', 'vYGpqYm', 'WQXix2nM', 'f8oAWRhcRSkI', 'FCo8WRddJSoj', 'CZmLCHi', 'a0nynCo7', 'uCo2WQu', 'rHuqWP5qkCkwWPiZ', 'W6tdKmo7W64n', 'cLH3WRL1', 'cIjHoa', 'W5JdISoXW7Cp', 'W6ldMrKCha', 'W63dHCoFsWm', 'dhflu8k6', 'WRVcVv0pxa', 'WRjVWQqQ', 'W4DAumksfG', 'r8oCtXldSG', 'u1q9', 'xmkRCG', 'ahhcOCoa', 'uGRdVSkqiG', 'B8oAeNKc', 'W4nOASkJma', 'f8oqWQddPCk5', 'vmobka', 'W7tcRCop', 'rCkJsmkcrG', 'FHK0gYm', 'WRj5WOC7WQi', 'W7u+aCksva', 'W6z0Frq2', 'vaiWFXi', 'D08WW5xdTG', 'WQumeXJdOG', 'WQBcUCkVWQLe', 'FSoSWPxdJCoo', 'omkSw8onvW', 'WPpdPmofWOy', 'WOqJvsf5', 'AaO6uX4', 'kxfXmSor', 'WOhdVmkyWOSK', 'W6GqbCkrBG', 'baqenSoJ', 'nrrHlMO', 'W4VcPmojwMe', 'WOddN0RdMrO', 'WOFdNSkaWPiO', 'CCo9W5ZdJSoq', 'W6tdImoIW4KF', 'khnBs8oH', 'W6yFlSkrtq', 'tCoXdh40', 'WPVdQmkWfSky', 'mxTmWQbl', 'uZGfW7hdMq', 'l8kXzCotDa', 'vb3dHSkuoG', 'ytqt', 'W6m9lvve', 'zSoUWOBdNa', 'CYqorrm', 'DIWtW6ldHW', 'kmkCdW', 'WRe4WRmGWR8', 'suG6WOpcVa', 'W5FcLCoiht0', 'd8kmiCo9Fa', 'WO/cU15eqq', 'W6zMESk+', 'saqVzHC', 'rCk6W7RdNSoO', 'W6JcOK7cP8kc', 'WPTZyhjK', 'sSkdW6hdLNy', 'yCk+W5BcMSkm', 'wmoPf8kOyW', 'WPC4DHDT', 'DHekW4RdLa', 'amoPaCkoeXhdRCo9WPBcLSouW6CL', 'abvDj8o+', 'WOVdKx4', 'qmodcCk2tq', 'WQRdKSoUWPn9', 'jfDJa8ot', 'qmoydSkPEG', 'D8kYFSoptG', 'smo5WONdSCoY', 'FCoLomk6xa', 'jCk5W5hcKmojWQtcIXxdG34', 'W7/dSSocAIK', 'W6uDjCkuFG', 'qCkYuSocta', 'vqBdUmknkG', 'WQeCtGfQ', 'W4PFcgGC', 'WOVcKgaUEa', 'WQT6W7SSWRG', 'WRZdLCoMWR4', 'FSk4Fmkata', 'W5T5FZaa', 'suaAWOtcGa', 'W5JdRaCaqCkOWPhdSCoZ', 'sYtdOSkhaq', 'WQ7dGSoYWQSK', 'W6OCl8kAqq', 't8knW7xdQmo3', 'BCk4W6ldOCoi', 'DmkBFCoDza', 'n8kye0rp', 'pmoJWPa', 'WQztshXZ', 'WOddM1hdQWS', 'iejRWPPY', 'ihLLWR9e', 'BCojWOzkwa', 'bXne', 'i03cGhD4', 'gWjBpSo9', 'WO/dVCooWPC5', 'WPVcL3e5ya', 'WRLlAKzn', 'mSkyAmoCsG', 'WPJdKNldUI8', 'WQZcTmkhph4vimkvWRddTgpcV8k4mq', 'WRy1CrnW', 'W7qPnbC4', 'W7GGpW', 'W43dJmoHsJa', 'W5PdAvTc', 'WQayAbDZ', 'W53cJYtcUhGbuCkFW6LwW6y', 'zZzsbde', 'W4tdU8oIW6CI', 'WRldNSoSWQ9Y', 'DmkAe19s', 'bcBdJsldOx7dVCk7lCkezLDh', 'WO5YuvPd', 'WRWBBGji', 'W6/dRmoNW4mc', 'W6ficMGk', 'oM1ix3ZcJLO+WPC6W67dLsFcKa', 'WR3dUSoNWQjT', 'BGmQbJu', 'xCoKp38z', 'W71GtZ0d', 'lSkcW4hdHCot', 'yCoUjSknBW', 'hgtcOCos', 'f3lcKSoTBq', 'WQtcKmk1WRbi', 'W7Prz8k7oq', 'WQtcJSku', 'qSkyW6/dP1a', 'rceNW6ddSa', 'wSolDqTX', 'zSk4WRtdOxy', 'DmoKamkWyq', 'W7ZcJuFcUSki', 'c8koF8oYAG', 'D8kyW5FcN8oy', 'FSoTDJn2', 'bYrRp3K', 'W6/dUCoBW4Ou', 'sIK7', 'b8kNrCopAW', 'Emo/eHhdUa', 'qXOwW40BE8onWReBrZ3dR8kz', 'WO7dGSoRWQbO', 'tI8BW6JdTa', 'W7W5W7SKWQu', 'W6q4adeJ', 'WRGkvq5+', 'rCoqfXhdOa', 'CSo9Ca', 'BtRdH8kHoa', 'WR9aA2zs', 't8koW6hdUmo6', 'rSoFbrxdQW', 'W5bvegC8', 'sHpdO8kboW', 'WRjft0Dc', 'rSk6zmoXxa', 'y8oKhmkZ', 'WOxcULCosW', 'gt/dLd3dVG', 'DCkqB8k2Bq', 'q2BdIfBcKG', 'FXG/pHy', 'W6FdM8o9FXC', 'bhRcPCoaFG', 'WQxcKmo4WR5o', 'WQFdHmkxEwi', 'WQhcKCkX', 'W601bIHd', 'dsrfmSoG', 'W4ZdTCo8rXe', 'sMm4WRlcHq', 'vCoTWQrABW', 'aq9nmSo1', 'WRFdUCkpWPSI', 'vYqXW67dHW', 'W67dPCofW58+', 'WRSeAqzU', 'WR0rDa9P', 'vxdcLgtcSG', 'p21npXhdRaT+WRu', 'rvldQ3BcTW', 'W7uGmHqG', 'sMldJepcJW', 'bKHi', 'vCoOWQfKra', 'WOtcOeCBqa', 'vNpdGeFdKW', 'W48giNXw', 'u8oWf2CM', 'CLeqWQ7cKG', 'vSk7r8o+rq', 'W6NcRmovycG', 'qc85xYy', 'WPtdQ8kKfmkf', 'FSobkMSw', 'cW5e', 'WRaFCH10', 'WRNdOmkPWPW8', 'B33cMq', 'umoxWRRcPG', 'W4iDdMTn', 'W6jJsXyi', 't1qZ', 'W7TynvG2', 'WQVdNmoPWR5S', 'umotWRRcPmk9', 'W4GykKfy', 'W5NdVYmWoW', 'W6PkyCkJmG', 'khRdRx9+', 'W5fOb0qI', 'W6FcRCoxxgi', 'cYLKowa', 'z8kRxSkGEq', 'WPTwAW', 'xSoqlMOZ', 'WPldSCkpWPSI', 'v8o3WR5Jva', 'W6BcJ8k6WRbj', 'x2BcKa', 'WQhcKCkYWRre', 'tSoefcldGq', 'WOeKxdX7', 'W5LIdKG0', 'lbbnleO', 'h8o6WPRcJCkd', 'WO7dTmkacmka', 'AZWpnaG', 'jgpdVhPJ', 'WOtdSCkXbCkH', 'W4j7EIeE', 'lSkZfNvj', 'tYiOvJy', 'D2ddV3FcVG', 'WPNdO8o4WPPZ', 'b3pcPCogEW', 'W4XNrcK8', 'vaiRW5NdNq', 'jh1nc8o2', 'W7eMW6z3WRVcU3fwW6eM', 'o8oLWR3cSYyIfCoRggpdTW', 'W6qAgN1A', 'nHjUeNm', 'W6HLzSkLla', 'b2r/pCoT', 'W6VdH8oBW60G', 'dsO9pNe', 'W5xdJ8ouAWG', 'jWnrjmoM', 'As4ipHC', 'w8k8FSk0', 'wCobjrZdTW', 'umkiW7BdU8oS', 'WPfUWQmLWPe', 'W6iFmwTw', 'saBdP8kNeW', 'W7Pve3eJ', 'W6tdTbqzfa', 'W4xdQmogW5qz', 'WRz0WRmOWQy', 'ymoQWPb4wa', 'hCoBWRJdPmkO', 'nxvqeCkT', 'WRWofGNdVa', 'lMjf', 'WOXJxKjD', 'cXnmnSo5', 'FSohfIRdHa', 'r8odbby', 'WOf8vezT', 'wCkSvSoq', 'dCkxASkZCG', 'ehpcT8oFuq', 'W7dcPmocBwG', 'BCkdWO/dKSou', 'xNNcMh3cSa', 'WPVdVgRdMIG', 'suKW', 'o2lcS21Q', 'W40Lj8kTtG', 'WQJdMmo9W6rP', 'WQhcTvaIxq', 'FsKhdwS', 'WPeJBszK', 'W6LnwSkpfq', 'cv3cOKL1', 'zCk6W6RdOMS', 'jSk7W5pcNmkuW7JdIYddNhn4xcK', 'nwvZWObu', 'cCk/cNfz', 'W6ddTCoV', 'ymk6W6C', 'W5ekbmk1uW', 'ECo5WRjHuW', 'WRZcP8k4WQbB', 'CCo7WORdMmow', 'udu/', 'F8obm0Gd', 'WOldOSkKWQC7', 'gCkGohvU', 'W6DVtCkOgG', 'DSoyWPxdOSoe', 'EZGpbtu', 'WQr5yvL1ksf0W63cRalcUKa', 'oCklffvC', 'Amobf21ghNCp', 'udOvlW4', 'owrEvmkS', 'W5hdVmoMW6Wv', 'W4lcP0CkrW', 'wJujgqS', 'o8kmchD6', 'W6VcQCk0lmos', 'W4tcHCoEiZW', 'BmktW4ddH2W', 'W7FcJCohkSkE', 'z8o0bWhdOa', 'WRRdP8kmCNS', 'W7VdPmoxEti', 'uwuDWQRcLq', 'W6ddRSoQW506', 'WRzbWRCdWRW', 'DCotfSkzqa', 'W6SalSkz', 'm17cK8obBG', 'CsKOqH8', 'A8kyB8kazG', 'WP1hDrPg', 'g0naWPbA', 'WQmxaXJdQa', 'rmolpSkWFW', 'W6ZcNSoAnJ0', 'rhVcJN4', 'WPddVmkaWOSR', 'W7NdSCo7', 'ACkbW4tdLmoe', 'W7hdSmoF', 'yrqSzYG', 'WRRdP8kmBNK', 'W6NcPSoe']; a0_0x5173 = function () { return _0x1b091d; }; return a0_0x5173(); } ```

I unfortunately don't actually know how to detect it, since it might take different shapes, but it would be a very useful feature. The main thing to check for that I can think of would be any variable that contains a function which is later used with either fun_name.toString() or something + fun_name, which are the two ways of converting a function to a string that are used in the snippet above. the next question is what to do about it, since it's more difficult to know how many layers of functions are just tamper-proofing where the actual code starts.

Clicking the "self defending" button on obfuscator.io gives somewhat similar but slightly simpler code.

anka-213 commented 1 week ago

I found the tool https://github.com/j4k0xb/webcrack which already has this feature, so that solved my problem

ben-sb commented 1 week ago

Good to hear you were able to find a solution, I will keep this issue open and hopefully implement it as a feature when I have time