coreruleset / wordpress-rule-exclusions-plugin

Rule exclusion plugin for WordPress.
Apache License 2.0
13 stars 7 forks source link

False positive on WordPress - Operation: Navigation menu update #46

Closed fabio-blanco closed 4 months ago

fabio-blanco commented 4 months ago

Description

I've found two false positives for the site navigation update operation on a Modsecurity with CRS and the wordpress-rule-exclusions-plugin that I have setup acting as a WAF for a WordPress Website.

How to reproduce the misbehavior

Just login as Admin, access the site editor > Navigation and change the menu (as an example, exchange the menu order). The problem happens when one clicks the Save button on the bottom of the page. Both false positives happens for the same action. I first solved one and then the other.

Logs

Anonymized audit log for the first false positive:

{"transaction":{"client_ip":"x.x.x.x","time_stamp":"Sun Jun 23 16:33:19 2024","server_id":"ee6660e22e1b7266d9f64d244facfa35f16ebdfe","client_port":51324,"host_ip":"x.x.x.x","host_port":8443,"unique_id":"171917119994.590543","request":{"method":"POST","http_version":2.0,"uri":"/wp-json/wp/v2/navigation/7?_locale=user","headers":{"x-wp-nonce":"bcb814b590","content-type":"application/json","user-agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36","sec-fetch-site":"same-origin","sec-ch-ua-mobile":"?0","origin":"https://mydomain.com","x-http-method-override":"PUT","accept":"application/json, */*;q=0.1","cache-control":"no-cache","sec-ch-ua":"\"Not/A)Brand\";v=\"8\", \"Chromium\";v=\"126\", \"Google Chrome\";v=\"126\"","pragma":"no-cache","sec-ch-ua-platform":"\"Linux\"","referer":"https://mydomain.com/wp-admin/site-editor.php?postId=7&postType=wp_navigation","content-length":"269","priority":"u=1, i","host":"mydomain.com","sec-fetch-mode":"cors","sec-fetch-dest":"empty","accept-encoding":"gzip, deflate, br, zstd","cookie":"wordpress_test_cookie=WP%20Cookie%20check; wp_lang=pt_BR; wp-settings-1=libraryContent%3Dbrowse; wp-settings-time-1=1718505837; cookieconsent_status=dismiss; wordpress_logged_in_0da61ab4e4440fb955c5f024aed18563=adfmb%7C1719248598%7Cmdcis24i8kJD2suIQP8pNOzAjtKcqRi40GtQu5Kxyli%7C5a32e4a0e3a95d033927f71e58f2d19b7191c9f354d8313069b64c36857f0b04","accept-language":"pt-BR,pt;q=0.9,en-US;q=0.8,en;q=0.7"}},"response":{"body":"","http_code":200,"headers":{"Access-Control-Allow-Origin":"https://mydomain.com","Cache-Control":"no-cache, must-revalidate, max-age=0, no-store, private","Expires":"Wed, 11 Jan 1984 05:00:00 GMT","X-Robots-Tag":"noindex","X-WP-Nonce":"bcb814b590","Access-Control-Allow-Headers":"Authorization, X-WP-Nonce, Content-Disposition, Content-MD5, Content-Type","Access-Control-Allow-Credentials":"true","Server":"Blessed","Server":"Blessed","X-Frame-Options":"SAMEORIGIN","Access-Control-Allow-Methods":"OPTIONS, GET, POST, PUT, PATCH, DELETE","Date":"Sun, 23 Jun 2024 19:33:19 GMT","Connection":"close","Access-Control-Expose-Headers":"X-WP-Total, X-WP-TotalPages, Link","X-Content-Type-Options":"nosniff","X-Content-Type-Options":"nosniff","Vary":"Accept-Encoding","Vary":"Origin","Content-Encoding":"gzip","Allow":"GET, POST, PUT, PATCH, DELETE","X-Powered-By":"","Content-Type":"application/json; charset=UTF-8","Link":"<https://mydomain.com/wp-json/>; rel=\"https://api.w.org/\"","X-XSS-Protection":"1; mode=block","Referrer-Policy":"no-referrer-when-downgrade","Content-Security-Policy":"default-src * data: 'unsafe-eval' 'unsafe-inline'; frame-src 'self' https: blob:;"}},"producer":{"modsecurity":"ModSecurity v3.0.12 (Linux)","connector":"ModSecurity-nginx v1.0.3","secrules_engine":"Enabled","components":["OWASP_CRS/4.2.0\""]},"messages":[{"message":"HTTP header is restricted by policy (/x-http-method-override/)","details":{"match":"Matched \"Operator `Within' with parameter `/content-encoding/ /proxy/ /lock-token/ /content-range/ /if/ /x-http-method-override/ /x-http-method/ /x-method-override/' against variable `TX:header_name_920450_x-http-method-override' (Value: `/x-http-method-override/' )","reference":"o0,10v420,10t:lowercaseo0,12v353,12t:lowercaseo0,10v239,10t:lowercaseo0,14v535,14t:lowercaseo0,16v218,16t:lowercaseo0,6v499,6t:lowercaseo0,22v443,22t:lowercaseo0,6v384,6t:lowercaseo0,13v118,13t:lowercaseo0,9v142,9t:lowercaseo0,6v101,6t:lowercaseo0,18v471,18t:lowercaseo0,7v606,7t:lowercaseo0,14v81,14t:lowercaseo0,8v794,8t:lowercaseo0,4v55,4t:lowercaseo0,14v563,14t:lowercaseo0,14v584,14t:lowercaseo0,15v700,15t:lowercaseo0,6v811,6t:lowercaseo0,15v741,15t:lowercaseo61,24","ruleId":"920450","file":"/etc/modsecurity.d/owasp-crs-wordpress/rules/REQUEST-920-PROTOCOL-ENFORCEMENT.conf","lineNumber":"1174","data":"Restricted header detected: /x-http-method-override/","severity":"2","ver":"OWASP_CRS/4.2.0","rev":"","tags":["modsecurity","modsecurity","application-multi","language-multi","platform-multi","attack-protocol","paranoia-level/1","OWASP_CRS","capec/1000/210/272","PCI/12.1"],"maturity":"0","accuracy":"0"}},{"message":"Node-Validator Deny List Keywords","details":{"match":"Matched \"Operator `Pm' with parameter `document.cookie document.domain document.write .parentnode .innerhtml window.location -moz-binding <!-- <![cdata[' against variable `ARGS:json.content' (Value: `<!-- wp:navigation-link {\"label\":\"O Autor\",\"url\":\"/author/fulano\",\"kind\":\"custom\"} /-->\\x0a\\x0a<!-- w (122 characters omitted)' )","reference":"o0,4v13,216t:utf8toUnicode,t:urlDecodeUni,t:htmlEntityDecode,t:jsDecode,t:cssDecode,t:removeNulls","ruleId":"941180","file":"/etc/modsecurity.d/owasp-crs-wordpress/rules/REQUEST-941-APPLICATION-ATTACK-XSS.conf","lineNumber":"252","data":"Matched Data: <!-- found within ARGS:json.content: <!-- wp:navigation-link {\"label\":\"O Autor\",\"url\":\"/author/fulano\",\"kind\":\"custom\"} /-->\n\n<!-- wp:navigation-link {\"label\":\"Blog\",\"type\":\"page\",\"id\":20,\"url\":\"https://mydomain.com/blog/\",\"kind\":\"post-type\"} /-->","severity":"2","ver":"OWASP_CRS/4.2.0","rev":"","tags":["modsecurity","application-multi","language-multi","platform-multi","attack-xss","xss-perf-disable","paranoia-level/1","OWASP_CRS","capec/1000/152/242"],"maturity":"0","accuracy":"0"}}]}}

