Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vulnerable to CVE-2015-3152 when using TLS with mysql 5.5/5.6 client libs #98

Closed
edmorley opened this issue Jul 21, 2016 · 11 comments
Closed

Comments

@edmorley
Copy link

edmorley commented Jul 21, 2016

tl;dr: mysql 5.5/5.6 client libraries have unfortunate defaults which means TLS connections can be silently man-in-the-middled - so if mysqlclient-python is used with them, is vulnerable to CVE-2015-3152.

With mysql client libraries < v5.7, if a CA certificate is specified, the client neither requires TLS/SSL, nor actually checks the CA certificate matches the hostname. This means that if the connection is MITMed and redirected to malicious mysql server instance that pretends to not support TLS, it proceeds anyway, silently leaking credentials (if using username/password rather than client cert/key).

For example when using:

  • Ubuntu 14.04 with the latest Ubuntu trusty libmysqlclient-dev package (which is libmysqlclient18 - ie mysql v5.5)
  • mysqlclient-python v1.3.7
  • mysql-server running on localhost with TLS disabled

And then running:

import MySQLdb

conn = MySQLdb.connect(
    host='localhost',
    user='foo',
    passwd='bar',
    ssl= {
        'ca': 'unrelated-ca-cert.pem',
    }
)

Expected:
Some kind of TLS error.

Actual:

Traceback (most recent call last):
  File "./mysql-test.py", line 10, in <module>
    'ca': 'unrelated-ca-cert.pem',
  File "/home/vagrant/venv/local/lib/python2.7/site-packages/MySQLdb/__init__.py", line 81, in Connect
    return Connection(*args, **kwargs)
  File "/home/vagrant/venv/local/lib/python2.7/site-packages/MySQLdb/connections.py", line 204, in __init__
    super(Connection, self).__init__(*args, **kwargs2)
_mysql_exceptions.OperationalError: (1045, "Access denied for user 'foo'@'localhost' (using password: YES)")

...ie our credentials were just sent in plaintext.

