JSReg down but not out
Published: Thu, 14 Jul 2011 15:58:52 GMT
Updated: Sat, 22 Mar 2025 15:38:17 GMT
Read time: ⏱️ 3 min read
A few months ago some very talented people called Jonas Magazinius aka @internot_ and Alexey Silin aka @lever_one broke JSReg. Maybe broke is the wrong word obliterated is more accurate. This was very humbling for me, I knew it wasn't perfect this is why I tried to tempt them to break it by stating it was unbreakable :) and it worked, they did and broke it so much more than I was hoping. I must admit I considered abandoning the project since the root of code was rotten. Over the months after I was slowly rewriting and taking a different approach, today I hit some inspiration and managed to finish off what I had started.
The core of the last version was centred around RegExes, I had to slightly adjust this path and allow a mix of regexes and states. Since I'm a rookie at parsers, I decided to take the approach of tokenizing and rewriting in one step. I read many recommendations that you could tokenize your code first then parse it but I feel it can be done at once.
So the modified loop looks something like this:-
pos = 0;
left = 0;
while(pos < code.length) {
chr = code.substr(pos, 1);
prevText = code.substr(0, pos);
currentText = code.substr(pos);
next = code.substr(pos+1, 1);
prev = code.substr(pos-1, 1);
....
} else if(test(objectIdentifiers, currentText)) {
matchStr = match(objectIdentifiers, currentText);
pos += matchStr.length;
output += rewriteObjectIdentifiers(matchStr);
left = 0;
lastState = 'objectIdentifiers';
....
Instead of using just a String replace I loop char by char and move the position along if the regex matches along with any parsing I do without regexes. This way I can have a nice mixture of them both. I had to revise the regex object detection with it's own state machine since it's very difficult to parse especially with IE bugs that Lever_One found. Consider the following regex on IE7 (provided by Lever_one):
/[]/]/,alert(1)
So IE7 seems to continue parsing as a regex when it encounters a blank regex class. Thankfully fixed in IE9 standards.
To fix other bugs I started to track parenthesis and check for curly braces after inside for/if statements etc, this fixed many of the vectors were no curlys confused the state of JSReg. Because I've collected a huge amount of vector data thanks to Jonas and Alexey, I created a automated check of previous vectors. If you trying to write something similar to JSReg then the following might help you.
//sandbox vectors by Jonas Magazinius, Alexey Silin and other sla.ckers
var tests = ['/lo\\[/;log($placeholder$);;/lo/;','/lo\\/;log($placeholder$);;/lo/;',
'/[/;lo="]/;log($placeholder$);//";','/lo\\//,/;lo=/;log($placeholder$);//;',
"lo=''/**/;log($placeholder$);//';",'/**/log/**/($placeholder$/**//**/);',
"/lo/+/'//log($placeholder$);//';","/lo='/?log($placeholder$);:''",
"[/lo='/];log($placeholder$+'');","<true>lo='</true\n>;log($placeholder$+'')",
"<?true lo='\\' ?>;log($placeholder$+'')","1/ /x='/;log($placeholder$+'');",
"1/(/x='/);log($placeholder$+'');","0/0/*lo='*/+log($placeholder$+'')",
"[/lo='/];log($placeholder$+'');",'/[;\nlo="]/&log($placeholder$+"");',
'alert(1===/x/ /1+/**/log($placeholder$)/**/)',"(0)[/[']/+log($placeholder$);+']/']",
'(/[/]/)[/(\/\))\/+log($placeholder$);+"\/"/i]',"[/lo='/];log($placeholder$+'');",
'/\\\\\',"m","/;lo=");log($placeholder$);//";','<>lo="<\/>;log($placeholder$);;""',
'<true true="lo\\"/>;log($placeholder$);;lo="";','<{\'_\'} {\'o\'}="\\"/>,log($placeholder$);//"\n\n\'/,log($placeholder$);,"lo/";',
"1/\n/lo='/,log($placeholder$+'')","<È>x='</È>;log($placeholder$+'')","<true>lo='</true\n>;log($placeholder$+'')",
"<?true lo='\\' ?>;log($placeholder$+'')","1/ /x='/;log($placeholder$+'');",
"1/(/x='/);log($placeholder$+'');", "0/0/*lo='*/+log($placeholder$+'')",
"{}/lo='/,log($placeholder$);//'",'_:/\\[/+log($placeholder$);/i','{}/\[/+log($placeholder$);/i','typeof/\[/+log($placeholder$);/i',
'0?0:/\\[/+log($placeholder$);/i','delete/\\[/+log($placeholder$);/i','void/\\[/+log($placeholder$);/i','with({})/\\[/+log($placeholder$);/i',
'if(1)/\\[/+log($placeholder$);/i','while(1)/\\[/+log($placeholder$);/i','try{/\\[/+log($placeholder$);/i}catch(a){}',
'throw/\\[/+log($placeholder$);/i','(function(){return/\\[/+log($placeholder$);/i})()','do{/\\[/+log($placeholder$);/i}while(1)',
'switch(0){case 0:/\\[/+log($placeholder$);/i}','_:/(]\\[)/+log($placeholder$);//]',"_:/'/+log($placeholder$);/i",
"_:<{'x'}>'</x>+log($placeholder$);//'","_:/{/;log($placeholder$);//","_:/\\(/;log($placeholder$);//","_:/ /+log($placeholder$);//",
'+{}/ /lo="/,log($placeholder$);//"',"/[]/,'lo]/,log($placeholder$);//'",
"/[^]/,'lo]/,log($placeholder$);//'","$='@mozilla.org/js/function';\n$::['log']($placeholder$);","true/'/'+log($placeholder$);+''","this/'/'+log($placeholder$);+''",
"undefined/'/'+log($placeholder$);+''","null/'/'+log($placeholder$);+''","false/'/'+log($placeholder$);+''","Infinity/'/'+log($placeholder$);+''","NaN/'/'+log($placeholder$);+''",
"+{}\n{}/lo='/,log($placeholder$);//'","0?0:{}/log($placeholder$);/i","switch(0){case {}/log($placeholder$);/i:1}",
"+function(){1}/log($placeholder$);/lo","~\n{}/log($placeholder$);/lo","lo:{}/'/,log($placeholder$);//'",
"lo:function x(){1}/'/,log($placeholder$);//'","0\n{}/lo='/,log($placeholder$);//'",
"i=0,i++\n{}/lo='/,log($placeholder$);//'","var È=È/log($placeholder$);/È",
"lo:{}/'/,log($placeholder$)//'","lo:function x(){1}/'/,log($placeholder$)//'",
"0\n{}/lo='/,log($placeholder$)//'","i=0,i++\n{}/lo='/,log($placeholder$)//'",
"(function lo(){1})(1,/'/);log($placeholder$)//'","{}'lo'.replace(/'/,\"\"),log($placeholder$)//'",
"var È=È/log($placeholder$)/È","var lo=0?\nlo:{}/log($placeholder$)/0","~{\nlo1:{lo2:1}/log($placeholder$)/1\n}",
"(\n{lo:1}/log($placeholder$)/1\n)","{lo1:\n{lo2:1}/'/,log($placeholder$)//'\n}",
"[\n{lo:1}/log($placeholder$)/1\n]","1?\n{}/log($placeholder$)/i:0",
"0?0?\nlo:lo:{}/log($placeholder$)/1","0\n{/'lo/,log($placeholder$)//'}",
"while(0)/'/;log($placeholder$)//","if(0)/'/;log($placeholder$)//","for(;0;)/'/;log($placeholder$)//","with(0)/'/;log($placeholder$)//",
"0/function(){}/log($placeholder$)//","1\n~/'lo/+log($placeholder$)//'",
"true\n{}/'lo/+log($placeholder$)//'",
"1?\nfunction lo(){1}/log($placeholder$)/1:1",
"var i=1\ni+++\n{lo:1}/log($placeholder$)/1",
"[\nfunction(lo){}/log($placeholder$)/1\n]",
"(function(){}['constructor'])('log($placeholder$)')()",
"(function(){}/log($placeholder$)/1)",
"1</**/!--i+\n{}/'lo/,log($placeholder$)//'",
"1<//\n!--i+\n{}/'lo/,log($placeholder$)//'",
"[]instanceof{}/log($placeholder$)//",
"[]in{}/log($placeholder$)//",
"var n\n/'lo/+log($placeholder$)//'",
"for (i=0;i<1;i++){\nif(0) continue\n{}/'lo/+log($placeholder$)//'}",
"for (i=0;i<1;i++){\nif(0) break\n{}/'lo/+log($placeholder$)//'\n}",
"x:for (i=0;i<1;i++){\nif(0) continue x\n/'lo/+log($placeholder$)//'\n}",
"x:for (i=0;i<1;i++){\nif(0) break x\n/'lo/+log($placeholder$)//'\n}",
"if(0)0\nelse {}/'lo/,log($placeholder$)//'",
"try{}\ncatch(e){}\nfinally{}/'lo/,log($placeholder$)//'",
"for(;\n{}/log($placeholder$)/1\n;lo)0",
"try{x}\ncatch(lo if(1)/log($placeholder$)/1){}",
//dos vectors
"_:/\[/","_:/'/","_:/(]\\[)/","_:/ / /**/","0/function(){}/","function()[]"
];
That's about it I want to thank Jonas Magazinius and Alexey Silin once again for their great work and you should hire those guys if you have a js sandbox that needs testing.
Think you can break JSReg too? Visit here for the demo:- JSReg demo
and report any bugs here:- JSReg bugs