From 74bd34de3822a49de5fcde5588d121f736cfec63 Mon Sep 17 00:00:00 2001 From: NateN1222 Date: Thu, 24 Aug 2017 15:41:36 -0500 Subject: Implemented basic script evaluation --- eval_test.js | 123 +++++++++++++++++++++++++++++++++++------------------------ 1 file changed, 73 insertions(+), 50 deletions(-) diff --git a/eval_test.js b/eval_test.js index 4e1dade..6a7b8d2 100644 --- a/eval_test.js +++ b/eval_test.js @@ -143,16 +143,20 @@ var licenses = { } } -// Objects which could be used with bracket suffix notation to do (very) nontrivial things -// If this system is used, bracket suffix notation could still be exploited to -var reserved_objects = { - "window":true, - "fetch":true, - "XMLHttpRequest":true, // only on chrome - "chrome":true, // only on chrome - "browser":true, // only on firefox - "clientInformation":true // good idea? -}; +// Objects which could be used to do nontrivial things +// Bracket suffix notation could still be exploited to get some of these objects +var reserved_objects = [ + "window", + "fetch", + "XMLHttpRequest", + "chrome", // only on chrome + "browser", // only on firefox + "eval" +]; + +// Objects that can only be used with dot notation +var reserved_ + function get_final_page(html_string, callback){ @@ -162,20 +166,34 @@ function get_final_page(html_string, callback){ * true = trivial, false = nontrivial * */ - function evaluate(script){ + function evaluate(script,name){ function reserved_object_regex(object){ - return new RegExp('/[{}\]\[\(\)\.\s]'+object+'\s*\(/g'); - } + // Matches use of object as a variable + + // This accounts for both unary, binary and assignment operators + var arith_operators = "\\+\\-\\*\\/\\%\\="; + + // These are allowed to preceed or trail a variable as in 'if(true){eval("thiscode")};' + // However, if you have 'function(){eval}' where the "}" char trails "eval", this can't + // be used to invoke member objects or pass arguments. + var scope_chars = "\{\}\]\[\(\)\,"; + + // No property accessors are allowed to follow the string stored in "object" + // Whitespace is allowed to come between these property accessors + var trailing_chars = "\s*"+"\(\.\["; + return new RegExp("(?:[^\\w\\d]|^|(?:"+arith_operators+"))"+object+'(?:\\s*?(?:[\\;\\,\\.\\(\\[])\\s*?)',"g"); + } + reserved_object_regex("window"); // Strings - var all_strings = new RegExp('/".*?"'+"|'.*?'/gm"); + var all_strings = new RegExp('".*?"'+"|'.*?'","gm"); // multi-line "/*" "*/" comments - var ml_comment = new RegExp('/\/\*[\s\S]+?\*\/g'); + var ml_comment = /\/\*([\s\S]+?)\*\//g; // in-line "//" comments - var il_comment = new RegExp('/\/\/.+/g'); - // Bracket suffix notation - var bracket_pairs = new RegExp('/\[.+?\]/g'); + var il_comment = /\/\/.+/gm; + // The contents of bracket pairs + var bracket_pairs = /\[.+?\]/g; // Replace string consts with values that won't interfere var temp = script.replace(/'.+?'+/gm,"'string'"); @@ -184,14 +202,24 @@ function get_final_page(html_string, callback){ temp = temp.replace(ml_comment,""); temp = temp.replace(il_comment,""); // Now that there can't be any brackets inside of comments or strings, - // see if there are any variable assignments on - var bracket_contents = temp.match(bracket_pairs); - - for(var i = 0; i < bracket_contents.length; i++){ - if(bracket_contents){} + // + + console.log("------evaluation results for "+ name +"------"); + console.log("Script accesses reserved objects?"); + var flag = true; + for(var i = 0; i < reserved_objects.length; i++){ + var res = reserved_object_regex(reserved_objects[i]).exec(script); + if(res != null){ + console.log("%c fail","color:red;"); + console.log(res["input"].substr(res["index"]-15,res["index"]+15)); + flag = false; + } + } + if(flag){ + console.log("%c pass","color:green;"); } - return false; + return flag; } /** @@ -227,7 +255,7 @@ function get_final_page(html_string, callback){ * */ // TODO: Known bug: extra \n chars thrown in at some splices - function license_read(script_src){ + function license_read(script_src,name){ if(typeof(script_src) != "string"){ return "fail" } @@ -246,7 +274,7 @@ function get_final_page(html_string, callback){ var matches = /^(@license)\s([\S]+)\s([\S]+$)/gm.exec(unedited_src); if(matches == null){ //console.log("No more matches, almost done"); - if(evaluate(unedited_src)){ + if(evaluate(unedited_src,name)){ edited_src += unedited_src; } return edited_src; @@ -255,7 +283,7 @@ function get_final_page(html_string, callback){ //console.log("Everything before the next match"); var before = unedited_src.substr(0,matches["index"]); //console.log(before); - if(evaluate(before)){ + if(evaluate(before,name)){ edited_src += before; } // This should remove the substring "before" @@ -284,7 +312,7 @@ function get_final_page(html_string, callback){ // TODO: this is here to prevent infinite loops, should be removed eventually watchdog++; if(watchdog > 20){ - console.log("%c Watchdog > 20.","color:red"); + console.log("%c !!!!!WARNING!!!!! Watchdog > 20.","color:red"); return false; } } @@ -362,14 +390,15 @@ function get_final_page(html_string, callback){ var done = false; var amt_done = 0; + var amt_remote_scripts = 0; var amt_todo = html_doc.scripts.length + has_intrinsic_events.length; function check_done(){ - console.log(amt_done + "/" + amt_todo); + console.log(amt_done + "/" + (amt_todo - amt_remote_scripts) ); if(amt_done > amt_todo){ console.warn("Not supposed to happen"); } - if(done == false && amt_done >= amt_todo){ + if(done == false && amt_done >= (amt_todo - amt_remote_scripts) ){ console.log("%c DONE.","color:red;"); callback(html_doc); done = true; @@ -379,7 +408,7 @@ function get_final_page(html_string, callback){ } // "i" is an index in html_doc.scripts function edit_src(src, i, name){ - var edited = license_read(src); + var edited = license_read(src,name); if(edited == "string"){ html_doc.scripts[i].outerHTML = ""; } else{ @@ -389,8 +418,8 @@ function get_final_page(html_string, callback){ } // "i" is an index in html_doc.all // "j" is an index in intrinsicEvents - function edit_event(src,i,j){ - var edited = license_read(src); + function edit_event(src,i,j,name){ + var edited = license_read(src,name); if(edited == "string"){ html_doc.all[i].attributes[intrinsicEvents[j]].value = edited; @@ -416,31 +445,22 @@ function get_final_page(html_string, callback){ } if(html_doc.scripts[i].src != ""){ - // it is a remote script ("") - // this function is here because otherwise there would be async issues - function remote_script(i){ - var name = html_doc.scripts[i].src; - var xml = new XMLHttpRequest(); - xml.open("get", html_doc.scripts[i].src); - xml.onload = function(response){ - console.log("%c Script " + i + ":","color:red;"); - console.log(name); - edit_src(this.responseText, i, name); - check_done(); - } - xml.send(); - }; - remote_script(i); + // this is a remote script ("") + var name = html_doc.scripts[i].src; + console.log("%c Will evaluate script '" + name + "' when it arrives. Document.scripts index: "+i,"color:blue;"); + amt_remote_scripts++; + } else{ // it is an inline script ("") - console.log("%c Script " + i + ": (src: inline)","color:red;"); + console.log("%c Evaluating inline script. Document.scripts index: "+i,"color:blue;"); //console.log(html_doc.scripts[i].innerText); edit_src(html_doc.scripts[i].innerText, i, "src: inline (index "+i+")"); } } // Find all the document's elements with intrinsic events for(var i = 0; i < has_intrinsic_events.length; i++){ - edit_event(html_doc.all[has_intrinsic_events[i][0]].attributes[intrinsicEvents[has_intrinsic_events[i][1]]].value,has_intrinsic_events[i][0],has_intrinsic_events[i][1]); + var s_name = "html_doc.all["+has_intrinsic_events[i][0]+"]"; + edit_event(html_doc.all[has_intrinsic_events[i][0]].attributes[intrinsicEvents[has_intrinsic_events[i][1]]].value,has_intrinsic_events[i][0],has_intrinsic_events[i][1],s_name); } check_done(); @@ -517,6 +537,9 @@ function get_final_page(html_string, callback){ get_final_page(document.documentElement.outerHTML,function(a){ console.log("returned"); + if(typeof(a) == "boolean"){ + return; + } document.documentElement.innerHTML = a.documentElement.innerHTML; }); -- cgit v1.2.3