Open
Description
Bug Description
After specific wrong request I get second response from Apache httpd server:
$ printf "GET / HTTP/1.1\r\nHost: localhost\r\nTransfer-Encoding: chunked\r\n\r\n\r\n" | nc localhost 60080
HTTP/1.1 400 Bad Request
Date: Thu, 11 Feb 2021 12:48:06 GMT
Server: Apache/2.4.46 (Linux/SUSE)
Connection: close
Content-Type: text/html; charset=iso-8859-1
<html>400</html>
<html>It works!</html>
$
Where
<html>It works!</html>
is content of index.html in docroot.
Logs and dumps
Not necessary.
To Reproduce
Minimal httpd config:
ServerName test
User user
Group users
Listen 60080
PidFile /tmp/apache-rex/poc/pid
ErrorLog /tmp/apache-rex/poc/error_log
LoadModule auth_basic_module /usr/lib64/apache2-prefork/mod_auth_basic.so
LoadModule dir_module /usr/lib64/apache2-prefork/mod_dir.so
LoadModule authz_host_module /usr/lib64/apache2-prefork/mod_authz_host.so
LoadModule authz_core_module /usr/lib64/apache2-prefork/mod_authz_core.so
LoadModule unique_id_module /usr/lib64/apache2-prefork/mod_unique_id.so
LoadModule security2_module /usr/lib64/apache2/mod_security2.so
LogLevel trace8
DocumentRoot /srv/www/htdocs
DirectoryIndex index.html
ErrorDocument 400 "<html>400</html>\n"
SecRuleEngine DetectionOnly
SecRequestBodyAccess on
SecAuditLogType Serial
SecAuditLog /tmp/apache-rex/poc/modsec_audit.log
SecTmpDir /tmp/
SecDataDir /tmp/
### example configuration
<Directory "/tmp/apache-rex/poc/htdocs">
Require local
</Directory>
Expected behavior
$ printf "GET / HTTP/1.1\r\nHost: localhost\r\nTransfer-Encoding: chunked\r\n\r\n\r\n" | nc localhost 60080
HTTP/1.1 400 Bad Request
Date: Thu, 11 Feb 2021 12:48:06 GMT
Server: Apache/2.4.46 (Linux/SUSE)
Connection: close
Content-Type: text/html; charset=iso-8859-1
<html>400</html>
Server (please complete the following information):
- ModSecurity version (and connector): ModSecurity v2.9.3, v2.9.2
- WebServer: Apache httpd 2.4.46
- OS (and distro): openSUSE Tumbleweed
Tested also with Ubuntu 20.04/http 2.4.41/mod_security 2.9.3 and fedora33/httpd 2.4.46/mod_security 2.9.3.
Patch
Yann Ylavic (@ylavic) wrote following patch:
diff -ru modsecurity-2.9.3.old/apache2/apache2_io.c modsecurity-2.9.3.new/apache2/apache2_io.c
--- modsecurity-2.9.3.old/apache2/apache2_io.c 2018-12-04 19:49:37.000000000 +0100
+++ modsecurity-2.9.3.new/apache2/apache2_io.c 2021-02-12 13:28:27.739749566 +0100
@@ -209,6 +209,10 @@
* too large and APR_EGENERAL when the client disconnects.
*/
switch(rc) {
+ case AP_FILTER_ERROR :
+ *error_msg = apr_pstrdup(msr->mp, "Error reading request body: filter error");
+ return -8;
+
case APR_INCOMPLETE :
*error_msg = apr_psprintf(msr->mp, "Error reading request body: %s", get_apr_error(msr->mp, rc));
return -7;
@@ -218,7 +222,7 @@
case APR_TIMEUP :
*error_msg = apr_psprintf(msr->mp, "Error reading request body: %s", get_apr_error(msr->mp, rc));
return -4;
- case AP_FILTER_ERROR :
+ case APR_ENOSPC:
*error_msg = apr_psprintf(msr->mp, "Error reading request body: HTTP Error 413 - Request entity too large. (Most likely.)");
return -3;
case APR_EGENERAL :
diff -ru modsecurity-2.9.3.old/apache2/mod_security2.c modsecurity-2.9.3.new/apache2/mod_security2.c
--- modsecurity-2.9.3.old/apache2/mod_security2.c 2018-12-04 19:49:37.000000000 +0100
+++ modsecurity-2.9.3.new/apache2/mod_security2.c 2021-02-12 13:34:22.940428406 +0100
@@ -1013,7 +1013,7 @@
}
rc = read_request_body(msr, &my_error_msg);
- if (rc < 0 && msr->txcfg->is_enabled == MODSEC_ENABLED) {
+ if (rc < 0) {
switch(rc) {
case -1 :
if (my_error_msg != NULL) {
@@ -1021,6 +1021,21 @@
}
return HTTP_INTERNAL_SERVER_ERROR;
break;
+ case -2 : /* Bad request. */
+ case -6 : /* EOF when reading request body. */
+ case -7 : /* Partial recieved */
+ if (my_error_msg != NULL) {
+ msr_log(msr, 4, "%s", my_error_msg);
+ }
+ r->connection->keepalive = AP_CONN_CLOSE;
+ return HTTP_BAD_REQUEST;
+ break;
+ case -3 : /* Apache's LimitRequestBody. */
+ if (my_error_msg != NULL) {
+ msr_log(msr, 1, "%s", my_error_msg);
+ }
+ return HTTP_REQUEST_ENTITY_TOO_LARGE;
+ break;
case -4 : /* Timeout. */
if (my_error_msg != NULL) {
msr_log(msr, 4, "%s", my_error_msg);
@@ -1042,19 +1057,11 @@
}
}
break;
- case -6 : /* EOF when reading request body. */
- if (my_error_msg != NULL) {
- msr_log(msr, 4, "%s", my_error_msg);
- }
- r->connection->keepalive = AP_CONN_CLOSE;
- return HTTP_BAD_REQUEST;
- break;
- case -7 : /* Partial recieved */
+ case -8 : /* Filter error. */
if (my_error_msg != NULL) {
- msr_log(msr, 4, "%s", my_error_msg);
+ msr_log(msr, 1, "%s", my_error_msg);
}
- r->connection->keepalive = AP_CONN_CLOSE;
- return HTTP_BAD_REQUEST;
+ return AP_FILTER_ERROR;
break;
default :
/* allow through */
I can send a PR, but I am not the author.