Anonymized audit log for the second false positive:

{"transaction":{"client_ip":"x.x.x.x","time_stamp":"Sun Jun 23 17:31:21 2024","server_id":"a77536b873b48dbdacb2d074f0c1e64a06e1f6aa","client_port":49524,"host_ip":"x.x.x.x","host_port":8443,"unique_id":"171917468134.564060","request":{"method":"POST","http_version":2.0,"uri":"/wp-json/wp/v2/navigation/31?_locale=user","headers":{"x-wp-nonce":"bcb814b590","content-type":"application/json","user-agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36","sec-fetch-site":"same-origin","sec-ch-ua-mobile":"?0","origin":"https://mydomain.com","x-http-method-override":"PUT","accept":"application/json, */*;q=0.1","cache-control":"no-cache","sec-ch-ua":"\"Not/A)Brand\";v=\"8\", \"Chromium\";v=\"126\", \"Google Chrome\";v=\"126\"","pragma":"no-cache","sec-ch-ua-platform":"\"Linux\"","referer":"https://mydomain.com/wp-admin/site-editor.php?postId=31&postType=wp_navigation","content-length":"291","priority":"u=1, i","host":"mydomain.com","sec-fetch-mode":"cors","sec-fetch-dest":"empty","accept-encoding":"gzip, deflate, br, zstd","cookie":"wordpress_test_cookie=WP%20Cookie%20check; wp_lang=pt_BR; wp-settings-1=libraryContent%3Dbrowse; wp-settings-time-1=1718505837; cookieconsent_status=dismiss; wordpress_logged_in_0da61ab4e4440fb955c5f024aed18563=adfmb%7C1719248598%7Cmdcis24i8kJD2suIQP8pNOzAjtKcqRi40GtQu5Kxyli%7C5a32e4a0e3a95d033927f71e58f2d19b7191c9f354d8313069b64c36857f0b04","accept-language":"pt-BR,pt;q=0.9,en-US;q=0.8,en;q=0.7"}},"response":{"body":"","http_code":200,"headers":{"Access-Control-Allow-Origin":"https://mydomain.com","Cache-Control":"no-cache, must-revalidate, max-age=0, no-store, private","Expires":"Wed, 11 Jan 1984 05:00:00 GMT","X-Robots-Tag":"noindex","X-WP-Nonce":"bcb814b590","Access-Control-Allow-Headers":"Authorization, X-WP-Nonce, Content-Disposition, Content-MD5, Content-Type","Access-Control-Allow-Credentials":"true","Server":"Blessed","Server":"Blessed","X-Frame-Options":"SAMEORIGIN","Access-Control-Allow-Methods":"OPTIONS, GET, POST, PUT, PATCH, DELETE","Date":"Sun, 23 Jun 2024 20:31:21 GMT","Connection":"close","Access-Control-Expose-Headers":"X-WP-Total, X-WP-TotalPages, Link","X-Content-Type-Options":"nosniff","X-Content-Type-Options":"nosniff","Vary":"Accept-Encoding","Vary":"Origin","Content-Encoding":"gzip","Allow":"GET, POST, PUT, PATCH, DELETE","X-Powered-By":"","Content-Type":"application/json; charset=UTF-8","Link":"<https://mydomain.com/wp-json/>; rel=\"https://api.w.org/\"","X-XSS-Protection":"1; mode=block","Referrer-Policy":"no-referrer-when-downgrade","Content-Security-Policy":"default-src * data: 'unsafe-eval' 'unsafe-inline'; frame-src 'self' https: blob:;"}},"producer":{"modsecurity":"ModSecurity v3.0.12 (Linux)","connector":"ModSecurity-nginx v1.0.3","secrules_engine":"Enabled","components":["OWASP_CRS/4.2.0\""]},"messages":[{"message":"Node-Validator Deny List Keywords","details":{"match":"Matched \"Operator `Pm' with parameter `document.cookie document.domain document.write .parentnode .innerhtml window.location -moz-binding <!-- <![cdata[' against variable `ARGS:json.content' (Value: `<!-- wp:navigation-link {\"label\":\"O Autor\",\"type\":\"page\",\"url\":\"/author/fulano\",\"kind\":\"post-type\"} / (139 characters omitted)' )","reference":"o0,4v13,233t:utf8toUnicode,t:urlDecodeUni,t:htmlEntityDecode,t:jsDecode,t:cssDecode,t:removeNulls","ruleId":"941180","file":"/etc/modsecurity.d/owasp-crs-wordpress/rules/REQUEST-941-APPLICATION-ATTACK-XSS.conf","lineNumber":"252","data":"Matched Data: <!-- found within ARGS:json.content: <!-- wp:navigation-link {\"label\":\"O Autor\",\"type\":\"page\",\"url\":\"/author/fulano\",\"kind\":\"post-type\"} /-->\n\n<!-- wp:navigation-link {\"label\":\"Blog\",\"type\":\"page\",\"id\":20,\"url\":\"https://mydomain.com/blog/\",\"kind\":\"post-type\"} /-->","severity":"2","ver":"OWASP_CRS/4.2.0","rev":"","tags":["modsecurity","application-multi","language-multi","platform-multi","attack-xss","xss-perf-disable","paranoia-level/1","OWASP_CRS","capec/1000/152/242"],"maturity":"0","accuracy":"0"}}]}

Your Environment

Confirmation

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

Possible solution

Exclusion rule for the second false positive:

# Updating wordpress website navigation
SecRule REQUEST_URI "@beginsWith /wp-json/wp/v2/navigation/" \
    "id:1002,\
    phase:1,\
    pass,\
    t:none,\
    nolog,\
    chain"
    SecRule REQUEST_HEADERS:x-http-method-override "@streq PUT" \
        "t:none,\
        ctl:ruleRemoveById=920450"

Exclusion rule for the second false positive:

# Updating wordpress website navigation
SecRule REQUEST_URI "@beginsWith /wp-json/wp/v2/navigation/" \
    "id:1003,\
    phase:2,\
    pass,\
    t:none,\
    nolog,\
    chain"
    SecRule ARGS:json.content "@pm <!--" \
        "t:none,t:utf8toUnicode,t:urlDecodeUni,t:htmlEntityDecode,t:jsDecode,t:cssDecode,t:removeNulls,\
        ctl:ruleRemoveById=941180"

Note: It is interesting to try to reproduce the problem also in other contexts. I think that, especially the second false positive may be affecting other places since full site editing and block template themes uses a lot of <!-- ... --> type of content everywhere.

azurit commented 4 months ago

@fabio-blanco Hi and thanks for reporting. Can you try this PR?