Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ClamAV with mod security #1610

Closed
intelbg opened this issue Nov 3, 2017 · 8 comments
Closed

ClamAV with mod security #1610

intelbg opened this issue Nov 3, 2017 · 8 comments
Assignees

Comments

@intelbg
Copy link

intelbg commented Nov 3, 2017

Hello,
I wondered if this is the right place where to ask and I am sorry if it's not. I would like to use clamAV scanning with mod security in nginx and found the following guide:

https://malware.expert/scan-every-file-clam-antivirus-scanner-modsecurity/

Although I receive the following error on the rule:

SecRule FILES_TMPNAMES "@inspectFile /usr/local/bin/runav.pl"
"phase:2,t:none,block,msg:'Virus found in uploaded file',id:'399999'"

Operator: InspectFile is not yet supported. Is it possible to use another operator to accomplish this task or is it planned to be implemented? I am using libmodsecurity version 3 with nginx connector and cars v3 too.

Thank you in advance.

@zimmerle zimmerle self-assigned this Nov 6, 2017
@zimmerle
Copy link
Contributor

zimmerle commented Nov 6, 2017

Hi @intelbg,

Please check the most recent version of v3/master. The code was recently implemented there.

@zimmerle zimmerle closed this as completed Nov 6, 2017
@intelbg
Copy link
Author

intelbg commented Nov 7, 2017

I did git clone -b v3/master https://github.com/SpiderLabs/ModSecurity
and recompiled it again, but still the operator is missing. I a wrong in doing the git clone or I should use the branch v3/dev/inspectFile. Are this branches merged?

@intelbg
Copy link
Author

intelbg commented Nov 14, 2017

Can you please provide me md5 sum of the specific file?

@zimmerle
Copy link
Contributor

@intelbg make sure you are cloning the repository into an empty directory.

@intelbg
Copy link
Author

intelbg commented Nov 14, 2017

@zimmerle I am sure it's cloned in empty directory. Is my command for git clone right?

@victorhora
Copy link
Contributor

@intelbg inspecFile support was added at 1866a3a. See https://github.com/SpiderLabs/ModSecurity/blob/v3/master/src/operators/inspect_file.cc for reference.

"$ git clone -b v3/master https://github.com/SpiderLabs/ModSecurity" should work to clone to a new directory. To update your local repository with the current master "$git pull" should do the trick.

@defanator
Copy link
Contributor

Hi @intelbg, @zimmerle, @victorhora,

I've been digging into libmodsecurity + clamav integration, and googled to this issue, so I decided to post some of my results here instead of creating another one.

The v3/master branch, as of 480a2f8, does have working support for the @inspectFile operator.

Test env:

root@vagrant:/# lsb_release -a
No LSB modules are available.
Distributor ID:	Ubuntu
Description:	Ubuntu 16.04.4 LTS
Release:	16.04
Codename:	xenial
root@vagrant:/# dpkg -l | fgrep clamav
ii  clamav                              0.99.4+addedllvm-0ubuntu0.16.04.1          amd64        anti-virus utility for Unix - command-line interface
ii  clamav-base                         0.99.4+addedllvm-0ubuntu0.16.04.1          all          anti-virus utility for Unix - base package
ii  clamav-daemon                       0.99.4+addedllvm-0ubuntu0.16.04.1          amd64        anti-virus utility for Unix - scanner daemon
ii  clamav-freshclam                    0.99.4+addedllvm-0ubuntu0.16.04.1          amd64        anti-virus utility for Unix - virus database update utility
ii  libclamav7                          0.99.4+addedllvm-0ubuntu0.16.04.1          amd64        anti-virus utility for Unix - library

ModSecurity config part:

root@vagrant:/# cat /etc/nginx/modsec/main.conf 
Include /etc/nginx/modsec/modsecurity.conf

SecRule FILES_TMPNAMES "@inspectFile /usr/local/bin/runav.pl" \
"phase:2,t:none,deny,msg:'Virus found in uploaded file',id:'399999'"

The /usr/local/bin/runav.pl script - note that it was modified ("1" and "0" in output have been swapped around, see below) and a bit differ from the "official" one at https://github.com/SpiderLabs/owasp-modsecurity-crs/blob/v3.0/master/util/av-scanning/runav.pl:

#!/usr/bin/env perl
#
# runav.pl
# Copyright (c) 2004-2011 Trustwave
#
# This script is an interface between ModSecurity and its
# ability to intercept files being uploaded through the
# web server and ClamAV.

