corazawaf / coraza-caddy

OWASP Coraza middleware for Caddy. It provides Web Application Firewall capabilities
https://www.coraza.io/
Apache License 2.0
355 stars 41 forks source link

ARGS_NAMES is Set Incorrectly with Multipart/related Request #101

Open zachgalvin opened 1 year ago

zachgalvin commented 1 year ago

I'm testing some multipart/related XML POST requests on the Coraza WAF with Caddy right now, and the requests I'm sending are getting blocked by rule 921150 (Detect newlines in argument names). After further investigation, it looks like the body of the request is getting incorrectly added to the list of ARGS_NAMES

Here is an example request (use your own hostname):

Headers:

MIME-Version: 1.0
Content-Type: multipart/related;type="application/xop+xml";boundary= "awegerhnargaY";start="<<start>>"
Host: yourtesthostname.com
Content-Length: 45

Body:
--awegerhnargaY
Content-ID: <<start>>
Content-Transfer-Encoding: 8bit
Content-Type: application/xop+xml;charset=utf-8;type="application/soap+xml"

<?xml version="1.0" encoding="UTF-8"?>
<test>
some text
<test>
--awegerhnargaY--

In the example above, the rule fires because it sees "\r" after the initial boundary. That's definitely not an argument name though, so it seems like it thinks the whole body is composed of arguments. Do you all know what may be causing this? Or could you point me to the part of the code that controls this logic so that I can further dig in from there?

From looking at the code, it seems like ARGS_NAMES comes from tx.variables.argsPost with a Post request, but I wasn't able to figure out where that gets set.

Here is the version of coraza and the coraza-caddy plugin that I'm using: dep github.com/corazawaf/coraza-caddy v1.2.2 h1:rsA7c1m5H++V6pQSv220dMl3PTMEgrsped7E3cc3Zjk= dep github.com/corazawaf/coraza/v3 v3.0.0-20230117071831-8b909c7fc345 h1:4xW94n5Lah6ca8ROtP1g9fhVHc8RW5IyMnLVp3uzkUo=

jcchavezs commented 1 year ago

coraza-caddy@v1.2.2 is way too old. Please do try https://github.com/corazawaf/coraza-caddy/releases/tag/v2.0.0-rc.3

zachgalvin commented 1 year ago

Thanks for the quick response @jcchavezs ! I'll work on testing with the newer version this week, and update this issue once that is complete.

zachgalvin commented 1 year ago

@jcchavezs I was able to reproduce this same issue on v2.0.0-rc.3.

Here is the setup I used to run the test:

The command I used to build caddy was this:

xcaddy build --with github.com/corazawaf/coraza-caddy/v2

After I did that, I ran caddy build-info, and I got the following for coraza and coraza-caddy:

dep     github.com/corazawaf/coraza-caddy/v2    v2.0.0-rc.3     h1:1oLZjJ500oUTaTH68Qurz6a3M/s0juMMkvV0WtO/RXc=
dep     github.com/corazawaf/coraza-coreruleset v0.0.0-20230723190514-7bdcbcff3d5a      h1:Lkmz2UckkFg86P65Xzet+bkt8jPMwNbIUtq73Z5Te9w=
dep     github.com/corazawaf/coraza/v3  v3.0.3  h1:VmUFZ7ep74DmtF9bwOaLKUC6AEaXtI80S2I6BQenRs0=

I created a Caddyfile with the following:

{
        debug
        order coraza_waf first
}

http://127.0.0.1:5000 {
        coraza_waf {
                directives `
                Include coraza.conf
                Include crs-setup.conf.example
                SecRuleEngine On
                Include REQUEST-901-INITIALIZATION.conf
                Include new_test.conf
                Include REQUEST-949-BLOCKING-EVALUATION.conf
                `
        }
        reverse_proxy http://127.0.0.1:5001
}

I created a really simple flask app that just returns a hello message for /test path that listens on http://127.0.0.1:5001 to use as my backend

from flask import Flask

app = Flask(__name__)

