Created
April 4, 2019 14:00
-
-
Save roddux/a61d59117d0cda7ef39a093871fe73f7 to your computer and use it in GitHub Desktop.
Another JS shell fuzzer, exploring some ideas from TODOs in the last
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 _variables = {}; | |
var _varCount = 1; | |
var _functions = []; | |
var _funcCount = 1; | |
var _dvarCount = 0; | |
var rc = (x)=>x[Math.floor(Math.random() * x.length)]; | |
function pick(X, recurseLevel) { | |
if (recurseLevel >= 5) return "'ZZ'" | |
switch (typeof X) { | |
case "object": | |
if (Array.isArray(X)) return pick(rc(X)); | |
break; | |
case "function": | |
return X(); | |
break; | |
case "string": | |
case "number": | |
return X; | |
break; | |
default: | |
console.log("FATAL ERROR! pick() with no return type!"); | |
throw "pick() with unhandled type: "+typeof X; | |
break; | |
} | |
} | |
var builtin = [ | |
"Array.prototype.join.apply( <variable>, [ <item> ] );", | |
"Array.prototype.toLocaleString.apply( <variable> );", | |
"Array.prototype.reverse.apply( <variable> );", | |
"Array.prototype.push.apply( <variable>, [ <item> ] );", | |
"Array.prototype.pop.apply( <variable> );", | |
"Array.prototype.shift.apply( <variable> );", | |
"Array.prototype.unshift.apply( <variable>, [ <item> ] );", | |
"Array.prototype.splice.apply( <variable>, [ <number> ] );", | |
"Array.prototype.splice.apply( <variable>, [ <number>, <number> ] );", | |
"Array.prototype.toSource.apply( <variable> );", | |
"Array.prototype.slice.apply( <variable> );", | |
"Array.prototype.slice.apply( <variable>, [ <number>, <number> ] );", | |
"Array.of( <item> );", | |
"Array.isArray( <item> );", | |
"Object.prototype.toSource.apply( <variable> );", | |
"Object.prototype.toString.apply( <variable> );", | |
"Object.prototype.isPrototypeOf.apply( <variable>, [ <variable> ] );", | |
// "Object.prototype.propertyIsEnumerable.apply( <variable>, [ <propertyName> ] );", | |
"Object.assign( <variable>, <variable> );", | |
"Object.setPrototypeOf( <variable>, <variable> );", | |
"Object.keys( <variable> );", | |
"Object.values( <variable> );", | |
"Object.entries( <variable> );", | |
"Object.is( <variable>, <variable> );", | |
// "Object.defineProperties( <variable>, <propertyDefinition> );", | |
"Object.create( <variable> );", | |
// "Object.create( <variable>, <propertyDefinition> );", | |
"Object.getOwnPropertyNames( <variable> );", | |
"Object.getOwnPropertySymbols( <variable> );", | |
"Object.preventExtensions( <variable> );", | |
"Object.freeze( <variable> );", | |
"Object.isFrozen( <variable> );", | |
"Object.seal( <variable> );", | |
]; | |
// loop handling | |
// don't die on recursion | |
var grammar = { | |
"<object>": ["[]", "{}", "NaN"], | |
"<primitive>": ["'a'", 0, 1], | |
"<item>": ["<primitive>", "<object>"], | |
"<variableDef>": function() { | |
let varNam = "var"+String(_varCount).padStart(8,"0"); | |
let varVal = pick(grammar["<item>"]); | |
_variables[varNam] = varVal; | |
_varCount += 1; | |
return "var " + varNam + " = " + varVal + ";"; | |
}, | |
"<number>": [0,1,2,3,4,5,6,7,8,9], | |
"<builtin>": builtin, | |
"<variable>": function() { | |
let varNam = "var"+String(Math.ceil(Math.random() * (_varCount-1))).padStart(8,0); | |
return varNam; | |
}, | |
"<Dvar>": function() { | |
let varNam = "Dvar"+String(_dvarCount).padStart(5,0); | |
_dvarCount += 1; | |
return varNam; | |
}, | |
"<loop>": [ | |
"for(let _=0;_<1e3;_++) { <noLstatement> }", | |
], | |
"<elementAssign>": function() { | |
let newEleNam = "eleNam"; // which variables can be assigned elements | |
return "<variable>['" + newEleNam + "'] = <item>;"; | |
}, | |
"<elementMutate>": function() { | |
let eleNam = "eleNam"; //find an element name for this(?) item | |
return "<variable>['" + eleNam + "'] = <item>;"; | |
}, | |
"<elementAccess>": function() { | |
let eleNam = "eleNam"; // which variables can be assigned elements | |
return "let <Dvar> = <variable>['" + eleNam + "'];"; | |
}, | |
"<propertyAssign>": function() { | |
let newPropNam = "propNam"; // which variables can be assigned properties | |
return "<variable>." + newPropNam + " = <item>;"; | |
}, | |
"<propertyMutate>": function() { | |
let propNam = "propNam"; //find an element name for this(?) item | |
return "<variable>['" + propNam + "'] = <item>;"; | |
}, | |
"<propertyAccess>": function() { | |
let propNam = "propNam"; // which variables can be assigned properties | |
return "let <Dvar> = <variable>." + propNam + ";"; | |
}, | |
"<mutate>": ["<propertyMutate>", "<elementMutate>", "<elementAssign>", "<propertyAssign>"], | |
"<functionCall>": function() { | |
if(_functions.length) return rc(_functions)+"();"; | |
}, | |
"<functionDef>": function() { | |
let funcNam = "func"+String(_funcCount).padStart(8,"0"); | |
let funcVal = "\n\t<noLstatement>\n\t<noLstatement>\n\t<noLstatement>"; | |
_functions.push(funcNam); // TODO: keep track of #args | |
_funcCount += 1; | |
return "function " + funcNam+ "() { " + funcVal + "\n};"; | |
}, | |
"<statement>": ["gc();", "<functionCall>", "<mutate>", "<propertyAccess>", "<elementAccess>", "<loop>", "<builtin>"], | |
"<noLstatement>": ["<mutate>", "<propertyAccess>", "<elementAccess>", "<builtin>"], // gimped until recursion fixed | |
}; | |
function genTmpl() { | |
let tmpl = ""; | |
// tmpl += "function gc(){for(j=0;j<10;j++,q=new ArrayBuffer(0xFFF));}\n"; | |
for(let j=0;j<10;j++) tmpl += grammar["<variableDef>"]() + "\n"; | |
for(let j=0;j<5;j++) tmpl += grammar["<functionDef>"]() + "\n"; | |
for(let j=0;j<30;j++) tmpl += rc(grammar["<statement>"]) + "\n"; | |
return tmpl; | |
} | |
function process(template) { | |
function toReplace() { | |
let instances = 0; | |
Object.keys(grammar).forEach( function (tag) { instances += (template.match(RegExp(tag,"g")) || []).length; } ); | |
return instances; | |
} | |
let ic = toReplace(); | |
var recurseLevel = 0; | |
while (ic > 0) { | |
Object.keys(grammar).forEach( | |
function(tag) { | |
// PICKVAL so we don't replace current tag potentially with another current tag | |
template = template.replace(new RegExp(tag, "g"), "<PICKVAL>"); | |
while ( template.indexOf("<PICKVAL>") != -1 ) { | |
template = template.replace("<PICKVAL>", pick(grammar[tag], recurseLevel)); | |
} | |
} | |
); | |
ic = toReplace(); | |
recurseLevel += 1; | |
} | |
return template; | |
} | |
console.log("[*] Entering fuzzing loop"); | |
var tnum = 0; | |
//for(tnum=0; tnum<1e2; tnum++) { | |
for(tnum=0; tnum<5; tnum++) { | |
//for(;;) { | |
console.log("[*] Generating template", tnum); | |
let tmpl = genTmpl(); | |
console.log("[*] Processing template", tnum); | |
let fuzz = process(tmpl); | |
console.log("[*] Displaying fuzz program"); | |
console.log(fuzz); | |
console.log("[*] Executing template"); | |
try { | |
//evalcx(fuzz, evalcx("")); | |
eval(fuzz); | |
} catch(e) { | |
console.log("[!] Caught exception"); | |
console.log(e); | |
} finally { | |
console.log("[*] Resetting state"); | |
(function resetState() { | |
Object.keys(_variables).forEach(function(V) { delete V; }); | |
_functions.forEach(function(F) { delete F; }); | |
let unsetStr=""; | |
for(let _=0;_<_dvarCount;_++) { let varNam = "Dvar"+String(_).padStart(5,0); unsetStr += "delete "+varNam+"; "; } | |
eval(unsetStr); _dvarCount = 0; | |
_variables = {}; _varCount = 1; | |
_functions = []; _funcCount = 1; | |
gc(); | |
})(); | |
} | |
console.log("[*] Completed fuzzing run\n"); | |
} | |
console.log("[*] Exiting fuzzing loop"); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment