Monday, December 23, 2013

Regexp Groups "Overflow" in Firefox

TL;DR: In Firefox regexps with 999 998+ groups  return false, no matter was the given string valid or not. It seems like a performance optimization, but theoretically can lead to security issues. I believe it should raise an exception instead of fooling the code.

Unrelated prehistory: Few weeks ago I was trying to XSS m.facebook.com location.hash validation with a timing attack.
(/^#~?!(?:\/?[\w\.-])+\/?(?:\?|$)/).test(location.hash)&&location.replace(...
I was trying to craft *long enough* regexp argument so I could get a spare second to replace the location.hash just before location.replace happens.
I'm no browser expert and don't comprehend how it works on low level - it didn't work out anyway, because it is single-threaded (with setTimeout timing attack works fine - try to XSS this page.)

The side-research is more interesting!

pic unrelated.

When I was testing FF i did notice, for huge payloads JS simply returns "false". For "/a/a..N times" it still was true but for N+1 times all of a sudden - false
Yeah, JS was like saying "TL;DR, hopefully it's false".

Wait, what?! Regexp can't just return a wrong result only because of the argument's length! Let's double check:

In Chrome

z=Array(999999).join('a');
console.log(/^([\w])+$/.test(z),z.length);
//true 999998

z=Array(999999+1).join('a');
console.log(/^([\w])+$/.test(z),z.length);
//true 999999


In FF

z=Array(999999).join('a');
console.log(/^([\w])+$/.test(z),z.length);
//true 999998

z=Array(999999+1).join('a');
console.log(/^([\w])+$/.test(z),z.length);
//false 999999

Apparently, thing is, after catching 999 998 regexp ([\w]) groups FF "gets tired" and returns false instead of finishing the work or raising an exception like Chrome does (RangeError: Maximum call stack size exceeded).

To turn it into an exploitable vulnerability you would need a JS regexp leading to something bad in "false" case - totally unlikely. But good place to start FF regexp "quirks" investigation.

P.S. Please fix me if I did not correctly understand the issue.

2 comments:

  1. Can you give a little more info on how one would XSS the "try to XSS this page" page? The most I can get it to do it redirect to a 404 on the same domain.

    ReplyDelete
  2. Previous anonymous: like so

    < iframe src="http://homakov.github.io/xssme.html#!/foo" onload='
    this.src="http://homakov.github.io/xssme.html#!javascript:alert(location.href)" '>

    ReplyDelete