Opened 7 months ago

Closed 7 months ago

Last modified 7 months ago

#1714 closed defect (duplicate)

Server Name Indication (SNI) in NGINX isn't honoring ssl_protocols, ssl_ciphers etc. per server{} config / TLS downgrade and server exhaustion attack.

Reported by: fortresslinux@… Owned by:
Priority: critical Milestone:
Component: nginx-core Version: 1.14.x
Keywords: Server Name Indication SNI TLS downgrade exhaustion attack Cc:
uname -a: Fortress Linux 4.14.94 #1 SMP Wed Jan 25 12:56:18 UTC 2019 aarch64 GNU/Linux
nginx -V: nginx/1.14.2

Description

SNI bug:

Reproduce: Configure nginx (partly) as:

server {
    listen 443 ssl http2 default_server;
    ssl_protocols TLSv1.3;
    ssl_ciphers 'TLS_CHACHA20_POLY1305_SHA256';

...

server {
    listen 192.168.66.1:443 ssl http2;
    ssl_protocols TLSv1;
    ssl_ciphers 'DHE-RSA-AES256-SHA';
    server_name www.example.org;

Test command:

openssl s_client -servername www.example.org -connect thisserver.com:443

It should honor the second server ssl_ciphers and ssl_protocols (and more) in SNI, but it uses ssl_ciphers and ssl_protocols (and more) from the first default_server. It sends the right certs though.

Cross-site TLS downgrade is possible in this way too (exchange the two example servers ssl_ciphers and ssl_protocols). And/or it makes the server no-op for older/newer clients.

TLS server exhaustion attack:

NGINX sends a 'server hello + cert + server hello done' after every initial 'client hello + SNI servername'. No matter if the SNI host in the client hello exists on the server or not. With some scripting and fake SNI servername generating, it is easily possible to exhaust all available connections or the server itself... completely.

We recommended to introduce the possibility in NGINX to respond with a code 444; when requesting non-existing SNI hosts/servernames without requiring (or sending) any certs, ciphers, protocols in the (possible catchall) default_server configuration.

Kind regards,

The Fortress Linux Security Team.

Change History (2)

comment:1 Changed 7 months ago by mdounin

  • Resolution set to duplicate
  • Status changed from new to closed

It should honor the second server ssl_ciphers and ssl_protocols (and more) in SNI, but it uses ssl_ciphers and ssl_protocols (and more) from the first default_server. It sends the right certs though.

The ciphers are used from the SNI-based virtual server. Protocols aren't, and this is unlikely to change, see #844.

NGINX sends a 'server hello + cert + server hello done' after every initial 'client hello + SNI servername'. No matter if the SNI host in the client hello exists on the server or not. With some scripting and fake SNI servername generating, it is easily possible to exhaust all available connections or the server itself... completely.

Much simpler version of the same "attack" is to simply open multiple connections to nginx (and start sending something). Alternatively, nothing stops the attacker from requesting a valid name. SSL handshakes are subject to the same timeouts as reading request headers, see http://nginx.org/r/client_header_timeout.

We recommended to introduce the possibility in NGINX to respond with a code 444; when requesting non-existing SNI hosts/servernames without requiring (or sending) any certs, ciphers, protocols in the (possible catchall) default_server configuration.

See comment:6:ticket:195 on how to instruct nginx to reject connections from clients requesting SNI names not explicitly configured. It has nothing to do with security though, but rather about rejecting connections in a user-friendly way.

comment:2 Changed 7 months ago by fortresslinux@…

You are right about the custom ciphers per server(). We mixed it up before.

The used protocols are indeed controlled by OpenSSL and its only possible to use one ssl_protocols directive for all servers. The proposed patches work, but they can have unexpected results with TLSv1.3 (and support for SSLv2, SSLv3 and non-SNI should be deprecated/unsupported these days). We assume NGINX will never use it's own TLS library, so this might never be resolved.

This unexpected behaviour is not described at http://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_protocols btw.

There are many ways to DOS or 'annoy' a NGINX server. We encounter and fight this almost every day. This doesn't mean unintended uses, no or limited validation should be accepted. For example: the servername in the SNI handshake accepts more than 255 characters, including invalid characters. This goes for OpenSSL and Nginx.

Feature, fault or insecurity; it's all in the eyes of the beholder.

Note: See TracTickets for help on using tickets.