CVE-2017-14474, CVE-2017-14475, CVE-2017-14476, CVE-2017-14477, CVE-2017-14478, CVE-2017-14479, CVE-2017-14480, CVE-2017-14481
Multiple exploitable remote command injection vulnerabilities exist in the MySQL Master-Master Replication Manager (MMM) mmm_agentd daemon 2.2.1. mmm_agentd commonly runs with root privileges and does not require authentication by default. A specially crafted MMM protocol message can cause a shell command injection resulting in arbitrary command execution with the privileges of the mmm_agentd process. An attacker that can initiate a TCP session with mmm_agentd can trigger these vulnerabilities.
MMM 2.2.1
http://mysql-mmm.org/
9.8 - CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
CWE-77: Improper Neutralization of Special Elements used in a Command (‘Command Injection’)
MMM, the Multi-Master Replication Manager for MySQL, provides high availability to MySQL database clusters. Though superseded by more modern approaches, MMM was commonly used in high availability MySQL environments up through MySQL version 5.5. In an MMM environment, each MySQL server host runs the mmm_agentd agent. In its default configuration, mmm_agentd does not require authentication and typically runs as root because it requires sufficient privileges to reconfigure network interfaces.
mmm_agentd contains multiple remotely exploitable command injection vulnerabilities. Therefore, in many MMM environments, if an unauthenticated network attacker can make a TCP connection to the mmm_agentd process, they can run arbitrary commands as root. This vulnerability occurs because mmm_agentd includes attacker-supplied input in shell commands in multiple locations without appropriate sanitization.
For example, the MMM SET_STATUS
protocol message can be used to
assign a number of roles to an mmm_agentd host. Roles are specified
as a comma-separated list of role_name(ip_addr)
pairs
(e.g. role_a(10.10.10.10),role_b(10.10.10.11)
).
MMM::Common::Role::from_string()
in lib/Common/Role.pm
uses the
following regular expression to parse the role name and IP address:
/(.*)\((.*)\)/
. Thus, everything before the last opening
parenthesis will interpreted as the role name and all remaining
characters up to the last closing parenthesis will be interpreted as
the role IP address. An attacker can construct malicious IP address
values that will cause subsequent role handling code to invoke
arbitrary commands. For example:
role_a(10.10.10.10`malicious_command`)
Malicious IP address values are subject to interpretation by the shell both in mmm_agentd and in helper applications called by mmm_agentd.
Role IP address values should be validated to ensure that only expected values are specified. However, because other data flows may allow malicious input to reach vulnerable functions, all dynamic values incorporated into shell commands should be sanitized to ensure that shell metacharacters do not introduce additional arguments or execute unintended commands.
Limitations:
The MMM::Agent::Helpers::_execute()
function accepts a command and
an string containing arguments for the command. It constructs a
Bourne shell command line by concatenating the path to the requested
command, the mmm_agentd config file, and the specified arguments.
_execute()
runs the resulting command using the the Perl backtick
operator as follows.
# lib/Agent/Helpers.pm
162 sub _execute($$$) {
163 my $command = shift;
164 my $params = shift;
165 my $return_all = shift;
166
167 my $path = $main::agent->bin_path . "/agent/$command";
168 my $config_file = $main::agent->config_file;
169 $params = '' unless defined($params);
170
171 DEBUG "Executing $path $params";
172 my $res = `$path $config_file $params`;
Because _execute()
does not sanitize $params
, any shell
metacharacters present in $params
will be interpreted by the shell.
There are several code paths that can cause _execute()
to be called
with untrusted input in the $params
variable. To handle roles that
have been added and removed, mmm_agentd will invoke
MMM::Agent::Helpers::configure_ip($if, $ip)
for each added role and
MMM::Agent::Helpers::clear_ip($if, $ip)
for each deleted role with
$ip
set to the IP address value specified for the role. Both,
functions pass $ip
to _execute()
without sanitization.
# lib/Agent/Helpers.pm
45 sub configure_ip($$) {
46 my $if = shift;
47 my $ip = shift;
48 return _execute('configure_ip', "$if $ip");
49 }
...
60 sub clear_ip($$) {
61 my $if = shift;
62 my $ip = shift;
63 return _execute('clear_ip', "$if $ip");
64 }
65
As noted above role IP addresses can contain arbitrary content, modulo a few character restrictions, allowing an attacker to execute arbitrary shell commands.
Additionally, the GET_SYSTEM_STATUS
and CLEAR_BAD_ROLES
MMM
protocol messages can be used to invoke
MMM::Agent::Helpers::check_ip($if, $ip)
on the IP address value of
each role. check_ip()
also passes the untrusted $ip
value to
_execute()
without further sanitization.
# lib/Agent/Helpers.pm
29 sub check_ip($$) {
30 my $if = shift;
31 my $ip = shift;
32 return _execute('check_ip', "$if $ip");
33 }
Because input may be derived from a variety of (potentially untrusted)
sources, _execute()
should be modified to take an array of discrete
command arguments and to either avoid shell interpretation by using
execv-like functionality or quote command arguments to prevent shell
interpretation.
As seen above, in order to configure a new IP address mmm_agentd invokes:
/path/to/agent/configure_ip /path/to/mmm_agent.conf $if $ip
To add the IP address to the specified interface, the configure_ip
helper command invokes MMM::Agent::Helpers::Network::add_ip()
.
Which runs the following command on Linux hosts:
# lib/Agent/Helpers/Network.pm
70 $output = `/sbin/ip addr add $ip/32 dev $if`;
As a result, a malicious role IP address value that has been quoted to
prevent interpretation in MMM::Agent::Helpers::_execute()
will
arrive to add_ip()
in unquoted form allowing the execution of
arbitrary commands.
As seen above, in order to configure a new IP address mmm_agentd invokes:
/path/to/agent/configure_ip /path/to/mmm_agent.conf $if $ip
To add the IP address to the specified interface, the configure_ip
helper command invokes MMM::Agent::Helpers::Network::add_ip()
.
Which runs the following command on Solaris hosts:
# lib/Agent/Helpers/Network.pm
74 $output = `/usr/sbin/ifconfig $if addif $ip`;
As a result, a malicious role IP address value that has been quoted to
prevent interpretation in MMM::Agent::Helpers::_execute()
will
arrive to add_ip()
in unquoted form allowing the execution of
arbitrary commands.
As seen above, in order to configure a new IP address mmm_agentd invokes:
/path/to/agent/configure_ip /path/to/mmm_agent.conf $if $ip
To add the IP address to the specified interface, the configure_ip
helper command invokes MMM::Agent::Helpers::Network::add_ip()
.
Which runs the following command on FreeBSD hosts:
# lib/Agent/Helpers/Network.pm
84 $output = `/sbin/ifconfig $if inet $ip netmask 255.255.255.255 alias`;
As a result, a malicious role IP address value that has been quoted to
prevent interpretation in MMM::Agent::Helpers::_execute()
will
arrive to add_ip()
in unquoted form allowing the execution of
arbitrary commands.
As seen above, to remove a deleted role’s IP address, mmm_agentd invokes:
/path/to/agent/clear_ip /path/to/mmm_agent.conf $if $ip
To remove the IP address from the specified interface, the clear_ip
helper command invokes MMM::Agent::Helpers::Network::clear_ip()
.
Which runs the following command on Linux hosts:
# lib/Agent/Helpers/Network.pm
106 $output = `/sbin/ip addr del $ip/32 dev $if`;
As a result, a malicious role IP address value that has been quoted to
prevent interpretation in MMM::Agent::Helpers::_execute()
will
arrive to clear_ip()
in unquoted form allowing the execution of
arbitrary commands.
As seen above, to remove a deleted role’s IP address, mmm_agentd invokes:
/path/to/agent/clear_ip /path/to/mmm_agent.conf $if $ip
To remove the IP address from the specified interface, the clear_ip
helper command invokes MMM::Agent::Helpers::Network::clear_ip()
.
Which runs the following command on Solaris hosts:
# lib/Agent/Helpers/Network.pm
110 $output = `/usr/sbin/ifconfig $if removeif $ip`;
As a result, a malicious role IP address value that has been quoted to
prevent interpretation in MMM::Agent::Helpers::_execute()
will
arrive to clear_ip()
in unquoted form allowing the execution of
arbitrary commands.
As seen above, to remove a deleted role’s IP address, mmm_agentd invokes:
/path/to/agent/clear_ip /path/to/mmm_agent.conf $if $ip
To remove the IP address from the specified interface, the clear_ip
helper command invokes MMM::Agent::Helpers::Network::clear_ip()
.
Which runs the following command on FreeBSD hosts:
# lib/Agent/Helpers/Network.pm
114 $output = `/sbin/ifconfig $if inet $ip -alias`;
As a result, a malicious role IP address value that has been quoted to
prevent interpretation in MMM::Agent::Helpers::_execute()
will
arrive to clear_ip()
in unquoted form allowing the execution of
arbitrary commands.
After a new IP address has been configured successfully, the
implementation of the configure_ip
helper command will send
gratuitous ARPs:
# lib/Agent/Helpers/Actions.pm
47 sub configure_ip($$) {
48 my $if = shift;
49 my $ip = shift;
50
51 if (MMM::Agent::Helpers::Network::check_ip($if, $ip)) {
52 _exit_ok('IP address is configured');
53 }
54
55 if (!MMM::Agent::Helpers::Network::add_ip($if, $ip)) {
56 _exit_error("Could not configure ip adress $ip on interface $if!");
57 }
58 MMM::Agent::Helpers::Network::send_arp($if, $ip);
59 _exit_ok();
60 }
# lib/Agent/Helpers/Network.pm
129 sub send_arp($$) {
130 my $if = shift;
131 my $ip = shift;
...
151 elsif ($OSNAME eq 'solaris') {
152 # Get params for send_arp
153 my $ipaddr = `/usr/sbin/ifconfig $if`;
154
155 # Get broadcast address and netmask
156 $ipaddr =~ /netmask\s*([0-9a-f]+)\s*broadcast\s*([\d\.]+)/i;
157 my $if_bcast = $1;
158 my $if_mask = $2;
159 `/bin/send_arp -i 100 -r 5 -p /tmp/send_arp $if $ip auto $if_bcast $if_mask`;
160 }
send_arp()
does not sanitize the value of $ip
before interpolating
it into shell commands on Solaris systems. While dangerous, this
particular instance may not be currently exploitable because
send_arp()
is only called if add_ip()
succeeds and add_ip()
will
return with failure if MMM::Agent::Helpers::Network::check_ip()
cannot verify that the IP address configuration attempt succeeded.
check_ip()
currently attempts to match the the full text of the role
IP address against the value obtained from the operating system.
Thus, additional non-IP address characters in $ip
will cause
check_ip()
to return false:
# lib/Agent/Helpers/Network.pm
32 sub check_ip($$) {
33 my $if = shift;
34 my $ip = shift;
35
36 my $output;
37 if ($OSNAME eq 'linux') {
38 $output = `/sbin/ip addr show dev $if`;
39 _exit_error("Could not check if ip $ip is configured on $if: $output") if ($? >> 8 == 255);
40 }
41 elsif ($OSNAME eq 'solaris') {
42 # FIXME $if is not used here
43 $output = `/usr/sbin/ifconfig -a | grep inet`;
44 _exit_error("Could not check if ip $ip is configured on $if: $output") if ($? >> 8 == 255);
45 }
46 elsif ($OSNAME eq 'freebsd') {
47 $output = `/sbin/ifconfig $if | grep inet`;
48 _exit_error("Could not check if ip $ip is configured on $if: $output") if ($? >> 8 == 255);
49 }
50 else {
51 _exit_error("ERROR: Unsupported platform!");
52 }
53
54 return ($output =~ /\D+$ip\D+/) ? 1 : 0;
55 }
Nevertheless, send_arp()
should be fixed to sanitize its shell
command arguments as well, because this behavior may change in
subsequent releases.
The impact of these vulnerabilities can be lessened by configuring mmm_agentd to require TLS mutual authentication and by using network ACLs to prevent hosts other than legitimate mmm_mond hosts from accessing mmm_agentd.
To enable TLS mutual authentication follow the steps below (examples provided using gnutls certtool):
Generate unique mmm_agentd and mmm_mond CAs for MMM. mmm_agentd and mmm_mond will accept any certificate signed by the CA that they have been configured to trust. Therefore, to prevent non-MMM nodes from connecting to mmm_agentd and to prevent malicious mmm_agentd hosts from impersonating mmm_mond, fresh separate CAs should be created to endorse mmm_agentd and mmm_mond certificates.
# Generate mmm_agentd CA certificate
certtool --generate-privkey --outfile agentd-ca.key
certtool --generate-self-signed --load-privkey agentd-ca.key \
--outfile agentd-ca.pem --template /dev/stdin <<EOT
cn = "mmm_agentd CA"
expiration_days = 3650
ca
path_len = -1
signing_key
encryption_key
cert_signing_key
organization =
unit =
locality =
state =
country =
uid =
EOT
# Generate mmm\_mond CA certificate
certtool --generate-privkey --outfile mond-ca.key
certtool --generate-self-signed --load-privkey mond-ca.key \
--outfile mond-ca.pem --template /dev/stdin <<EOT
cn = "mmm_mond CA"
expiration_days = 3650
ca
path_len = -1
signing_key
encryption_key
cert_signing_key
organization =
unit =
locality =
state =
country =
uid =
EOT
Generate a private key and certificate for each mmm_agentd host (e.g. db1):
# Generate mmm_agentd cert
certtool --generate-privkey --outfile db1.key
certtool --generate-certificate --load-privkey db1.key \
--outfile db1.pem \
--load-ca-certificate agentd-ca.pem \
--load-ca-privkey agentd-ca.key \
--template /dev/stdin <<EOT
cn = "db1 mmm_agentd"
expiration_days = 365
signing_key
encryption_key
tls_www_client
tls_www_server
organization =
unit =
locality =
state =
country =
uid =
EOT
Generate a private key and certificate for each mmm_mond host (e.g. mmm_mond1):
# Generate mmm_mond cert
certtool --generate-privkey --outfile mmm_mond1.key
certtool --generate-certificate --load-privkey mmm_mond1.key \
--outfile mmm_mond1.pem \
--load-ca-certificate mond-ca.pem \
--load-ca-privkey mond-ca.key \
--template /dev/stdin <<EOT
cn = "mmm_mond1"
expiration_days = 365
signing_key
encryption_key
tls_www_client
tls_www_server
organization =
unit =
locality =
state =
country =
uid =
EOT
Configure mmm_agentd hosts to require mmm_mond clients to identify with mmm_mond CA certificates. Add a section such as the following to /etc/…/mmm_agent.conf:
<socket>
type ssl
cert_file /path/to/db1.pem
key_file /path/to/db1.key
ca_file /path/to/mond-ca.pem
</socket>
Configure mmm_mond hosts to require mmm_agentd daemons to identify with mmm_agentd CA certificates. Add a section such as the following to /etc/…/mmm_mon.conf:
<socket>
type ssl
cert_file /path/to/mmm_mond1.pem
key_file /path/to/mmm_mond1.key
ca_file /path/to/agentd-ca.pem
</socket>
Restart mmm_agentd and mmm_mond processes
2017-12-07 - Initial vendor contact
2018-01-11 - 2nd vendor contact
2018-03-06 - 3rd vendor contact (90 day notice)
2018-04-17 - Vendor acknowledged
2018-05-04 - Vendor confirmed patch
2018-05-07 - Public Release
Discovered by Matthew Van Gundy of Cisco ASIG.