Owasp-modsecurity-crs: XSS False Positives - GB stores html comments in json

Created on 16 Feb 2019  Â·  7Comments  Â·  Source: SpiderLabs/owasp-modsecurity-crs


Type of Issue

False positives - hitting XSS rules.

Running Modsec behind a reverse proxy http cache.

Description






---OgqPdJlU---A--
[16/Feb/2019:09:15:21 +0000] 15503085215.783826 127.0.0.1 39696 127.0.0.1 8080
---OgqPdJlU---B--
POST /index.php?rest_route=%2Fwp%2Fv2%2Fposts%2F50%2Fautosaves&_locale=user HTTP/1.1
Accept-Encoding: gzip, deflate
X-Original-Scheme: http
Cookie: wordpress_test_cookie=WP+Cookie+check; wordpress_logged_in_b6b989e68e8dac9f08763f7c0a28aa33=jeff%7C1550413361%7C911AK3V3QYGrxr7F8Y0BwudItGseKcW7jjuCGV5YWtp%7Ce60a48e65db80d1ab9759a7d09a05bd0a0defa6e8f06e3a7ffa886fae95d3465; wp-settings-time-1=1550308493
Accept: application/json, /;q=0.1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:66.0) Gecko/20100101 Firefox/66.0
Connection: close
X-Original-Host: serious-wordpress.site
X-Forwarded-For: 208.167.237.62
Accept-Language: en-GB,en;q=0.5
Content-Length: 125
Host: serious-wordpress.site
Referer: http://serious-wordpress.site/wp-admin/post.php?post=50&action=edit
X-WP-Nonce: 0e4c5f207b
Content-Type: application/json
Origin: http://serious-wordpress.site

---OgqPdJlU---D--

---OgqPdJlU---E--
\x0d\x0a403 Forbidden\x0d\x0a\x0d\x0a

403 Forbidden
\x0d\x0a
nginx
\x0d\x0a\x0d\x0a\x0d\x0a

---OgqPdJlU---F--
HTTP/1.1 403
Server: nginx
Date: Sat, 16 Feb 2019 09:15:21 GMT
Content-Length: 146
Content-Type: text/html
Connection: close

---OgqPdJlU---H--
ModSecurity: Warning. Matched "Operator Pm' with parameterdocument.cookie document.write document[ self[ .parentnode .innerhtml window.location -moz-binding ARGS:json.content' (Value:\x0a

Is this something new

\x0a' ) [file "/etc/nginx/modsec/owasp-modsecurity-crs-3.1.0/rules/REQUEST-941-APPLICATION-ATTACK-XSS.conf"] [line "254"] [id "941180"] [rev ""] [msg "Node-Validator Blacklist Keywords"] [data "Matched Data: \x0a

is this something new

\x0a"] [severity "2"] [ver "OWASP_CRS/3.1.0"] [maturity "0"] [accuracy "0"] [tag "application-multi"] [tag "language-multi"] [tag "platform-multi"] [tag "attack-xss"] [tag "OWASP_CRS/WEB_ATTACK/XSS"] [tag "WASCTC/WASC-8"] [tag "WASCTC/WASC-22"] [tag "OWASP_TOP_10/A3"] [tag "OWASP_AppSensor/IE1"] [tag "CAPEC-242"] [hostname "127.0.0.1"] [uri "/index.php"] [unique_id "15503085215.783826"] [ref "o0,4v13,73t:utf8toUnicode,t:urlDecodeUni,t:htmlEntityDecode,t:jsDecode,t:cssDecode,t:lowercase,t:removeNulls"]
ModSecurity: Access denied with code 403 (phase 2). Matched "Operator Ge' with parameter5' against variable TX:ANOMALY_SCORE' (Value:5' ) [file "/etc/nginx/modsec/owasp-modsecurity-crs-3.1.0/rules/REQUEST-949-BLOCKING-EVALUATION.conf"] [line "80"] [id "949110"] [rev ""] [msg "Inbound Anomaly Score Exceeded (Total Score: 5)"] [data ""] [severity "2"] [ver ""] [maturity "0"] [accuracy "0"] [tag "application-multi"] [tag "language-multi"] [tag "platform-multi"] [tag "attack-generic"] [hostname "127.0.0.1"] [uri "/index.php"] [unique_id "15503085215.783826"] [ref ""]

Your Environment



CRS 3.1.0
ModSec 3
Nginx ModSecurity Connector
Nginx 1.15.8

In an effort to aid performance of ModSec, I am following Trustwaves old advice or running it behind a http cache.

https://www.trustwave.com/en-us/resources/blogs/spiderlabs-blog/modsecurity-performance-recommendations/

I am using Nginx, so instead of putting Varnish up front I am attempting to reverse proxy back to Nginx (same as I might with using it as http terminator infront of varnish)

I have the cache all working, and modsec is working in the back.

However I am having issues with Gutenberg saves. I initially thought it was because the rules exclusions don't include WordPress API endpoints.

So I included the rules from here
https://github.com/SpiderLabs/owasp-modsecurity-crs/pull/1298

But I have been looking in the audit and it seems I am hitting Args:json/content but specifically XSS rules.

