Convert reflected XSS to DOM-based XSS to bypass length limit filter

Hi,
Welcome to my blog!

Have you ever found an XSS that be limited to only 30 characters?
Oh, surely sometimes. How could you get around it?

Today, I will talk about a highly effective technique to beat length limit filter of XSS. It is converting reflected XSS to DOM-based XSS.
For example, you can find this payload in other blogs:
<script>eval(location.hash.slice(1)</script>
It executes the real payload put after the hash sign ("#") in URL. However, I think this is still too long for some cases and may be filtered the dot (".") character. So, I constructed a special payload that even shorter:
<script>eval(unescape(location))</script>
You can use it like this:
http://example.com/?message=<script>eval(unescape(location))</script>#%0Aalert('XSS');//very long code comes here
In this version, the whole URL is URL-decoded and then passed to the eval function. It is treated as valid JavaScript because:
  • http: serves as a code label here
  • // serves as a single line comment
  • %0A is the newline character after URL-decoding, so it terminates the comment
  • finally, whatever after %0A will be executed by eval function
As you see, the payload has 2 parts:
  • stager is <script>eval(unescape(location))</script>
  • stage is alert('XSS');//very long code comes here
I borrowed the concepts above from Metasploit, the stager executes first and then it calls the stage.
All character follows the hash sign will not be sent to the server, so you don't need to worry about something may be filtered like quote, double quote,...
With this, I constructed the shortest auto-trigger XSS payload I've ever seen with only 37 characters without using <script> tag:
<svg/onload=eval(unescape(location))>
And results:
Not only reflected XSS but also stored XSS which has length limit can use this technique. However, it's only when you can't put a complete payload inside the stored XSS entry.
Hope you find this post helpful.

Thank you for reading!

Comments