Security Advisory

RaspberryMatic Unauthenticated Remote Code Execution

By exploiting multiple vulnerabilities unauthenticated attackers can gain remote code execution on a RaspberryMatic instance.

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
Severity: critical
Affected versions: ≤ 3.73.9.20240130
Fixed versions: ≥ 3.75.6.20240316
Discovered by: Hans-Martin Münch

Product description

RaspberryMatic is a free, lightweight operating system alternative for an OpenSource-based “homematicIP CCU” under your control using a CCU3, ELV-Charly, RaspberryPi, Tinkerboard, ODROID, Intel NUC or by running it as a virtual appliance 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

RaspberryMatric includes a Java based HMIPServer fom the HomeMatic-Open-Central-Control-Unit-SDK (HM-OCCU-SDK), accessible through URLs starting with /pages/jpages. The code within to upload new firmware releases does not perform a session id checks, thus the controller can be accessed without a valid session.

The following snippet shows code from de.eq3.ccu.server.internal.FirmwareUploadRouteHandler. The sid value is read in line 6, but not validated.

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

This code can be reached through the path /pages/jpages/system/DeviceFirmware/addFirmware as long as a valid multipart/form-data body is present.

The uploaded TGZ archive is extracted using the method extractFileFromArchiveToTmp. Within the code, the file names within the archive are not sanitized, allowing to write files outside of the intended directory. This vulnerability is commonly referred to as ZipSlip.

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

Additional details and a Proof of Concept can be found in the RaspberryMatic adivsory.

Workarounds

None

Indicators of Compromise

  • Check the lighttpd access logs (/var/log/lighttpd-access.log) for POST requests to /pages/jpages/system/DeviceFirmware/addFirmware
  • RapsberryMatic mounts most directories read only, adversaries therefore can’t simply drop a web shell on the target. A reliable way to gain remote code execution is by overwriting the file /usr/local/addons/mediola/bin/watchdog. The SHA1 checksum of this file is 659c7b88873dc2065e83ed28f4ceb84bfc50005f.

Coordinated Disclosure Timeline

  • 30/01/2024 Initial contact with RaspberryMatic.
  • 30/01/2024 Response from RaspberryMatic, asking for more details.
  • 30/01/2024 Providing vulnerability details to RapsberryMatic.
  • 31/01/2024 RaspberryMatic adds eQ3 employees, opens GitHub security issue.
  • 31/01/2024 eQ3 provides initial fix.
  • 31/01/2024 GitHub assigned CVE-2024-24578.
  • 01/02/2024 MOGWAI LABS provides feedback on the security update.
  • 12/02/2024 RaspberryMatic informs us that the issue will be fixed in CCU/OCCU firmware 3.76.x, which is planned for March 2024.
  • 13/02/2024 MOGWAI LABS accepting the planned release, asking if CCU/OCCU will request a seperate CVE.
  • 18/03/2024 Public advisory for RaspberryMatic and CCU/OCCU.