Security Advisory

RaspberryMatic Unauthenticated Remote Code Execution

Durch die Kombination mehrerer Schwachstellen können unauthentifizierte Angreifer eigenen Code auf einer RaspberryMatic Instanz ausführen.

Advisory ID: MLSA-2024-001
CVE: CVE-2024-24578
CVSS: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
Kritikalität: critical
Betroffene Versionen: ≤ 3.73.9.20240130
Behoben in: ≥ 3.75.6.20240316
Gefunden von: Hans-Martin Münch

Produktbeschreibung

RaspberryMatic ist ein alternatives Betriebssystem für eine freie, OpenSource-basierte “homematicIP CCU-Zentrale” aus deiner Hand bei Nutzung einer CCU3, ELV-Charly, RaspberryPi, Tinkerboard, ODROID, Intel NUC oder als virtuelle Maschine via Proxmox VE, Home Assistant Add-on, Docker/OCI, LXC, Qemu/KVM, Kubernetes/K8s, Synology VMM, QNAP VS, UNRAID, XCP-ng/XenServer, VirtualBox, vmWare or HyperV.

Details

RasberryMatic nutzt den in Java entwickelten HMIPServer des HomeMatic-Open-Central-Control-Unit-SDK (HM-OCCU-SDK). Die Funktionen dieses Dienstes können über URLs, die mit /pages/jpages starten aufgerufen werden. Der Code zum Hochladen neuer Firmware-Releases führt keine Prüfung der SessionID durch, daher kann der Controller ohne eine gültige Session aufgerufen werden.

Das folgende Code Beispiel zeigt den Code von de.eq3.ccu.server.internal.FirmwareUploadRouteHandler. Der Wert des URL-Parameters “sid” wird in Zeile 6 gelesen, jedoch nicht weiter geprüft.

 1public void handle(RoutingContext event) {
 2    String msgCommandFinishedWithErrors = "Handling of command finished with error.";
 3    HttpServerRequest request = event.request();
 4    if (logger.isDebugEnabled())
 5      logger.debug("Request: " + request.uri()); 
 6    String sid = request.params().get("sid");
 7    request.setExpectMultipart(true);
 8    request.uploadHandler(upload -> {
 9          String tempFile = DeviceFirmwareController.getTempFirmwarePath(upload.filename());
10          upload.exceptionHandler(());
11          boolean isValidFilename = DeviceFirmwareController.isValidFilename(upload.filename());
12          if (isValidFilename) {
13            upload.streamToFileSystem(tempFile, ());
14          } else {
15            request.response().putHeader("Content-Type", "text/html");
16            request.response().end("${addDevFirmwareInvalid}");
17            request.response().close();
18            logger.warn(msgCommandFinishedWithErrors);
19          } 
20        });
21    ...

Dieser Code kann über den Pfad /pages/jpages/system/DeviceFirmware/addFirmware erreicht werden, solange die HTTP Anfrage einen gültigen “multipart/form-data” Body enthält.

Das hochgeladene TGZ Archiv wird mit der Methode extractFileFromArchiveToTmp entpackt. In dieser Methode werden die im Archiv enthaltenen Dateinamen nicht weiter geprüft, weshalb das Schreiben einer Datei außerhalb des vorgesehenen Verzeichnisses möglich ist. Diese Art von Schwachstellen wird allgemein als ZipSlip bezeichnet.

 1private static void extractFileFromArchive2Tmp(TarInputStream is, TarEntry entry) throws IOException {
 2    boolean success = false;
 3    File tmpFirmware = new File(TMP_DIR);
 4    success = tmpFirmware.setWritable(true, false);
 5    if (!tmpFirmware.exists()) {
 6      success = tmpFirmware.mkdir();
 7    } else {
 8      success = true;
 9    } 
10    if (success) {
11      byte[] data = new byte[2048];
12      try(FileOutputStream entryStream = new FileOutputStream(TMP_DIR + FILE_SEP + entry.getName()); 
13          BufferedOutputStream dest = new BufferedOutputStream(entryStream)) {
14        int count;
15        while ((count = is.read(data)) != -1)
16          dest.write(data, 0, count); 
17        dest.flush();
18        File folder = new File(TMP_DIR);
19        File[] listOfFiles = folder.listFiles();
20        for (File file : listOfFiles) {
21          if (file.isFile())
22            file.setReadable(true, false); 
23        } 
24      } 
25    } else {
26      log.error("Not able to create " + TMP_DIR);
27    } 
28  }
29  ...

Weitere Details sowie ein Proof of Concept finden sich im offiziellen RaspberyMatic Advisory.

Workarounds

Keine

Indicators of Compromise

  • Prüfung der lighttpd Access Logs (/var/log/lighttpd-access.log) auf POST Requests für /pages/jpages/system/DeviceFirmware/addFirmware
  • RapsberryMatic mountet die meisten Verzeichnisse als read only, daher können Angreifer über diese Schwachstelle nicht einfach eine WebShell auf das System laden. Eine zuverlässige Technik um eigenen Code auszuführen stellt das Überschreiben der Datei /usr/local/addons/mediola/bin/watchdog dar. Die SHA1 Checksumme dieser Datei ist 659c7b88873dc2065e83ed28f4ceb84bfc50005f.

Coordinated Disclosure Zeitverlauf

  • 30/01/2024 Initialer Konakt mit RaspberryMatic.
  • 30/01/2024 Rückmeldung von RaspberryMatic, mit der Bitte um weitere Details.
  • 30/01/2024 Senden der detailierten Schwachstellenbeschreibung an RapsberryMatic.
  • 31/01/2024 RaspberryMatic nimmt Mitarbeiter von eQ3 in die Kommunikation auf, eröffnet ein GitHub Security issue.
  • 31/01/2024 eQ3 liefert ersten Fix für die Schwachstelle.
  • 31/01/2024 GitHub weißt de Schwachstelle die CVE-2024-24578 zu.
  • 01/02/2024 MOGWAI LABS gibt Feedback zum Security-Update von eQ3.
  • 12/02/2024 RaspberryMatic informiert uns, dass die Schwachstelle in in der CCU/OCCU Firmware 3.76.x behoben sein wird, welche im März 2024 veröffentlicht werden soll.
  • 13/02/2024 MOGWAI LABS akzeptiert das geplante Release, Nachfrage ob CCU/OCCU eine separate CVE beantragen werden.
  • 18/03/2024 RaspberryMatic und CCU/OCCU veröffentlichen Advisories.