I am passing the host back, everything looks fine in inspector, but perhaps I am missing something - seems modsec is viewing everything from 127.0.0.1:8080, could this be why its triggering XSS?

Alternatively, I have tried:

SecRule REQUEST_FILENAME "@rx ^/wp/v[0-9]+/(?:posts|pages)" \
    "id:9002141,\
    phase:1,\
    pass,\
    t:none,\
    nolog,\
    ctl:ruleRemoveTargetByTag=CRS;ARGS:content,\
    ctl:ruleRemoveTargetByTag=CRS;ARGS:json.content,\
    ctl:ruleRemoveById=941180"

If there is nothing to be done with fixing the reverse proxy set up, then I would need a bit of guidance on correcting my exclusion to exclude this XSS protection rule.

Any help would be very much appreciated.

Confirmation

[ X] I have removed any personal data (email addresses, IP addresses,
passwords, domain names) from any logs posted.

All 7 comments

Seems looking at the rule and my log outpute

https://github.com/SpiderLabs/owasp-modsecurity-crs/blob/v3.2/dev/rules/REQUEST-941-APPLICATION-ATTACK-XSS.conf

[data "Matched Data: <!-- found within ARGS:json.content: <!-- wp:paragraph -->\x0a<p>hello</p>\x0a<!-- /wp:paragraph -->"]

It seems the Rule is flagging the html comment <!-- within the json.

Problem is that Gutenberg is storing all block data in the post_content field... and wrapping it in <!-- html comments -->

(I agree - very stupid)

I am trying to exclude this specific rule on post pages using Gutenberg but having no luck so far. (Brand new to modsec and also not the brightest spark :D )

For me re the XSS

this is working

SecRule REQUEST_URI "@contains /wp/v2/posts" \
    "id:1,\
    phase:2,\
    pass,\
    t:none,\
    nolog,\
    ctl:ruleRemoveById=941180"

SecRule REQUEST_URI "@contains /wp/v2/pages" \
    "id:2,\
    phase:2,\
    pass,\
    t:none,\
    nolog,\
    ctl:ruleRemoveById=941180"

Tried regex

SecRule REQUEST_URI "@rx ^\/wp\/v[0-9]+\/(?:posts|pages)" \
    "id:3,\
    phase:1,\
    pass,\
    t:none,\
    nolog,\
    ctl:ruleRemoveTargetByTag=CRS;ARGS:content,\
    ctl:ruleRemoveTargetByTag=CRS;ARGS:json.content,\
    ctl:ruleRemoveById=941180"

and

SecRule REQUEST_URI "@rx ^/wp/v[0-9]+/(?:posts|pages)" \
    "id:3,\
    phase:1,\
    pass,\
    t:none,\
    nolog,\
    ctl:ruleRemoveTargetByTag=CRS;ARGS:content,\
    ctl:ruleRemoveTargetByTag=CRS;ARGS:json.content,\
    ctl:ruleRemoveById=941180"

But no joy that way

regex seems okay, I thought it might have been a lack of escaping but doesn't seem these rules need them.

It's working, but any pointers would be great.

We run a WordPress focused SaaS VPS control Panel, lots of our users are using RestAPI based plugins now, so I need to get my head around these rules, and probably write some exclusions for the most popular ones that are hitting these errors.

Sorry for the inconvenience @JeffCleverley. But well done with the attempts are rule exclusions. The problem with wordpress and other CMS systems are evident.

We have provided WP rule exclusions, but they are very limited in their scope and I am not surprised Gutenberg is not covered. Are you interested to give a hand to complete your setup in this regard?

Yeah certainly.

I am actually right in the middle of building our next stack, that is why I am integrating ModSec/OWASP

I have things working with the above two rules for GB. Everything else re vanilla WP seems fine.

Hopefully by next week we will have it rolling out to our dev plan clients as 'beta' for them to test their most used plugins etc. I

I wrote the whitelist from inspecting the audit log, I figure it will just be a case of rinse and repeat and maintaining list of rules for the most used plugins etc.

I expect I will need to develop a bunch of other rules as I am going for both those plugins and as WordPress extends the reach of the Rest API further through wp-admin.

I will be spending the next 2 - 3 weeks rolling out the stack on beta and tweaking. I will contribute anything back.

Thank you very much in advance. I'm overly busy at the moment, but if there is anything, we can try to get you some support.

Thanks for the heads-up @JeffCleverley , it turns out that WordPress has a 'backup' route /index.php?rest_route=... for installations without pretty URLs /wp/v2/...

For sites without “pretty permalinks” enabled, /wp-json/ isn’t automatically handled by WordPress. This means that normal/default WordPress permalinks will be used instead. These headers look more like this:

Link: http://example.com/?rest_route=/; rel="https://api.w.org/"
Clients should keep this variation in mind and ensure that both routes can be handled seamlessly.

https://developer.wordpress.org/rest-api/using-the-rest-api/discovery/

We are now only excluding the /wp/v2/... endpoint. So we should also add the /index.php?rest_route... URL pattern to the Gutenberg exclusion.

I'll have a stab at this very soon.

Solved in pull request #1330 , thanks for submitting this report! :)

Was this page helpful?
0 / 5 - 0 ratings