Sindbad~EG File Manager
--[[
A lib with some method to pseudo obfuscate some JS Code.
The idea is to just avoid having some code that is easily parsable using regex / pattern detection.
The obfuscation used will be easy to defeat, it's not the goal. The goal is just to hide some information to avoid
having the JS challenge bypassed by just grepping the code for instance.
--]]
local o2config = require "lib/o2switch_config"
local table = table
local math = math
local tostring = tostring
local string = string
local pairs = pairs
local _M = {}
local charset = {}
do
for c = 65, 90 do table.insert(charset, string.char(c)) end
for c = 97, 122 do table.insert(charset, string.char(c)) end
end
-- Generate a random string that is safe for JS use. For instance for a variable name.
function _M.randomString(maxLength)
math.randomseed(math.random(1,999999999) + ngx.time() + math.random(1,99999999))
local length = math.random(2,maxLength or 35)
local randomString = ''
for i = 1, length do
randomString = randomString .. charset[math.random(1, #charset)]
end
return randomString
end
-- Add some random quote using the 3 available in JS around a string
-- @string The string to hide using the method
function _M.randomQuote(string)
local quotes = {"'", '"', '`'}
local quote = quotes[ math.random( #quotes ) ]
return quote .. string .. quote
end
-- Add a random amount of parenthesis arround a string
-- @string The string to hide using this method
-- @return The string with (or without) parenthesis
function _M.randomParenthesis(string)
local nb = math.random(0,10)
return string.rep('(', nb) .. string .. string.rep(')', nb)
end
-- Return a piece of JS Code that eval to a char using the Hex representation
-- @char the char to hide
-- @return obfuscated char
function _M.toHex(char)
return _M.randomParenthesis(_M.randomQuote('\\x' .. string.format('%02X', string.byte(char))))
end
-- Return a random value from a table
-- @table The table that contains the list of values to choose from
-- @return A random value from the table
function _M.randomValueFromTable(table)
return table[ math.random( #table ) ]
end
-- Randomly call a function stored in a table and also pass the val argument to the function
-- @table The table that contains the list of function to choose from
-- @val The value to pass to the table
-- @return Result from the function
function _M.randomCallFromTable(table, val)
return table[ math.random( #table ) ](val)
end
-- Add some garbage obfuscated HS comments arround the char / string we want to hide
-- @char the char to "hide"
-- @return string Obfuscated JS Code
function _M.toGargabeComment(char)
return '/** /*** "@/ **'
.. _M.randomQuote('*') .. _M.randomString(5) .. '**/'
.. _M.randomParenthesis(_M.randomQuote(char))
.. '/* * // *' .. _M.randomString(7) .. '* / */'
end
-- Return an obfuscated JS code that force cast to string
-- @string the string to force cast to a string in JS
-- @return string Obfuscated JS Code
function _M.returnJsToString(string)
return _M.randomValueFromTable({
"String",
"window['String'].fromCharCode",
"window['S' + \"t\" + 'ring'].fromCharCode",
"window['S' + \"t\" + 'ring'].fromCharCode",
"window['S' + 't' + 'r' + 'i' + 'n' + 'g'].fromCharCode",
"window[(function(){return 'String';})()]['fromCharCode']",
"window[(function(){return 'S' + \"tring\";})()][".._M.toGargabeComment('fromCharCode') .."]"
}) .. '(' .. string .. ')';
end
-- Obfuscate a char using the int representation
-- @char The character to encode using the int representation
-- @return string Obfuscated JS Code
function _M.toInt(char)
return _M.randomValueFromTable({
"String.fromCharCode",
"window['String'].fromCharCode",
"window['S' + \"t\" + 'ring'].fromCharCode",
"window['S' + \"t\" + 'ring'].fromCharCode",
"window['S' + \"t\" + `r` + 'i' + 'n' + 'g'].fromCharCode",
"window[(function(){return 'String';})()]['fromCharCode']",
"window[(function(){return 'S' + \"tring\";})()][" .. _M.toGargabeComment('fromCharCode') .. "]"
}) .. '(' .. _M.randomParenthesis(_M.randomQuote(char.byte(char))) .. ')'
end
-- Generate some useless JS obfuscated code that return or concatenate to an empty string. Dead code.
-- @return Obfuscated dead JS code
function _M.emptyGarbage()
return _M.randomValueFromTable({
"(function(){return '';})()",
"(function(){return String('');})()",
"/***/''/***/+\"\"+``",
})
end
-- Obfuscate a string, character by character using different obfuscation method
-- @string the string to obfuscate
-- @return string Obfuscated JS Code
function _M.obfuscateString(string)
local obfuscated = '';
string = tostring(string)
for c in string:gmatch"." do
obfuscated = obfuscated .. (obfuscated == '' and '' or '+') .. _M.randomCallFromTable({
_M.toHex,
_M.toInt,
_M.toGargabeComment,
}, c)
obfuscated = obfuscated .. (obfuscated == '' and '' or '+') .. _M.emptyGarbage()
end
return obfuscated;
end
-- Generate a JS Function (obfuscated) that will solve the challenge
-- @hashVarName The name of the randomly generated JS variable that contains the hash to guess
-- @hashMethodCollection The collection of hashing methods to use to pass the challenge
-- @return The obfuscated JS Code (self calling function)
function _M.generateFindHashJsPart(hashVarName, hashMethodCollection)
local replacementTable = {
['hash'] = _M.randomString(),
['i'] = _M.randomString(),
['k'] = _M.randomString(),
['hashFunction'] = _M.randomString(),
['maxCost'] = _M.obfuscateString(o2config.maxCost),
['found'] = _M.randomString(),
['window'] = _M.randomString(),
['hashVarNameGlobal'] = hashVarName,
['hashVarName'] = _M.randomString(),
['headlessDetection'] = _M.headlessBrowserDetection(),
}
local jsCode = '(function(){'
jsCode = jsCode .. 'var #hashFunction = function(#i, #hashVarName){/*d*sq*/#window=/*x*/window/**--**/;';
local hashCode = '';
local i = 0
for k, hashMethod in pairs(hashMethodCollection) do
local tmp = '#window[' .. _M.obfuscateString('CryptoJS') .. '][' .. _M.obfuscateString(string.upper(hashMethod)) .. ']('
if i == 0 then
tmp = tmp .. "#window[" .. _M.obfuscateString('String') .. "](#i)"
else
tmp = tmp .. hashCode
end
hashCode = tmp .. ")[" .._M.obfuscateString('toString') .. "](CryptoJS['enc']['Hex'])"
i = i + 1
end
jsCode = jsCode .. 'return (' .. hashCode .. ') === #hashVarNameGlobal ? #i : false; }; '
jsCode = jsCode .. ' for(#k=0; #k < (#maxCost); #k++){#headlessDetection let #found = #hashFunction(#k); if(#found !== false){return #found;}} return false;})()'
jsCode = ((jsCode:gsub('#(%w+)', replacementTable)))
return jsCode;
end
-- Return a challenge JS payload. The JS code will be obfuscated.
-- @hash The hash to guess
-- @hashMethodCollection The collection of hash function name to use
-- @challengeId The challenge ID. It's the equivalent of a session, it's used to store the result in the backend.
-- @return string
function _M.getChallengeJsPayload(hash, hashMethodCollection, challengeId)
local hashToFoundVarName = _M.randomString()
local replacementTable = {
['challengeId'] = _M.randomString(),
['hashToFound'] = hashToFoundVarName,
['challengeIdValue'] = _M.obfuscateString(challengeId),
['hashToFoundValue'] = _M.obfuscateString(hash),
['maxComplexityValue'] = _M.obfuscateString(tostring(o2config.maxCost)),
['findValue'] = _M.randomString(),
['value'] = _M.randomString(),
['i'] = _M.randomString(),
['findHashPart'] = _M.generateFindHashJsPart(hashToFoundVarName, hashMethodCollection),
['headlessDetection'] = _M.headlessBrowserDetection(),
}
-- TODO : Block automated browser
local jsCode=[=[
#headlessDetection
var #challengeId = #challengeIdValue;
var #hashToFound = #hashToFoundValue;
var #value = #findHashPart;
if(!#value){
setTimeout(function(){
document.location.reload(true);
}, 2000);
}
$('#js-chl-response').val(#value);
$('#js-chl-id').val(#challengeId);
setTimeout(function(){
$('#js-chl-form').submit();
}, 1000);
]=]
jsCode = ((jsCode:gsub('#(%w+)', replacementTable))) -- Template replacement
jsCode = ((jsCode:gsub("[\n\r\t]", ""))) -- Remove line break & tabs
jsCode = ((jsCode:gsub("%s+", " "))) -- Remove double whitespace
return jsCode
end
-- Return an obfuscated, self-executing function that try to detect headless browser and create an infinite loop.
function _M.headlessBrowserDetection()
-- The detection is simple at the moment. We'll need to work on that later.
local signatures = {
'_phantom', 'phantom', 'callPhantom', -- phantomJS
'__phantomas', -- phantomas PhantomJS-based web perf metrics + monitoring tool
'Buffer', -- nodejs
'emit', -- couchjs
'spawn', -- rhino
'webdriver', -- selenium
'domAutomation', 'domAutomationController', -- chromium based automation driver
}
local jsCode = '(function(){/*d*sq*/#window=/*x*/window/**--**/;';
for i, s in pairs(signatures) do
jsCode = jsCode .. "if(#window[" .. _M.obfuscateString(s) .. "]){#window['#crash'] = []; while(true){#window['#crash'].push('#crash');$('body').append('#crash');}}"
end
jsCode = jsCode ..'})();';
jsCode = ((jsCode:gsub('#(%w+)', {
['window'] = _M.randomString(),
['crash'] = _M.randomString(),
})))
return jsCode
end
return _M
Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists