owasp-modsecurity / owasp-modsecurity/ModSecurity

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

Open
#2,849 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

2.x bug Platform - Apache
Dominant language
C++
Stars
9.8k
Forks
1.8k
Avg merge
2h 46m
Merged PRs (30d)
1

Description

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 https://github.com/SpiderLabs/ModSecurity/issues/533#issuecomment-1270214868

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start at Apache httpd's ap_die_r function and ModSecurity's send_error_bucket function, using the supplied phase 4 configuration and curl reproduction. Trace how the original response status affects ErrorDocument handling, then verify that the custom error document is returned instead of the generated recursive-error response.

Written by the indexing model from the issue text.

Assessment

Tech stack
apache, cpp
Domain
backend, security
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Clearly specified
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.