Skip to content

Instantly share code, notes, and snippets.

@roddux
Created April 4, 2019 14:00
Show Gist options
  • Save roddux/a61d59117d0cda7ef39a093871fe73f7 to your computer and use it in GitHub Desktop.
Save roddux/a61d59117d0cda7ef39a093871fe73f7 to your computer and use it in GitHub Desktop.
Another JS shell fuzzer, exploring some ideas from TODOs in the last
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