Closed
Description
I always run into a MULTIPART_UNMATCHED_BOUNDARY error when I upload any kind of file.
The test environment is @defanator's Vagrant image.
Components:
test@vagrant:~/ModSecurity$ git describe
v3.0.4-118-g4127c1bf
test@vagrant:~/ModSecurity-nginx$ git describe
v1.0.1-23-g21bc821
/usr/sbin/nginx -V
nginx version: nginx/1.19.6
built by gcc 8.3.0 (Debian 8.3.0-6)
built with OpenSSL 1.1.1d 10 Sep 2019
TLS SNI support enabled
configure arguments: --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib/nginx/modules \
--conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log \
--http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock \
--http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp \
--http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp \
--http-scgi-temp-path=/var/cache/nginx/scgi_temp --user=nginx --group=nginx --with-compat --with-file-aio \
--with-threads --with-http_addition_module --with-http_auth_request_module --with-http_dav_module \
--with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_mp4_module \
--with-http_random_index_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module \
--with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-mail \
--with-mail_ssl_module --with-stream --with-stream_realip_module --with-stream_ssl_module \
--with-stream_ssl_preread_module --add-dynamic-module=../ModSecurity-nginx
This is what I modified:
diff --git a/states/files/etc/nginx/fastcgi_params b/states/files/etc/nginx/fastcgi_params
new file mode 100644
index 0000000..28decb9
--- /dev/null
+++ b/states/files/etc/nginx/fastcgi_params
@@ -0,0 +1,25 @@
+
+fastcgi_param QUERY_STRING $query_string;
+fastcgi_param REQUEST_METHOD $request_method;
+fastcgi_param CONTENT_TYPE $content_type;
+fastcgi_param CONTENT_LENGTH $content_length;
+
+fastcgi_param SCRIPT_NAME $fastcgi_script_name;
+fastcgi_param REQUEST_URI $request_uri;
+fastcgi_param DOCUMENT_URI $document_uri;
+fastcgi_param DOCUMENT_ROOT $document_root;
+fastcgi_param SERVER_PROTOCOL $server_protocol;
+fastcgi_param REQUEST_SCHEME $scheme;
+fastcgi_param HTTPS $https if_not_empty;
+
+fastcgi_param GATEWAY_INTERFACE CGI/1.1;
+fastcgi_param SERVER_SOFTWARE nginx/$nginx_version;
+
+fastcgi_param REMOTE_ADDR $remote_addr;
+fastcgi_param REMOTE_PORT $remote_port;
+fastcgi_param SERVER_ADDR $server_addr;
+fastcgi_param SERVER_PORT $server_port;
+fastcgi_param SERVER_NAME $server_name;
+
+# PHP only, required if PHP was built with --enable-force-cgi-redirect
+fastcgi_param REDIRECT_STATUS 200;
diff --git a/states/files/etc/nginx/modsec/main.conf b/states/files/etc/nginx/modsec/main.conf
index 16af6e5..fc181a0 100644
--- a/states/files/etc/nginx/modsec/main.conf
+++ b/states/files/etc/nginx/modsec/main.conf
@@ -1,5 +1,5 @@
include /etc/nginx/modsec/modsecurity.conf
# OWASP CRS rules
-include /etc/nginx/modsec/owasp-crs/crs-setup.conf
-include /etc/nginx/modsec/owasp-crs/rules/*.conf
+#include /etc/nginx/modsec/owasp-crs/crs-setup.conf
+#include /etc/nginx/modsec/owasp-crs/rules/*.conf
diff --git a/states/files/etc/nginx/modsec/modsecurity.conf b/states/files/etc/nginx/modsec/modsecurity.conf
new file mode 100644
index 0000000..284e543
--- /dev/null
+++ b/states/files/etc/nginx/modsec/modsecurity.conf
@@ -0,0 +1,49 @@
+SecRuleEngine On
+SecRequestBodyAccess On
+SecRule REQUEST_HEADERS:Content-Type "(?:application(?:/soap\+|/)|text/)xml" \
+ "id:'200000',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=XML"
+SecRule REQUEST_HEADERS:Content-Type "application/json" \
+ "id:'200001',phase:1,t:none,t:lowercase,pass,nolog,ctl:requestBodyProcessor=JSON,ctl:ruleRemoveById=930120"
+SecRequestBodyLimit 13107200
+SecRequestBodyNoFilesLimit 131072
+SecRequestBodyLimitAction Reject
+SecRule REQBODY_ERROR "!@eq 0" \
+"id:'200002', phase:2,t:none,log,deny,status:400,msg:'Failed to parse request body.',logdata:'%{reqbody_error_msg}',severity:2"
+SecRule MULTIPART_STRICT_ERROR "!@eq 0" \
+"id:'200003',phase:2,t:none,log,deny,status:400, \
+msg:'Multipart request body failed strict validation: \
+PE %{REQBODY_PROCESSOR_ERROR}, \
+BQ %{MULTIPART_BOUNDARY_QUOTED}, \
+BW %{MULTIPART_BOUNDARY_WHITESPACE}, \
+DB %{MULTIPART_DATA_BEFORE}, \
+DA %{MULTIPART_DATA_AFTER}, \
+HF %{MULTIPART_HEADER_FOLDING}, \
+LF %{MULTIPART_LF_LINE}, \
+SM %{MULTIPART_MISSING_SEMICOLON}, \
+IQ %{MULTIPART_INVALID_QUOTING}, \
+IP %{MULTIPART_INVALID_PART}, \
+IH %{MULTIPART_INVALID_HEADER_FOLDING}, \
+FL %{MULTIPART_FILE_LIMIT_EXCEEDED}'"
+SecRule MULTIPART_UNMATCHED_BOUNDARY "@gt 1" \
+ "id:'200004',phase:2,t:none,log,deny,msg:'Multipart parser detected a possible unmatched boundary.'"
+SecPcreMatchLimit 1000
+SecPcreMatchLimitRecursion 1000
+SecRule TX:/^MSC_/ "!@streq 0" \
+ "id:'200005',phase:2,t:none,deny,msg:'ModSecurity internal error flagged: %{MATCHED_VAR_NAME}'"
+SecResponseBodyAccess On
+SecResponseBodyMimeType text/plain text/html text/xml
+SecResponseBodyLimit 524288
+SecResponseBodyLimitAction ProcessPartial
+SecTmpDir /tmp/
+SecDataDir /tmp/
+SecDebugLog /var/log/nginx/modsec_debug.log
+SecDebugLogLevel 9
+SecAuditEngine On
+SecAuditLogRelevantStatus "^(?:5|4(?!04))"
+SecAuditLogParts ABCDEFHIJZ
+SecAuditLogType Serial
+SecAuditLog /var/log/modsec_audit.log
+SecArgumentSeparator &
+SecCookieFormat 0
+SecUnicodeMapFile unicode.mapping 20127
+SecStatusEngine On
diff --git a/states/files/etc/nginx/nginx.conf b/states/files/etc/nginx/nginx.conf
index 15cd224..0cb2772 100644
--- a/states/files/etc/nginx/nginx.conf
+++ b/states/files/etc/nginx/nginx.conf
@@ -33,6 +33,10 @@ http {
server 127.0.0.1:3131;
}
+ upstream php-fpm {
+ server unix:/run/php/php7.3-fpm.sock;
+ }
+
server {
listen 8080 default_server backlog=32000;
location / {
@@ -67,6 +71,8 @@ http {
include uwsgi_params;
uwsgi_pass upload-app;
client_max_body_size 256m;
+ modsecurity on;
+ modsecurity_rules_file /etc/nginx/modsec/main.conf;
}
location /modsec-light/ {
@@ -94,6 +100,15 @@ http {
include uwsgi_params;
uwsgi_pass upload-app;
}
+ location ~ \.php$ {
+ modsecurity on;
+ modsecurity_rules_file /etc/nginx/modsec/main.conf;
+ fastcgi_pass php-fpm;
+ fastcgi_index index.php;
+ fastcgi_param SCRIPT_FILENAME /var/www/html/$fastcgi_script_name;
+ fastcgi_intercept_errors on;
+ include fastcgi_params;
+ }
}
include /etc/nginx/conf.d/*.conf;
Short summary of modifications:
- added fastcgi_params to nginx
- turned off CRS
- added default
modsecurity.conf
- few things were modifed - set up PHP FPM
Here is the PHP file what I used:
# cat /var/www/html/index.php
<?php
var_dump($_POST);
The file what I try to upload:
$ cat text.txt
test text
curl commands:
$ curl -vvv "http://localhost/upload/" -F "data=@text.txt"
* Expire in 0 ms for 6 (transfer 0x56389b68df50)
...
* Expire in 1 ms for 1 (transfer 0x56389b68df50)
* Trying ::1...
* TCP_NODELAY set
* Expire in 149998 ms for 3 (transfer 0x56389b68df50)
* Expire in 200 ms for 4 (transfer 0x56389b68df50)
* connect to ::1 port 80 failed: Connection refused
* Trying 127.0.0.1...
* TCP_NODELAY set
* Expire in 149998 ms for 3 (transfer 0x56389b68df50)
* Connected to localhost (127.0.0.1) port 80 (#0)
> POST /upload/ HTTP/1.1
> Host: localhost
> User-Agent: curl/7.64.0
> Accept: */*
> Content-Length: 196
> Content-Type: multipart/form-data; boundary=------------------------0e87336e7e623e42
>
< HTTP/1.1 403 Forbidden
< Server: nginx/1.19.6
< Date: Thu, 01 Apr 2021 19:06:25 GMT
< Content-Type: text/html
< Content-Length: 153
< Connection: keep-alive
* HTTP error before end of send, stop sending
<
<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.19.6</center>
</body>
</html>
* Closing connection 0
in the error.log:
2021/04/01 19:06:25 [error] 3329#3329: *2 [client 127.0.0.1] ModSecurity: Access denied with code 403 (phase 2). Matched "Operator `Gt' with parameter `1' against variable `MULTIPART_UNMATCHED_BOUNDARY' (Value: `2' ) [file "/etc/nginx/modsec/modsecurity.conf"] [line "15"] [id "200004"] [rev ""] [msg "Multipart parser detected a possible unmatched boundary."] [data ""] [severity "0"] [ver ""] [maturity "0"] [accuracy "0"] [hostname "127.0.0.1"] [uri "/upload/"] [unique_id "1617303985"] [ref "v180,1"], client: 127.0.0.1, server: , request: "POST /upload/ HTTP/1.1", host: "localhost"
Note: this request has sent to /upload/
which was already configured.
Another curl command:
$ curl -vvv "http://localhost/index.php" -F "data=@text.txt"
* Expire in 0 ms for 6 (transfer 0x558bc0435f50)
...
* Expire in 0 ms for 1 (transfer 0x558bc0435f50)
* Trying ::1...
* TCP_NODELAY set
* Expire in 149999 ms for 3 (transfer 0x558bc0435f50)
* Expire in 200 ms for 4 (transfer 0x558bc0435f50)
* connect to ::1 port 80 failed: Connection refused
* Trying 127.0.0.1...
* TCP_NODELAY set
* Expire in 149998 ms for 3 (transfer 0x558bc0435f50)
* Connected to localhost (127.0.0.1) port 80 (#0)
> POST /index.php HTTP/1.1
> Host: localhost
> User-Agent: curl/7.64.0
> Accept: */*
> Content-Length: 196
> Content-Type: multipart/form-data; boundary=------------------------9dd881243a0bfb97
>
< HTTP/1.1 403 Forbidden
< Server: nginx/1.19.6
< Date: Thu, 01 Apr 2021 19:08:37 GMT
< Content-Type: text/html
< Content-Length: 153
< Connection: keep-alive
* HTTP error before end of send, stop sending
<
<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.19.6</center>
</body>
</html>
* Closing connection 0
in the error log I got:
2021/04/01 19:08:37 [error] 3329#3329: *3 [client 127.0.0.1] ModSecurity: Access denied with code 403 (phase 2). Matched "Operator `Gt' with parameter `1' against variable `MULTIPART_UNMATCHED_BOUNDARY' (Value: `2' ) [file "/etc/nginx/modsec/modsecurity.conf"] [line "15"] [id "200004"] [rev ""] [msg "Multipart parser detected a possible unmatched boundary."] [data ""] [severity "0"] [ver ""] [maturity "0"] [accuracy "0"] [hostname "127.0.0.1"] [uri "/index.php"] [unique_id "1617304117"] [ref "v182,1"], client: 127.0.0.1, server: , request: "POST /index.php HTTP/1.1", host: "localhost"
When I run tcpdump, I see that there IS the final boundary in the request. I also tried it from browser, final boundary also at there.
Am I spoiling something? Or is this a really unexpected behavior?
Metadata
Metadata
Assignees
Labels
No labels