owasp-modsecurity / ModSecurity

ModSecurity is an open source, cross platform web application firewall (WAF) engine for Apache, IIS and Nginx. It has a robust event-based programming language which provides protection from a range of attacks against web applications and allows for HTTP traffic monitoring, logging and real-time analysis.
https://www.modsecurity.org
Apache License 2.0
8.06k stars 1.58k forks source link

ModSecurity @inspectFile not work , Nginx set PrivateTmp=true #2904

Closed mean-cj closed 1 year ago

mean-cj commented 1 year ago

Hi

Nginx with modsecurity , @inspectFile not working because clamdscan scan file inside /tmp , result is error "No such file or directory"
but in reality nginx write temporary upload file to privateTmp directory.

Nginx temporary file upload to privateTmp directory.

ll /tmp/systemd-private-d334b42ed17b4e1fa57ecc00280187f5-nginx.service-Bp0OZg/tmp/**
-rw------- 1 nginx nginx 157077 May 18 16:45 20230518-164558-168440315843.733775-file-pbzXzg

runav.conf

SecRule FILES_TMPNAMES "@inspectFile /usr/local/bin/runav.pl" \
        "phase:2,t:none,block,msg:'Virus found in uploaded file',id:'950115',tag:'MALICIOUS_SOFTWARE/VIRUS',tag:'PCI/5.1',severity:'2',setvar:tx.anomaly_score=+%{tx.critical_anomaly_score},setvar:tx.%{rule.id}-MALICIOUS_SOFTWARE/VIRUS-%{matched_var_name}=%{tx.0}"

runav.pl

my $CLAMDSCAN = "/usr/bin/clamdscan";
my $input = `$CLAMDSCAN --stdout --no-summary $FILE`;
$input =~ m/^(.+)/;
my $error_message = $1;
print "\n\n\n------------------\n $input \n----------------------\n";

ModSecurity ::: modsec_debug.log

[168440315843.733775] [/] [4] (Rule: 913200) Executing operator "Pm" with param "bytes=0-,5-0,5-1,5-2,5-3,5-4,5-5,5-6,5-7,5-8,5-9,5-10,5-11,5-12,5-13,5-14 myvar=1234 acunetix acunetix-product acunetix-scanning-agreement acunetix-user-agreement X-Scanner x-ratproxy-loop" against REQUEST_HEADERS_NAMES|REQUEST_HEADERS.
[168440315843.733775] [/] [9] Target value: "Host" (Variable: REQUEST_HEADERS_NAMES:Host)
[168440315843.733775] [/] [9] Target value: "User-Agent" (Variable: REQUEST_HEADERS_NAMES:User-Agent)
[168440315843.733775] [/] [9] Target value: "Accept" (Variable: REQUEST_HEADERS_NAMES:Accept)
[168440315843.733775] [/] [9] Target value: "Content-Length" (Variable: REQUEST_HEADERS_NAMES:Content-Length)
[168440315843.733775] [/] [9] Target value: "Content-Type" (Variable: REQUEST_HEADERS_NAMES:Content-Type)
[168440315843.733775] [/] [9] Target value: "127.0.0.1" (Variable: REQUEST_HEADERS:Host)
[168440315843.733775] [/] [9] Target value: "curl/7.74.0" (Variable: REQUEST_HEADERS:User-Agent)
[168440315843.733775] [/] [9] Target value: "*/*" (Variable: REQUEST_HEADERS:Accept)
[168440315843.733775] [/] [9] Target value: "157264" (Variable: REQUEST_HEADERS:Content-Length)
[168440315843.733775] [/] [9] Target value: "multipart/form-data; boundary=------------------------3050ffce0ef1ff52" (Variable: REQUEST_HEADERS:Content-Type)
[168440315843.733775] [/] [4] Rule returned 0.
[168440315843.733775] [/] [9] Matched vars cleaned.
[168440315843.733775] [/] [4] (Rule: 950115) Executing operator "InspectFile" with param "/usr/local/bin/runav.pl" against FILES_TMPNAMES.
[168440315843.733775] [/] [9] Target value: "/tmp/20230518-164558-168440315843.733775-file-pbzXzg" (Variable: FILES_TMPNAMES:/tmp/20230518-164558-168440315843.733775-file-pbzXzg)

[168440315843.733775] [/] [4] InspectFile Executing: /usr/local/bin/runav.pl /tmp/20230518-164558-168440315843.733775-file-pbzXzg
[168440315843.733775] [/] [4] InspectFile Result: 

------------------
/tmp/20230518-164558-168440315843.733775-file-pbzXzg: File path check failure: No such file or directory. ERROR
------------------

[168440315843.733775] [/] [9] Matched vars updated.
[168440315843.733775] [/] [4] Running [independent] (non-disruptive) action: setvar
[168440315843.733775] [/] [8] Saving variable: TX:anomaly_score with value: 0
[168440315843.733775] [/] [4] Running [independent] (non-disruptive) action: setvar
[168440315843.733775] [/] [8] Saving variable: TX:950115-MALICIOUS_SOFTWARE/VIRUS-FILES_TMPNAMES:/tmp/20230518-164558-168440315843.733775-file-pbzXzg with value: 

ll /tmp/20230518-164558-168440315843.733775-file-pbzXzg

ls: cannot access '/tmp/20230518-164558-168440315843.733775-file-pbzXzg': No such file or directory

Add ms_dbg_a() to src/operators/inspect_file.cc for debug

please merge this function to master It's great to see the result.

bool InspectFile::evaluate(Transaction *transaction, const std::string &str) {

    if (m_isScript) {
        return m_lua.run(transaction, str);
    } else {
        FILE *in;
        char buff[512];
        std::stringstream s;
        std::string openstr;
        std::string res;

        openstr.append(m_param);
        openstr.append(" ");
        openstr.append(str);
        ms_dbg_a(transaction, 4, "InspectFile Executing: " + openstr);
        if (!(in = popen(openstr.c_str(), "r"))) {
            return false;
        }

        while (fgets(buff, sizeof(buff), in) != NULL) {
            s << buff;
        }

        pclose(in);

        res.append(s.str());

        ms_dbg_a(transaction, 4, "InspectFile Result: " + res);

        if (res.size() > 1 && res.at(0) != '1') {
            return true; /* match */
        }

        /* no match */
        return false;
    }
}
martinhsv commented 1 year ago

Hello @mean-cj ,

In summary, it looks like the issue is that: a) you have specified in ModSecurity configuration, /tmp as the directory, but b) because of your use of PrivateTmp, the actual directory is /tmp/<some directory name>/tmp

When the SecRule attempts to read the file, it doesn't know to look for (b), and is assuming that the config in (a) specifies the directory fully.

One alternative you could consider, is to not use /tmp for this functionality. For example you could use a location like /opt/modsecurity/var/upload/