
Schritt-für-Schritt-Analyse von CVE-2022-46169: nicht authentifizierte Remote-Code-Ausführung in Cacti durch Authentifizierungsumgehung und Befehlsinjektion, mit Docker-Laboreinrichtung und Exploitation-Walkthrough.
Cacti ist ein Open-Source-Überwachungstool, das in PHP, MySQL/MariaDB geschrieben ist und eine benutzerfreundliche Oberfläche bietet.
Die Schwachstelle wurde im Jahr 2022 entdeckt und betrifft alle Versionen vor 1.2.23. Dieser Fehler erfordert eine Kette von Authentifizierungsumgehung und Befehlseinschleusung, um RCE (Remote Code Execution) zu erreichen.
In dieser CVE-Analyse werde ich Cacti in Docker ausführen und VSCode zur Codeanalyse verwenden. Die Einrichtung wird recht einfach sein, zuerst benötigen wir eine docker-compose.yaml-Datei, um eine neue Umgebung zu erstellen. Unten ist die docker-compose.yaml-Datei:
version: '2'
services:
cacti:
image: "smcline06/cacti"
container_name: cacti
domainname: example.com
hostname: localhost
ports:
- "8088:80"
environment:
- DB_NAME=cacti_master
- DB_USER=cactiuser
- DB_PASS=cactipassword
- DB_HOST=db
- DB_PORT=3306
- DB_ROOT_PASS=rootpassword
- INITIALIZE_DB=1
- TZ=America/Los_Angeles
volumes:
- cacti-data:/cacti
- cacti-spine:/spine
- cacti-backups:/backups
links:
- db
db:
image: "mariadb:10.3"
container_name: cacti_db
domainname: example.com
hostname: db
ports:
- "3307:3306" # Change host port to 3307
command:
- mysqld
- --character-set-server=utf8mb4
- --collation-server=utf8mb4_unicode_ci
- --max_connections=200
- --max_heap_table_size=128M
- --max_allowed_packet=32M
- --tmp_table_size=128M
- --join_buffer_size=128M
- --innodb_buffer_pool_size=1G
- --innodb_doublewrite=ON
- --innodb_flush_log_at_timeout=3
- --innodb_read_io_threads=32
- --innodb_write_io_threads=16
- --innodb_buffer_pool_instances=9
- --innodb_file_format=Barracuda
- --innodb_large_prefix=1
- --innodb_io_capacity=5000
- --innodb_io_capacity_max=10000
environment:
- MYSQL_ROOT_PASSWORD=User@123
- TZ=America/Los_Angeles
volumes:
- cacti-db:/var/lib/mysql
volumes:
cacti-db:
cacti-data:
cacti-spine:
cacti-backups:
Nachdem die Datei erstellt wurde, öffnen Sie die Kommandozeile und navigieren Sie zum Dateiverzeichnis. Führen Sie den Befehl docker-compose up -d aus, öffnen Sie den Browser und greifen Sie auf localhost:8088 zu. Zuerst sehen Sie eine Anmeldeseite:

Die Standard-Anmeldedaten sind admin/admin. Der Einrichtungsprozess wird durch die folgenden Bilder dargestellt:

Neues Passwort erstellen

Nach Abschluss der Installation haben wir einen Konsolenbildschirm wie diesen:

Beginnen wir nun mit der Analyse der Schwachstelle. Wie wir wissen, ist die anfällige Datei remote_agent.php, also versuchen wir, auf die Datei im Browser zuzugreifen:

Es zeigt an, dass wir nicht berechtigt sind, auf die Datei zuzugreifen. Es ist an der Zeit, den Quellcode der Datei anzusehen:

Es prüft durch den Aufruf der Funktion remote_client_authorized(). Lassen Sie uns tiefer in diese Funktion eintauchen:

Zuerst ruft der Server unsere IP-Adresse über die Funktion get_client_addr() ab und verwendet dann die Funktion gethostbyaddr(), um unsere IP in einen Hostnamen zu übersetzen. Der Server holt dann alle verfügbaren pollers aus der poller-Tabelle und vergleicht den Hostnamen jedes Pollers mit Ihrem aus der IP-Adresse übersetzten Hostnamen. Es gibt hier eine Umgehungsmöglichkeit innerhalb der get_client_addr()-Funktion:

Wir können sehen, dass der Server die IP-Adresse über einen der folgenden Header abruft:
- X-Forwarded-For
- X-Client-IP
- X-Real-IP
- X-ProxyUser-Ip
- CF-Connecting-IP
- True-Client-IP
- HTTP_X_FORWARDED
- HTTP_X_FORWARDED_FOR
- HTTP_X_CLUSTER_CLIENT_IP
- HTTP_FORWARDED_FOR
- HTTP_FORWARDED
- HTTP_CLIENT_IP
- REMOTE_ADDR
Dies erlaubt uns, den Wert unserer IP-Adresse vollständig zu kontrollieren. In diesem Fall können wir den X-Forwarded-For-Header verwenden, um unsere IP auf eine gültige IP zu fälschen, was uns erlaubt, die Autorisierung zu umgehen. Der X-Forwarded-For-Header wird häufig verwendet, um die ursprüngliche IP-Adresse zu identifizieren, wenn ein Proxy oder Load Balancer zwischen Client und Server sitzt. Dies stellt jedoch eine Angriffsfläche für Angreifer dar. Da wir Cacti lokal ausführen, müssen wir eine IP-Adresse angeben, die zu localhost übersetzt wird, also 127.0.0.1.

Sieht jetzt gut aus, oder? Das ist jedoch erst der Anfang, Leute!!! Wir brauchen weitere Codeanalyse, um erfolgreich einen Befehl einzuschleusen und Remote-Codeausführung zu erlangen. Nach Abschluss der Authentifizierung führt das Programm diesen Code aus:

Der Server holt den action-Parameter und tritt in eine Switch/Case-Anweisung. Wenn der Wert von action pollerdata ist, ruft das Programm poll_for_data() auf. Diese Funktion ist anfällig für Befehlseinschleusung, daher werden wir sie sorgfältig analysieren.

Die Funktion nimmt drei Parameter $local_data_ids, $host_id, $poller_id entgegen, die aus den Anfrageparametern des Benutzers local_data_ids, host_id, poller_id stammen. Beachten Sie den Unterschied in der Funktion zum Abrufen der Parameter: Eine ist get_filter_request_var und die andere ist get_nfilter_request_var, es gibt ein zusätzliches n in der letzten Funktion, wir werden später mehr darüber diskutieren. Danach prüft das Programm, ob wir den Parameter local_data_ids bereitgestellt haben, und durchläuft jeden einzelnen, um Daten aus der poller_item-Tabelle basierend auf local_data_ids und host_id abzurufen. Die Abfrage wird in $items gespeichert.