@app.route('/')
def hello():
        return 'Hello, World!'

@app.route('/test', methods=['POST','GET'])
def test():
        return 'Test!'

if __name__ == "__main__":
    app.run(port=5001)

This could be replaced by anything though that listens on 127.0.0.1 on port 5001 and returns some sort of a response.

For the config files, I got coraza.conf from the main Coraza repo on GitHub. For REQUEST-901-INITIALIZATION.conf, REQUEST-949-BLOCKING-EVALUATION.conf, and crs-setup.conf.example, I grabbed the most recent version of each of those files from the Core Rule Set GitHub repo. Lastly, new_test.conf just has rule 921150 in it:

# Detect newlines in argument names.
# Checking for GET arguments has been moved to paranoia level 2 (921151)
# in order to mitigate possible false positives.
#
# This rule is also triggered by the following exploit(s):
# [ SAP CRM Java vulnerability CVE-2018-2380 - Exploit tested: https://www.exploit-db.com/exploits/44292 ]
#
SecRule ARGS_NAMES "@rx [\n\r]" \
    "id:921150,\
    phase:2,\
    block,\
    capture,\
    t:none,t:htmlEntityDecode,\
    msg:'HTTP Header Injection Attack via payload (CR/LF detected)',\
    logdata:'Matched Data: %{TX.0} found within %{MATCHED_VAR_NAME}: %{MATCHED_VAR}',\
    tag:'application-multi',\
    tag:'language-multi',\
    tag:'platform-multi',\
    tag:'attack-protocol',\
    tag:'paranoia-level/1',\
    tag:'OWASP_CRS',\
    tag:'capec/1000/210/272/220/33',\
    ver:'OWASP_CRS/4.0.0-rc1',\
    severity:'CRITICAL',\
    setvar:'tx.http_violation_score=+%{tx.critical_anomaly_score}',\
    setvar:'tx.inbound_anomaly_score_pl1=+%{tx.critical_anomaly_score}'"

I ran my flask app script above to get that setup, and then I ran caddy with the following: ./caddy run --config Caddyfile

After that, I used the below Python script to send the request I mentioned in my first post to reproduce the error:

import requests

headers = {
        'MIME-Version': '1.0',
        'Content-Type': 'multipart/related;type="application/xop+xml";boundary= "awegerhnargaY";start="<<start>>"',
        'Host': '127.0.0.1:5000',
        'Content-Length': '45'
}

body = '''--awegerhnargaY
Content-ID: <<start>>
Content-Transfer-Encoding: 8bit
Content-Type: application/xop+xml;charset=utf-8;type="application/soap+xml"

<?xml version="1.0" encoding="UTF-8"?>
<test>
some text
<test>
--awegerhnargaY--
'''

p = requests.post('http://127.0.0.1:5000/test', data = body, headers = headers)
print(p.text)
print(p.status_code)

When I did that, I got the following error:

ERROR   http.handlers.waf       [client "127.0.0.1"] Coraza: Warning. HTTP Header Injection Attack via payload (CR/LF detected) [file "new_test.conf"] [line "1496"] [id "921150"] [rev ""] [msg "HTTP Header Injection Attack via payload (CR/LF detected)"] [data "Matched Data: \n found within ARGS_NAMES:--awegerhnargaY\nContent-ID: <<start>>\nContent-Transfer-Encoding: 8bit\nContent-Type: application/xop xml;charset: --awegerhnargaY\nContent-ID: <<start>>\nContent-T"] [severity "critical"] [ver "OWASP_CRS/4.0.0-rc1"] [maturity "0"] [accuracy "0"] [tag "application-multi"] [tag "language-multi"] [tag "platform-multi"] [tag "attack-protocol"] [tag "paranoia-level/1"] [tag "OWASP_CRS"] [tag "capec/1000/210/272/220/33"] [hostname ""] [uri "/test"] [unique_id "XQnGJJEgQuRilayD"]
jcchavezs commented 1 year ago

@M4tteoP could you please have a look at this?