A couple of weeks ago I put up a small challenge for a specific XSS problem, called Twins of Ten.
The idea was to find a payload that was limited to ten characters, these characters would repeat once and you could expand it to how many pairs you wanted.
The challenge was to both find the shortest payload but also find a way around the XSS Auditor inside Chrome / Safari. My own solution was posted to a secret gist so I could prove I had a solution before the challenge started. What’s also interesting is that my solution, according to the submissions posted, could be shortened down even further.
So, I got a bunch of submissions, only 2 people submitted only one time. 6 people submitted at least 2 submissions. Average was 2.6 emails per submitter which was pretty interesting.
Okay, so let’s focus on the challenge itself. The result of posting this to the example code:
Would end up with this response:
<0123><b x="x">hejsan</b></0123><0123XXX><b x="x">hejsan</b></0123XXX><0123YYY><b x="x">hejsan</b></0123YYY><0123ZZZ><b x="x">hejsan</b></0123ZZZ><!-- 9 chars long -->
So as you see, you have a bunch of characters you could take advantage of to trigger an XSS.
Now, my solution looked like this:
This results in the following cleaned up response:
<script>//... '...'; '...' alert/*... alert/*...*/(1)//...</script>
As you see, when the script-node starts, it begins with a line comment (
//). Then, on the next line, I just wrap the content into a string. The string doesn’t do anything. The same thing happens on the third line, just a string. The fourth line begins with the function call
alert then a multi-line comment starts (
/*). It ends (
*/) on the fifth line which then has the parameters passed to the alert function which in this case is
(1) followed by a line comment block (
Now, when submissions started to arrive, people had a simliar approach, but with some tweaks.
First, @killr0x33d posted a 35 char solution without XSS bypass:
And then a 40 char version with audit bypass (which was similar to mine):
This code would then end up as:
<script>//... -alert//... -alert//... (1)//... (1)//...</script>
This code would end up as:
<script>//... alert(/*... ...*/1)//...</script>
As you see, extremly similar to each other, but with some differences.
@bugch3ck also came up with an equal solution but 35 chars long:
@hv1989 also joined in and posted both a 99 and a 86-char solution, both with XSS Audit bypass. He finally shortened it down to 47 chars:
Which translates into:
<script>//... '...' '...' //... //... e=alert//... e=alert//... e(1);'...\ e(1);'//...</script>
Which looks awesome, he’s even using backslash escape for multi line strings. Really nice!
Now, things started to get really interesting.
@bugch3ck posts a 32 character solution without XSS audit bypass:
@aboul3la joins in with another one, actually the EXACT same sort of solution:
@martinlaak now joins in with a 38 character version without bypass and then a 46 character version with a bypass. But then, he slams everyone off their chairs with a total knockout:
And since the content of the response is wrapped in
> he uses the
> to create this form of comment:
<script>-->... alert//... alert//... (1)//... (1)//...</script>
Which will confuse Chrome/Safari since the start of the comment is not inside the request but already in the content of the response.
@irsdl also joined in with a different approach to the problem (both with a 47 and a 42 character XSS auditor bypass):
He doesn’t do any new line insertion at all, just hardcore ES6 template string one line madness 😀
It translates into:
Now, sorting the list of contestants on the shortest payloads and then on XSS Auditor bypass you’ll see this:
Three guys did 32 characters, and one of them also with an XSS audit bypass!
I’ll contact you on email to send out some nice swag to you guys! Thanks for participating, I had a lot of fun, I hope you did as well.