JSReg down but not out

Back to articles

hackvertor

Author:

Gareth Heyes

@hackvertor

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

Back to articles