MariaDB · Report #3896617
Connector/C TLS auth-switch bypass exposes plaintext password on hostname verification failure
How it was found
Summary
A logic flaw in run_plugin_auth() allows a man-in-the-middle attacker to capture a client's plaintext database password during TLS-secured authentication. The auth-switch guard in plugins/auth/my_auth.c checks only MARIADB_TLS_VERIFY_TRUST (bit 0, value 1) before blocking non-hashing auth plugins. A hostname-only verification failure (MARIADB_TLS_VERIFY_HOST = 2) bypasses this check because (2 & 1) == 0, letting the attacker switch to mysql_clear_password and receive the password in cleartext before the post-auth catch-all rejects the connection.
Originally reported by: Aisle Research
Environment
- Component: MariaDB Connector/C (
mariadb-connector-c) - Affected versions: All versions through current
3.4branch (verified on commita4a04c52, 82 commits after v3.4.9 tag). The v3.4.9 release is affected. - Platform: All platforms (Linux, Windows, macOS) — the vulnerable code path is platform-independent.
- Build configuration: Default build with TLS support enabled.
mysql_clear_passwordis registered as a default dynamic auth plugin.
Vulnerability Details
In plugins/auth/my_auth.c, run_plugin_auth() handles server auth-switch requests (0xFE packet). The TLS verification guard at line 875 is:
if ((mysql->net.tls_verify_status & MARIADB_TLS_VERIFY_TRUST) &&
!password_and_hashing(mysql, auth_plugin))
The tls_verify_status flags are defined as:
MARIADB_TLS_VERIFY_TRUST= 1 (CA trust chain failure)MARIADB_TLS_VERIFY_HOST= 2 (hostname mismatch)MARIADB_TLS_VERIFY_AUTO= 3 (both)
When only hostname verification fails, tls_verify_status = 2. The bitwise AND (2 & 1) evaluates to 0, bypassing the guard entirely. This allows a non-hashing plugin like mysql_clear_password to execute and send the password in plaintext before the later catch-all check at line 894 (if (!mysql->net.tls_verify_status) return 0; ... return 1;) terminates the connection.
The initial-auth check at line 484 uses a different comparison: tls_verify_status > MARIADB_TLS_VERIFY_AUTO (i.e. > 3). This is intentionally more permissive — it allows hashing plugins through on first auth even without full CA verification. The auth-switch path needs to be stricter: once a server requests a non-hashing plugin mid-authentication, ANY verification failure should block it.
Steps to Reproduce
-
Set up a MariaDB/MySQL-compatible test server with a TLS certificate from a trusted CA, but issued for a different hostname (e.g., server is at
db.example.combut the cert is issued forother.example.com). -
Configure a client using Connector/C with hostname verification enabled (
MYSQL_OPT_SSL_VERIFY_SERVER_CERT=1). -
Connect. The TLS handshake completes because the CA is trusted, but
ma_verification_callback()inopenssl.csetstls_verify_status = MARIADB_TLS_VERIFY_HOST(value 2). -
Initial auth check at line 484:
2 > 3is false — falls through.password_and_hashing()returns true for the default plugin (e.g.,caching_sha2_password) — the initial hashed auth exchange proceeds. -
The attacker-controlled server responds with
0xFEAuthSwitchRequest selectingmysql_clear_password. -
Auth-switch guard at line 875:
(2 & 1) == 0— bypassed. -
clear_password_auth_client()inmariadb_cleartext.csends the full password in cleartext viavio->write_packet(). -
The attacker captures the plaintext password on the wire.
-
Only after the plugin completes does the catch-all at line 894 reject the connection — too late, the password is already disclosed.
Network trace expectation: The attacker observes the password bytes in the auth-switch response packet, before the connection error is returned to the client application.
Suggested Fix
Replace the bitmask check with a nonzero check, so that ANY tls_verify_status (trust failure, hostname failure, or both) blocks non-hashing plugins on auth-switch:
diff --git a/plugins/auth/my_auth.c b/plugins/auth/my_auth.c
index 8ee14b66..17007b8b 100644
--- a/plugins/auth/my_auth.c
+++ b/plugins/auth/my_auth.c
@@ -872,12 +872,12 @@ retry:
auth_plugin= &dummy_fallback_client_plugin;
/* can we use this plugin with this tls server cert ? */
- if ((mysql->net.tls_verify_status & MARIADB_TLS_VERIFY_TRUST) &&
+ if (mysql->net.tls_verify_status &&
!password_and_hashing(mysql, auth_plugin))
{
my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
ER(CR_SSL_CONNECTION_ERROR),
- "Certificate verification failure: The certificate is NOT trusted.");
+ "Certificate verification failure: peer certificate verification failed.");
return 1;
}
goto retry;
This does not change the initial-auth check at line 484 (tls_verify_status > MARIADB_TLS_VERIFY_AUTO), which is intentionally more permissive for hashing plugins on first auth. The auth-switch path is the only one affected.
Originally reported by: Aisle Research to Red Hat via Embargo process. Aisle research should be attributed for the finding. To my knowledge this report was not shared outside of Aisle Research and Red Hat.
Impact
Impact
A man-in-the-middle attacker who can present a valid certificate from any trusted CA (but for the wrong hostname) can capture the client's database password in plaintext. The captured credentials can then be used to authenticate to the legitimate database server, achieving full unauthorized access. This is a credential disclosure that enables subsequent access control bypass.
The attack requires:
- A MitM position between client and server
- A valid TLS certificate from a trusted CA (wrong hostname is sufficient)
mysql_clear_passwordavailable in the client build (default)