$CLAMDSCAN = "clamdscan";

if ($#ARGV != 0) {
    print "Usage: runav.pl <filename>\n";
    exit;
}

my ($FILE) = shift @ARGV;

$cmd = "$CLAMDSCAN --stdout --no-summary $FILE";

$input = `$cmd`;
$input =~ m/^(.+)/;
$error_message = $1;

$output = "1 Unable to parse clamscan output [$1]";

if ($error_message =~ m/: Empty file\.?$/) {
    $output = "0 empty file";
}
elsif ($error_message =~ m/: (.+) ERROR$/) {
    $output = "1 clamscan: $1";
}
elsif ($error_message =~ m/: (.+) FOUND$/) {
    $output = "1 clamscan: $1";
}
elsif ($error_message =~ m/: OK$/) {
    $output = "0 clamscan: OK";
}

print "$output\n";

Test request for good file:

root@vagrant:/# curl -i -F 'upload=@"/tmp/configs.tar.gz"' http://localhost/modsec-full/upload/a/b/c/
HTTP/1.1 100 Continue

HTTP/1.1 200 OK
Server: nginx/1.13.9
Date: Tue, 20 Mar 2018 11:49:43 GMT
Content-Type: text/plain
Content-Length: 38
Connection: keep-alive

5522:4d3d67931da1f3de75c5b156140b174b

