Last active
March 15, 2021 05:57
-
-
Save theseanl/bd3184a2d4f9e920f18439befd3a910b to your computer and use it in GitHub Desktop.
Sandboxed eval
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var esprima = require('esprima'); | |
var escodegen = require('escodegen'); | |
var estraverse = require('estraverse'); | |
var comm = require('./comm.js'); | |
var SandboxEval = require('./sandboxeval.js'); | |
/* Temporary, syntax should be improved */ | |
deobfuscator = (function(){ | |
var fnLiterals = [], seval; | |
var validateInput = function(expr) { | |
var len = expr.body.length; | |
if(expr.body[len - 1].type == "ExpressionStatement" && | |
expr.body[len - 1].expression.type == "ArrayExpression") { | |
expr.body[len - 1].expression["elements"].forEach(function(el) { | |
if(!el.value) { | |
return false; | |
} | |
fnLiterals.push(el.value); | |
}); | |
return true; | |
} | |
else { | |
return false; | |
} | |
}; | |
var condition = function(node) { | |
if(node.type == "CallExpression" && | |
node.callee && | |
node.callee.type == "Identifier") { | |
var index = fnLiterals.indexOf(node.callee.name); | |
return index == -1 ? false : index; | |
} | |
}; | |
var replace = function(node) { | |
// This part is a pseudocode, have not been tested | |
var codeToEval = escodegen.generate(node); | |
var result = seval.getResult(codeToEval); | |
return { | |
type: "Literal", | |
value: result | |
}; | |
}; | |
var deobfuscate = function(inputvar, code) { | |
var inputExpr = esprima.parse(inputvar); | |
if(!validateInput(inputExpr)) { | |
comm.throwError("Unexpected input."); | |
return false; | |
} | |
seval = new SandboxEval(); | |
seval.getResult(inputvar); // declare functions in the iframe's scope, we don't expect any return value. | |
var codeExpr = esprima.parse(code); | |
codeExpr = estraverse.replace(codeExpr, { | |
enter: function(node) { | |
var i = condition(node); | |
if( i !== false ) { | |
return replace(node); | |
} | |
} | |
}); | |
return escodegen.generate(comm.concatStrings(codeExpr)); | |
}; | |
return { | |
deobfuscate: deobfuscate | |
}; | |
})(); | |
module.exports = deobfuscator; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Calls eval in a sandboxed iframe. | |
* Usage: var seval = new SandboxEval(window.location.href); | |
* seval.getResult(code); | |
* | |
* @constructor | |
*/ | |
var SandboxEval = function () { | |
// The below function is a setup to run in an iframe. Receives messages, evaluates it, and pass back the result. | |
function receiveAndPassBack(){ | |
function evalScript(evt){ | |
//Check that the message came from where we expect. | |
if(evt.origin != "%ORIGIN%") { | |
return; | |
} | |
try{ | |
parent.postMessage({"success": 1, "result":_eval(evt.data)}, "%ORIGIN%"); | |
} catch(error) { | |
parent.postMessage({"success":0, "error": error}, "%ORIGIN%"); | |
} | |
} | |
// Store eval in case of bad situations. | |
_eval = window.eval; | |
window.addEventListener("message", evalScript); | |
} | |
// Create an iframe. | |
// Can't use contentWindow.document.write in a sandboxed iframe, | |
// so using data:text/html. | |
var sandbox = document.createElement('iframe'); | |
sandbox.id ='sandbox'; | |
sandbox.sandbox ='allow-scripts'; //sandboxing | |
var html = "\x3Cscript>(" + receiveAndPassBack.toString().replace(/"%ORIGIN%"/g, '"' + window.location.origin + '"') + ")();\x3C/script>"; | |
sandbox.src = 'data:text/html;charset=utf-8,' + encodeURI(html); | |
document.body.appendChild(sandbox); | |
// register a local variable that we store the returned result. | |
var result; | |
function returnResult(evt) { | |
if(evt.success == 1) { | |
resolve(evt.data.result); | |
} | |
else if(evt.sucess == 0) { | |
reject(evt.data.error); | |
} | |
else { | |
reject(Error("Internal error.")); | |
} | |
} | |
this.getResult = function(code) { | |
result = undefined; | |
(new Promise(function(resolve, reject) { | |
window.addEventListener("message", returnResult, false); | |
sandbox.contentWindow.postMessage(code, "*"); | |
})).then(/* some code goes here.. */) | |
window.addEventListener("message", returnResult, false); | |
sandbox.contentWindow.postMessage(code, "*"); | |
return result; | |
}; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment