From ebcb9eca29295b3884b6bd7be099f0bf9e206025 Mon Sep 17 00:00:00 2001 From: Daniel Lenski Date: Mon, 12 Jun 2023 14:44:04 -0700 Subject: [PATCH] [CONC-648] Do not trust error packets received prior to TLS handshake completion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MariaDB Connector/C does not distinguish [application-layer error packets](https://mariadb.com/kb/en/err_packet) that it receives prior to TLS handshake completion from those that it receives immediately after. (A trivially modified server built from https://github.com/dlenski/mariadb-server/commit/demonstration_of_CONC-648_vulnerability can easily be used to demonstrate this.) Pre-TLS error packet received from this trivially modified server. This packet should NOT be trusted to actually originate from the server: $ mariadb --ssl --ssl-verify-server-cert -uUsername -pVerySecretPassword -h CONC-648.vuln.demo.server.com ERROR 1815 (HY000): Internal error: Client will accept this error as genuine even if running with --ssl --ssl-verify-server-cert, and even though this error is sent in plaintext PRIOR TO TLS HANDSHAKE. Post-(TLS handshake) error packet received from a normal MariaDB server upon an attempt to connect with incorrect credentials. This error packet CAN be trusted to actually originate from the server, assuming transitive trust in the TLS protocol implementation and PKI-based certificate validation: $ mariadb --ssl --ssl-verify-server-cert -uUsername -pWrongPassword -h $NORMAL_MARIADB10.6.14_SERVER ERROR 1045 (28000): Access denied for user 'Username'@'A.B.C.D' (using password: YES) This client behavior opens up MariaDB Connector/C clients to an extremely straightforward [downgrade attack](https://en.wikipedia.org/wiki/Downgrade_attack). An on-path or pervasive attacker can inject errors into MariaDB client→server connections that are intended to be protected by TLS, and the client has no clear mechanism to distinguish such errors from errors that actually come from the server. An attacker could easily use this to DOS a client, or even influence its behavior. For example, consider a client application which is configured… 1. To use TLS with server certificate validation (`--ssl --ssl-verify-server-cert`), and 2. To wait for a back-off period and then *retry* connection attempts if the server responds with `ER_CON_COUNT_ERROR` ("Too many connections") from the server, and 3. To give up and shut down if its connection attempts fail with `ER_ACCESS_DENIED_ERROR` ("Access denied for user"), on the assumption that this is due to an incorrect or expired password, and cannot be resolved without human intervention. An attacker could completely disable the retry mechanism of this application by intercepting connection attempts and replying with `ER_ACCESS_DENIED_ERROR` packets. This patch modifies MariaDB Connector/C so that if the client is configured to use TLS, error packets received prior to the completion of the TLS handshake are untrusted, and are changed to a generic `CR_CONNECTION_ERROR`. $ mariadb --ssl --ssl-verify-server-cert -uUsername -pVerySecretPassword -h CONC-648.vuln.demo.server.com ERROR 2002 (HY000): Received error packet before completion of TLS handshake. The authenticity of the following error cannot be verified: 1815 - Internal error: Client will accept this error as genuine even if running with --ssl --ssl-verify-server-cert, and even though this error is sent in plaintext PRIOR TO TLS HANDSHAKE. All new code of the whole pull request, including one or several files that are either new files or modified ones, are contributed under the BSD-new license. I am contributing on behalf of my employer Amazon Web Services, Inc. --- libmariadb/mariadb_lib.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libmariadb/mariadb_lib.c b/libmariadb/mariadb_lib.c index 70b9df032..587ac542c 100644 --- a/libmariadb/mariadb_lib.c +++ b/libmariadb/mariadb_lib.c @@ -1804,6 +1804,11 @@ MYSQL *mthd_my_real_connect(MYSQL *mysql, const char *host, const char *user, ER(CR_SERVER_LOST_EXTENDED), "handshake: reading initial communication packet", errno); + else if (mysql->options.use_ssl) + my_set_error(mysql, CR_CONNECTION_ERROR, SQLSTATE_UNKNOWN, + "Received error packet before completion of TLS handshake. " + "The authenticity of the following error cannot be verified:\n%d - %s", + mysql->net.last_errno, mysql->net.last_error); goto error; }