Opened 3 years ago

Closed 3 years ago

#1092 closed defect (invalid)

virtal host attacks: Limit Host header to CN/DNS-Name of currently used certificate

Reported by: Darkudorus@… Owned by:
Priority: major Milestone: 1.11.5
Component: nginx-core Version: 1.11.x
Keywords: Cc:
uname -a: Ubuntu 16.04 # Linux hostname.tld 4.4.0-38-generic #57-Ubuntu SMP Tue Sep 6 15:42:33 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux
nginx -V: https://launchpad.net/~nginx/+archive/ubuntu/development # nginx version: nginx/1.11.3 built with OpenSSL 1.0.2g-fips 1 Mar 2016 (running with OpenSSL 1.0.2g 1 Mar 2016) TLS SNI support enabled configure arguments: --with-cc-opt='-g -O2 -fPIE -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2' --with-ld-opt='-Wl,-Bsymbolic-functions -fPIE -pie -Wl,-z,relro -Wl,-z,now' --prefix=/usr/share/nginx --conf-path=/etc/nginx/nginx.conf --http-log-path=/var/log/nginx/access.log --error-log-path=/var/log/nginx/error.log --lock-path=/var/lock/nginx.lock --pid-path=/run/nginx.pid --http-client-body-temp-path=/var/lib/nginx/body --http-fastcgi-temp-path=/var/lib/nginx/fastcgi --http-proxy-temp-path=/var/lib/nginx/proxy --http-scgi-temp-path=/var/lib/nginx/scgi --http-uwsgi-temp-path=/var/lib/nginx/uwsgi --with-debug --with-pcre-jit --with-ipv6 --with-http_ssl_module --with-http_stub_status_module --with-http_realip_module --with-http_auth_request_module --with-http_addition_module --with-http_dav_module --with-http_flv_module --with-http_geoip_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_image_filter_module --with-http_mp4_module --with-http_perl_module --with-http_random_index_module --with-http_secure_link_module --with-http_v2_module --with-http_sub_module --with-http_xslt_module --with-mail --with-mail_ssl_module --with-stream --with-stream_ssl_module --with-threads --add-module=/build/nginx-arw1Xp/nginx-1.11.3/debian/modules/headers-more-nginx-module --add-module=/build/nginx-arw1Xp/nginx-1.11.3/debian/modules/nginx-auth-pam --add-module=/build/nginx-arw1Xp/nginx-1.11.3/debian/modules/nginx-cache-purge --add-module=/build/nginx-arw1Xp/nginx-1.11.3/debian/modules/nginx-dav-ext-module --add-module=/build/nginx-arw1Xp/nginx-1.11.3/debian/modules/nginx-development-kit --add-module=/build/nginx-arw1Xp/nginx-1.11.3/debian/modules/nginx-echo --add-module=/build/nginx-arw1Xp/nginx-1.11.3/debian/modules/ngx-fancyindex --add-module=/build/nginx-arw1Xp/nginx-1.11.3/debian/modules/nginx-http-push --add-module=/build/nginx-arw1Xp/nginx-1.11.3/debian/modules/nginx-lua --add-module=/build/nginx-arw1Xp/nginx-1.11.3/debian/modules/nginx-upload-progress --add-module=/build/nginx-arw1Xp/nginx-1.11.3/debian/modules/nginx-upstream-fair --add-module=/build/nginx-arw1Xp/nginx-1.11.3/debian/modules/ngx_http_substitutions_filter_module

Description

I have read https://bh.ht.vc/vhost_confusion.pdf (read it, it's enlightening!)
and seen the first image here: https://twitter.com/com/status/781521581575135232
But Error 1013 "HTTP hostname and TLS SNI hostname mismatch" don't come in my personal tests with domains on cloudflare.


My test (with a normal nginx mainline server):
sni-evil.tld has
echo "Server Name: $server_name<br>\nHTTP_Host: $http_host<br>\nHost: $host<br>\nSSL_server_name: $ssl_server_name";

sni-website.tld is not modified.

Both are on hostname.tld (default_server).

With https://addons.mozilla.org/firefox/addon/modify-headers/ I changed the Host header to "Host: sni-evil.tld" while I am on sni-website.tld.


From Screenshot 1:

Browser location bar: https://sni-website.tld

Server Name: sni-evil.tld
HTTP_Host: sni-evil.tld
Host: sni-evil.tld
SSL_server_name: sni-website.tld

Notice: You don't see the original content from sni-website.tld here, this is from the echo of sni-evil.tld, because the Host header is modified.
But sni-evil.tld would get all cookies from sni-website.tld.

I am no expert in this. But the webserver should only deliver the content of the address in the location bar of the browser.

Another possible bug: Think also of a session-resumption with a certificate "localhost, www.facebook.com". It might be applicable here (read the PDF from the first sentence).


then I changed the address in the location bar to www.sni-website.tld and pressed F5


From Screenshot 2:

Browser location bar: https://www.sni-website.tld

Server Name: sni-evil.tld
HTTP_Host: sni-evil.tld
Host: sni-evil.tld
SSL_server_name: sni-website.tld


Because the certificate includes both domains (with and without www), the current tls session and $ssl_server_name are not changed (to the real used SNI domain).
$ssl_server_name only changes if I press Strg + F5 (in Firefox):


From Screenshot 3:

Browser location bar: https://www.sni-website.tld

Server Name: sni-evil.tld
HTTP_Host: sni-evil.tld
Host: sni-evil.tld
SSL_server_name: www.sni-website.tld


So you see "if ($ssl_server_name != $http_host) { return 400; }" would not work as fix here.

This problem has the whole web.

You can go to https://nofap.nl (hosted on Cloudflare) and Modify the Host header to "Host: getbootstrap.com" (both are in the same certificate), and you will see the whole getbootstrap.com website under nofap.nl, where it doesn't belong to.


The host header could be modified in the browser by an addon, "security" software, malware etc.
To assign an exclusive IPv6 address for each certificate can't be se solution because SNI would be dead then.


Proposed fix for this:
Because one certificate with multiple DNS-Names could be used in multiple server-blocks, it would be enough to check,

  • if the current "Host" header is one of the domains in CN/DNS-Names in the certificate of the current tls session

and

  • when the Host header includes a Port, then it have to be a Port (in one of "listen") of the server block nginx wants to use.

Otherwise return Error 400 Bad Request.

Change History (3)

comment:1 by Darkudorus@…, 3 years ago

I have forgotten to mention, that
certificate A only has "sni-website.tld, www.sni-website.tld"
and certificate B in another server block has "sni-evil.tld, www.sni-evil.tld" as CN/DNS-Names.

The "server_name" in each server block contain only "domain.tld www.domain.tld".

Last edited 3 years ago by Darkudorus@… (previous) (diff)

comment:2 by Darkudorus@…, 3 years ago

Please edit the first word in the ticket name to "virtual".

comment:3 by Maxim Dounin, 3 years ago

Resolution: invalid
Status: newclosed

As long as you, as a client, request a virtual server different from one seen in the SNI extension, there is no problem, as this is a legitimate choice of the client. Moreover, this is how things work without SNI and how browsers reuse connections if they think it is legitimate. The Virtual host confusion attack is about persuading clients to request a different host by an external attacker, and nginx has countermeasures in place since 1.7.5.

Note: See TracTickets for help on using tickets.