This means anyone can MITM a connection to eg an Amazon RDS instance from a local dev's machine (or say Heroku, where we can't set up a VPC with the RDS instance) running mysql 5.5/5.6 client libraries, and the client will be none the wiser. Note this also affects the command line mysql client too (see: http://bugs.mysql.com/bug.php?id=79862).

Thankfully this has been fixed in mysql 5.7 (ie if a CA certificate is specified it defaults to enforcing TLS and that the cert matches the hostname; see here), however we can't easily use mysql 5.7 on Ubuntu 14.04 on Heroku/Travis/....

As for mysql 5.5/5.6, recent point releases (specifically 5.5.49 and 5.6.30) have added opt-in support for enforcing TLS/CA cert verification, however neither mysqlclient-python nor MySQLdb support enabling these.

Edit: It turns out the backported libmysqlclient 5.5/5.6 fixes are insufficient to solve this after all - there's no way mysqlclient-python can be securely used with them...

Whilst recent point releases of MySQL 5.5/5.6's libmysqlclient (specifically 5.5.49 and 5.6.30) have added opt-in support for verifying the CA certificate (using MYSQL_OPT_SSL_VERIFY_SERVER_CERT):
(a) neither mysqlclient-python nor MySQLdb currently support enabling it (though there is a PR open against MySQLdb to add it: farcepest/MySQLdb1#100)
(b) even with this option enabled, if the remote server pretends to not support TLS, then the connection can be silently downgraded to non-TLS (the "enforce TLS" options from 5.7 were only backported for the command line mysql client and not the C API)

Oracle's own MySQL Python connector does support (a) (by passing ssl_verify_cert=True), however is still vulnerable to (b). I've filed an upstream MySQL bug requesting further libmysqlclient 5.5/5.6 backports of the 5.7 fixes.

As such at the moment, there is no secure way to use mysqlclient-python, MySQLdb or MySQL Connector Python with libmysqlclient 5.5/5.6.

See also:
https://dev.mysql.com/doc/refman/5.5/en/mysql-options.html
https://dev.mysql.com/doc/refman/5.6/en/mysql-options.html
http://www.ocert.org/advisories/ocert-2015-003.html
https://mariadb.org/information-on-the-ssl-connection-vulnerability-of-mysql-and-mariadb-2/

@edmorley edmorley changed the title Add support for enforcing TLS and verifying the CA certificate Add support for enforcing TLS/verifying CA certificate (fix CVE-2015-3152) Jul 21, 2016
@edmorley edmorley changed the title Add support for enforcing TLS/verifying CA certificate (fix CVE-2015-3152) Allow enforcing TLS/verifying CA certificates (ie fix CVE-2015-3152) Jul 21, 2016
@methane
Copy link
Member

methane commented Jul 22, 2016

Thank you for this report. I'll fix it (PR is welcome).

But I'm waiting Oracle releases new MySQL Connector/C which support VC14 (for Python 3.5)
for a long time...

@edmorley
Copy link
Author

edmorley commented Jul 26, 2016

I've had a quick unsuccessful attempt at a fix, however I'm not convinced the backported libmysqlclient changes are complete enough to solve this. Whilst MYSQL_OPT_SSL_VERIFY_SERVER_CERT has been backported, MYSQL_OPT_SSL_ENFORCE doesn't exist in the libmysqlclient 5.5 series, which negates half the benefit.

I could be missing the obvious of course!

Workarounds in the meantime (Edited to add some more options):

  1. Use a pure Python connector, such as PyMySQL or Oracle's MySQL Connector Python.
  2. On the machines using mysqlclient-python, use a newer version of the C API (which will work fine when connecting to a 5.5/5.6 mysql server instance), by either:
    • Updating to libmysqlclient 5.7. On non-bleeding edge distro releases (such as Ubuntu 14.04) this will require using the MySQL APT repo and not the distro-provided packages.
    • Using the standalone Connector/C release of the C API instead, which is kept more up to date and simpler to obtain/install than libmysqlclient.
  3. Keep all connections behind a VPN (though sadly not an option on Heroku's 'common runtime')

I'm still working on a Heroku-compatible version of (2), since lack of sudo means the libraries have to be installed to a non-standard location, and thus mean fiddling about with library/include path manipulation.

@methane
Copy link
Member

methane commented Jul 26, 2016

You can use new libmysqlclient without sudo.
mysqlclient-python uses mysql_config from PATH. You can set rpath option in mysql_config.
I don't have experience on Heroku, but I think docker based approach fixes such issues nowdays.

And you can use PyMySQL instead.

@edmorley edmorley changed the title Allow enforcing TLS/verifying CA certificates (ie fix CVE-2015-3152) Vulnerable to CVE-2015-3152 when using TLS with mysql 5.5/5.6 client libs Jul 30, 2016
@edmorley
Copy link
Author

edmorley commented Jul 30, 2016

Hmm - the mysql version numbers in the MySQL Connector Python source had me misled - '50711' is actually 5.7 not 5.0.7.

In addition, whilst my initial rummaging through the libmysqlclient 5.5/5.6 source had seen references to the backported mysql 5.7 options we need (like enforcing TLS) - only the command line client supports them (docs) and not the C API (docs). I'd naively presumed there would be parity between the two... sigh.

As such:

  • There is no way for mysqlclient-python or MySQLdb to protect against this, with the current libmysqlclient 5.5/5.6 library (I've edited the OP above to reflect this)
  • Even Oracle's own MySQL Connector Python is also vulnerable
  • I've filed a ticket requesting further fixes to 5.5/5.6: http://bugs.mysql.com/bug.php?id=82383

Things that mysqlclient-python can do in the meantime:

  1. Warn either during installation or at runtime if using TLS with libmysqlclient 5.5/5.6 (the latter being a bit like urllib3's logger warning if it's being used on a Python that doesn't support TLS properly)
  2. Update the readme/docs to recommend using the standalone Connector/C release of the C API, rather than the libmysqlclient 5.5/5.6 packages (I didn't even realise it was a thing until reading this by chance). The TLS issue should be documented specifically both on the installation instructions and also in the Connect() API reference.
  3. (Possibly) After at least one release with (1) landed, create a new major version release of mysqlclient-python that doesn't just warn, but actively refuses to make a TLS connection with the vulnerable versions of libmysqlclient (whilst still allowing non-TLS will still work)

@edmorley
Copy link
Author

edmorley commented Aug 4, 2016

Using the standalone Connector/C release of the C API instead, which is kept more up to date and simpler to obtain/install than libmysqlclient.

Hmm sadly this workaround doesn't even help - since it turns out Connector/C 6.1.6 (the latest available) is based on the older mysql 5.7.5/5.7.6 which doesn't include the "default to enforced TLS" changes we're after. I've filed a bug against mysql requesting they create a newer release:
http://bugs.mysql.com/bug.php?id=82448

@methane
Copy link
Member

methane commented Aug 4, 2016

Unlike HTTP, MySQL is commonly used in controlled network (LAN or VPN).
People uses TLS for vary reasons. And in some usages, this vulnerability is not a problem.
(See also: http://mysqlblog.fivefarmers.com/2015/04/09/implications-of-tls-plans-for-third-party-products/ )

So I won't do the (3). If this vulnerability is so important, linux distros must security upgrade
libmysqlclient.so.

@methane
Copy link
Member

methane commented Aug 31, 2017

There are nothing about this library can do for this issue.
Just use new mysqlclient.

@methane methane closed this as completed Aug 31, 2017
@edmorley
Copy link
Author

edmorley commented Sep 1, 2017

Since the comments above, Oracle have released new versions of libmysqlclient that add support to mysql_options() for the MYSQL_OPT_SSL_MODE option (as of 5.5.55 and 5.6.36):
https://dev.mysql.com/doc/relnotes/mysql/5.6/en/news-5-6-36.html
https://dev.mysql.com/doc/relnotes/mysql/5.5/en/news-5-5-55.html

They posted this as an update to MySQL ticket 82383, however it's still marked as private even though I've asked several times for it to be opened up. They also didn't credit me for the reporting the issue either, sigh.

Anyway - as such, I think there is now a way to implement this here?

@methane
Copy link
Member

methane commented Sep 1, 2017

Oracle doesn't provide macro like "HAVE_MYSQL_OPT_SSL_MODE".
There are no easy way to check the client library has the option or not.
I don't want to do it for now.

You can pass it via read_default_file="/path/to/my.cnf".

@methane
Copy link
Member

methane commented Sep 1, 2017

If you can write proper #if works nicely for MySQL 5.1.x, 5.5.x, 5.6.x, 5.7+, MariaDB 5.5~10.2,
please send pull request. (I don't know percona)

I'm very busy in this year and I have no motivation for hacking on such thing.

@edmorley
Copy link
Author

edmorley commented Sep 1, 2017

Makes sense - similarly I won't have the time to look into this further. We've since updated to MySQL 5.7 and moved on :-)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants