Opened 3 years ago

Last modified 3 years ago

#32061 assigned Bug

Credential leaks on dbshell crash on MySQL and Oracle.

Reported by: Simon Charette Owned by: Mariusz Felisiak
Component: Core (Management commands) Version: 3.1
Severity: Normal Keywords:
Cc: Triage Stage: Accepted
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description (last modified by Simon Charette)

I reported this issue to the security team on September 9th 2020 but it was deemed that it was better to handle it in public. On September 30th Charlie Denton also reported the issue to the security team hence why I'm finally creating a ticket for this so he also gets credit.

A variant of this issue was initially reported 5 years ago (#24999) where the reporter mentioned that usage of the --password flag when spawning a mysql shell process would show up in ps with the password in plain text.

What I'm reporting now is that if the dbshell management crash on backends that pass the --password flag in plain text (e.g. MySQL) the CalledProcessError exception raised will have the following signature

CalledProcessError: Command '['mysql', '--user=user', '--password=p4ssword', '--host=host', '--port=3306', 'database']' returned non-zero exit status 1.

That's not a big deal when no form of logging is in place but when it's the case (e.g. Sentry, Elastic, Graylog) it's possible for some systems to fail at obfuscating the --password= part due to the complex nature of the string.

In order to prevent that from happening I suggest we override MySQL backend's runshell method to pass env={'MYSQL_PWD': PASSWORD} instead of using the --password flag even if it looks like this is discouraged (and even meant to be deprecated?) for reasons similar to the ones mentioned in #24999. That's the exact approach currently used by the PostgreSQL backend though.

An alternative could be using the pexpect library to provide the password when prompted for instead but I fear this solution might not be suitable due to the external dependency it would incur. I haven't investigated how complex it would be to implement a cross-platform vendored solution without the use of pexpect but I fear dragons may lurk in the details of dealing with that properly.

Change History (8)

comment:1 by Simon Charette, 3 years ago

Description: modified (diff)

comment:2 by timmysilv, 3 years ago

If we don't want to set it to require some external dependency like pexpect, maybe encrypting the password before saving it to args would be better. Then the MySQL client can decrypt it with some saved+protected key. I can get a PR for that going.

comment:3 by Mariusz Felisiak <felisiak.mariusz@…>, 3 years ago

In bbe6fbb8:

Refs #32061 -- Unified DatabaseClient.runshell() in db backends.

comment:4 by Mariusz Felisiak, 3 years ago

Summary: Credential leaks on MySQL's dbshell crashCredential leaks on dbshell crash on MySQL and Oracle.

comment:5 by Mariusz Felisiak <felisiak.mariusz@…>, 3 years ago

In eb25fdb6:

Refs #32061 -- Added test for dbshell password leak on PostgreSQL.

comment:6 by Mariusz Felisiak <felisiak.mariusz@…>, 3 years ago

In 384ac099:

Refs #32061 -- Prevented password leak on MySQL dbshell crash.

The usage of the --password flag when invoking the mysql CLI has the
potential of exposing the password in plain text if the command happens
to crash due to the inclusion of args provided to
subprocess.run(check=True) in the string representation of the
subprocess.CalledProcessError exception raised on non-zero return code.

Since this has the potential of leaking the password to logging
facilities configured to capture crashes (e.g. sys.excepthook, Sentry)
it's safer to rely on the MYSQL_PWD environment variable instead even
if its usage is discouraged due to potential leak through the ps
command on old flavors of Unix.

Thanks Charlie Denton for reporting the issue to the security team.

Refs #24999.

comment:7 by Mariusz Felisiak, 3 years ago

Has patch: unset
Owner: changed from nobody to Mariusz Felisiak
Status: newassigned

I'm going to leave this ticket open because dbshell on Oracle is still affected.

comment:8 by GitHub <noreply@…>, 3 years ago

In 009fddc9:

Refs #32061 -- Fixed test_crash_password_does_not_leak() crash on Windows.

When env is passed to subprocess.run() we should pass all existing
environment variables. This fixes crash on Windows:

Fatal Python error: failed to get random numbers to initialize Python

Fatal Python error: _Py_HashRandomization_Init: failed to get random
numbers to initialize Python
Python runtime state: preinitialized

Note: See TracTickets for help on using tickets.
Back to Top