Test request for bad file (http://www.eicar.org/86-0-Intended-use.html):

root@vagrant:/# curl -i -F 'upload=@"/tmp/eicar.com"' http://localhost/modsec-full/upload/a/b/c/
HTTP/1.1 100 Continue

HTTP/1.1 403 Forbidden
Server: nginx/1.13.9
Date: Tue, 20 Mar 2018 11:50:34 GMT
Content-Type: text/html
Content-Length: 169
Connection: keep-alive

<html>
<head><title>403 Forbidden</title></head>
<body bgcolor="white">
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.13.9</center>
</body>
</html>

Log for bad file:

2018/03/20 04:50:34 [info] 18473#18473: *6 ModSecurity: Access denied with code %d (phase 2). Matched "Operator `InspectFile' with parameter `/usr/local/bin/runav.pl' against variable `FILES_TMPNAMES:eicar.com' (Value: `eicar.com' ) [file "/etc/nginx/modsec/main.conf"] [line "31"] [id "399999"] [rev ""] [msg "Virus found in uploaded file"] [data ""] [severity "0"] [ver ""] [maturity "0"] [accuracy "0"] [hostname "127.0.0.1"] [uri "/modsec-full/upload/a/b/c/"] [unique_id "152154663498.971791"] [ref "v321,9"], client: 127.0.0.1, server: , request: "POST /modsec-full/upload/a/b/c/ HTTP/1.1", host: "localhost"
2018/03/20 04:50:34 [warn] 18473#18473: *6 [client 127.0.0.1] ModSecurity: Warning. Matched "Operator `InspectFile' with parameter `/usr/local/bin/runav.pl' against variable `FILES_TMPNAMES:eicar.com' (Value: `eicar.com' ) [file "/etc/nginx/modsec/main.conf"] [line "31"] [id "399999"] [rev ""] [msg "Virus found in uploaded file"] [data ""] [severity "0"] [ver ""] [maturity "0"] [accuracy "0"] [hostname "127.0.0.1"] [uri "/modsec-full/upload/a/b/c/"] [unique_id "152154663498.971791"] [ref "v321,9"], client: 127.0.0.1, server: , request: "POST /modsec-full/upload/a/b/c/ HTTP/1.1", host: "localhost"

Also, every run of external script from @inspectFile operator produces two log lines in nginx error log (level "notice"):

2018/03/20 05:26:10 [notice] 18473#18473: signal 17 (SIGCHLD) received from 18603
2018/03/20 05:26:10 [notice] 18473#18473: unknown process 18603 exited with code 0

The 18473 in the above example is a pid of nginx worker process - so there is a fork/exec every time @inspectFile is being iterated, and obviously this blocks nginx worker during the execution time.

Finally, I had to modify runav.pl to swap around "1" and "0" in output in order to make it work as expected:

  • trigger rule when ClamAV finds a virus,
  • do not trigger rule when ClamAV reports OK (or a file was empty).

Corresponding parts of ModSecurity debug log, good case:

[4] Starting phase REQUEST_BODY. (SecRules 2)
[9] Multipart: Boundary: ------------------------de27167d9e098491
[9] Multipart: Added part header "Content-Disposition" "form-data; name="upload"; filename="configs.tar.gz"".
[9] Multipart: Added part header "Content-Type" "application/octet-stream".
[9] Multipart: Content-Disposition name: upload.
[9] Multipart: Content-Disposition filename: configs.tar.gz.
[9] Multipart: Added file part to the list: name "upload" file name "configs.tar.gz" (offset 160, length 5314)
[4] Multipart: Cleanup started (remove files Not set)
[9] This phase consists of 5 rule(s).
[4] (Rule: 200002) Executing operator "Eq" with param "0" against REQBODY_ERROR.
[9] Target value: "0" (Variable: REQBODY_ERROR)
[4] Rule returned 0.
[9] Matched vars cleaned.
[4] (Rule: 200003) Executing operator "Eq" with param "0" against MULTIPART_STRICT_ERROR.
[9] Target value: "0" (Variable: MULTIPART_STRICT_ERROR)
[4] Rule returned 0.
[9] Matched vars cleaned.
[4] (Rule: 200004) Executing operator "Eq" with param "0" against MULTIPART_UNMATCHED_BOUNDARY.
[9] Target value: "0" (Variable: MULTIPART_UNMATCHED_BOUNDARY)
[4] Rule returned 0.
[9] Matched vars cleaned.
[4] (Rule: 200005) Executing operator "StrEq" with param "0" against TX:regex(^MSC_).
[4] Rule returned 0.
[9] Matched vars cleaned.
[4] (Rule: 399999) Executing operator "InspectFile" with param "/usr/local/bin/runav.pl" against FILES_TMPNAMES.
[9] Target value: "configs.tar.gz" (Variable: FILES_TMPNAMES:configs.tar.gz)
[4] Rule returned 0.
[9] Matched vars cleaned.

Bad case:

[4] Starting phase REQUEST_BODY. (SecRules 2)
[9] Multipart: Boundary: ------------------------7379d86185e7c6b7
[9] Multipart: Added part header "Content-Disposition" "form-data; name="upload"; filename="eicar.com"".
[9] Multipart: Added part header "Content-Type" "application/octet-stream".
[9] Multipart: Content-Disposition name: upload.
[9] Multipart: Content-Disposition filename: eicar.com.
[9] Multipart: Added file part to the list: name "upload" file name "eicar.com" (offset 155, length 68)
[4] Multipart: Cleanup started (remove files Not set)
[9] This phase consists of 5 rule(s).
[4] (Rule: 200002) Executing operator "Eq" with param "0" against REQBODY_ERROR.
[9] Target value: "0" (Variable: REQBODY_ERROR)
[4] Rule returned 0.
[9] Matched vars cleaned.
[4] (Rule: 200003) Executing operator "Eq" with param "0" against MULTIPART_STRICT_ERROR.
[9] Target value: "0" (Variable: MULTIPART_STRICT_ERROR)
[4] Rule returned 0.
[9] Matched vars cleaned.
[4] (Rule: 200004) Executing operator "Eq" with param "0" against MULTIPART_UNMATCHED_BOUNDARY.
[9] Target value: "0" (Variable: MULTIPART_UNMATCHED_BOUNDARY)
[4] Rule returned 0.
[9] Matched vars cleaned.
[4] (Rule: 200005) Executing operator "StrEq" with param "0" against TX:regex(^MSC_).
[4] Rule returned 0.
[9] Matched vars cleaned.
[4] (Rule: 399999) Executing operator "InspectFile" with param "/usr/local/bin/runav.pl" against FILES_TMPNAMES.
[9] Target value: "eicar.com" (Variable: FILES_TMPNAMES:eicar.com)
[9] Matched vars updated.
[4] Running [independent] (non-disruptive) action: msg
[9] Saving msg: Virus found in uploaded file
[4] Rule returned 1.

That makes me think that there is an error in https://raw.githubusercontent.com/SpiderLabs/owasp-modsecurity-crs/v3.0/master/util/av-scanning/runav.pl - but I'm not sure as I don't have any knowledge on how @inspectFile is working in ModSecurity 2.x - are there any differences in parsing stdout, or in something else?

Hope this helps someone.

@victorhora
Copy link
Contributor

For future reference: #1646 (comment)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants