Skip to content

Apache httpd does not execute ErrorDocument directive if response code is changed during phase 4 #2849

Open
@TomasKorbar

Description

@TomasKorbar

Describe the bug
When response code is changed during phase 4 and the original response code is other than 200, Apache httpd does not execute ErrorDocument directive and bad response is generated.

Logs and dumps

Output of:

  1. DebugLogs (level 9)

modsec_debug.log

  1. AuditLogs

modsec_audit.log

  1. Error logs

error_log.txt

To Reproduce

  1. Add following configuration:
    ProxyPass /helloworld http://localhost:9090/unavaible
    ErrorDocument 403 /custompage.html
    SecRule RESPONSE_PROTOCOL "@contains HTTP" "id:'4',phase:4,auditlog,log,deny,status:403,msg:'yay!'"
  1. Create and place custompage.html page to server root.
  2. execute $ curl -v localhost/helloworld

Actual behavior

<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>403 Forbidden</title>
</head><body>
<h1>Forbidden</h1>
<p>You don't have permission to access this resource.</p>
<p>Additionally, a 503 Service Unavailable
error was encountered while trying to use an ErrorDocument to handle the request.</p>
</body></html>

Expected behavior
Proper rewritten response with custom error document:

Server (please complete the following information):

  • ModSecurity version (and connector): mod_security-2.9.6
  • WebServer: httpd-2.4.37
  • OS (and distro): Linux Fedora

Rule Set (please complete the following information):

  • Running any public or commercial rule set? No

Additional context

In my opinion, this is caused by apache httpds assumption that changed status of request which got through filter is a recursive error which occured during handling of previous errors.
See ap_die_r function in http module of apache, particularly this part of code:

    /*
     * The following takes care of Apache redirects to custom response URLs
     * Note that if we are already dealing with the response to some other
     * error condition, we just report on the original error, and give up on
     * any attempt to handle the other thing "intelligently"...
     */
    if (recursive_error != HTTP_OK) {
        while (r_1st_err->prev && (r_1st_err->prev->status != HTTP_OK))
            r_1st_err = r_1st_err->prev;  /* Get back to original error */

        if (r_1st_err != r) {
            /* The recursive error was caused by an ErrorDocument specifying
             * an internal redirect to a bad URI.  ap_internal_redirect has
             * changed the filter chains to point to the ErrorDocument's
             * request_rec.  Back out those changes so we can safely use the
             * original failing request_rec to send the canned error message.
             *
             * ap_send_error_response gets rid of existing resource filters
             * on the output side, so we can skip those.
             */
            update_r_in_filters(r_1st_err->proto_output_filters, r, r_1st_err);
            update_r_in_filters(r_1st_err->input_filters, r, r_1st_err);
        }

        custom_response = NULL; /* Do NOT retry the custom thing! */
    }
    else {
        int error_index = ap_index_of_response(type);
        custom_response = ap_response_code_string(r, error_index);
        recursive_error = 0;
    }

Clearing the original response code with f->r->status = 200; in send_error_bucket function appears to fix this issue.

This issue is a continuation of #533 and extends findings of Marc Stern in #533 (comment)

Metadata

Metadata

Assignees

No one assigned

    Labels

    2.xRelated to ModSecurity version 2.xPlatform - ApachebugIt is a confirmed bug

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions