CVE-2022-24796 (Other, published 2022-03-31T23:15Z)
Description: value: RaspberryMatic is a free and open-source operating system for running a cloud-free smart-home using the homematicIP / HomeMatic hardware line of IoT devices. A Remote Code Execution (RCE) vulnerability in the file upload facility of the WebUI interface of RaspberryMatic exists. Missing input validation/sanitization in the file upload mechanism allows remote, unauthenticated attackers with network access to the WebUI interface to achieve arbitrary operating system command execution via shell metacharacters in the HTTP query string. Injected commands are executed as root, thus leading to a full compromise of the underlying system and all its components. Versions after `2.31.25.20180428` and prior to `3.63.8.20220330` are affected. Users are advised to update to version `3.63.8.20220330` or newer. There are currently no known workarounds to mitigate the security impact and users are advised to update to the latest version available.
Severity: HIGH (CVSS v3 base score: 9.8)
Weakness: CWE-78 \u2014 Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection')
The product constructs all or part of an OS command using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended OS command when it is sent to a downstream component.
Repository: https://github.com/jens-maus/RaspberryMatic
Files changed:
buildroot-external/patches/occu/0031-WebUI-Fix-FileUpload.patch
buildroot-external/patches/occu/0031-WebUI-Fix-FileUpload/occu/WebUI/www/config/fileupload.ccc
Commit message: reworked fileupload.ccc in 0031-WebUI-Fix-FileUpload WebUI patch to
contain several security checks for a valid admin session id and
query string checks as well as omitting the critical use of "eval"
to parse the URL query string altogether. This should significantly
improve the security burden, thus fix a security issue raised by @qx-f7


Vulnerable code:
@@ -0,0 +1,61 @@
+# shellcheck shell=dash disable=SC2169,SC2034,SC2154
+# extract all params from QUERY_STRING
+# shellcheck disable=SC2046,SC2116,SC2086
+eval $(echo ${QUERY_STRING//&/;})
+
+if [ -z "${HTTP_CONTENT_LENGTH}" ]; then
+  echo "ERROR (head)"
# shellcheck shell=dash disable=SC2169,SC2034,SC2154
# extract all params from QUERY_STRING
# shellcheck disable=SC2046,SC2116,SC2086
eval $(echo ${QUERY_STRING//&/;})

if [ -z "${HTTP_CONTENT_LENGTH}" ]; then
  echo "ERROR (head)"

Fixed code:
@@ -0,0 +1,109 @@
+# shellcheck shell=dash disable=SC3036,SC3010,SC2034,SC3060,SC2116,SC3045 source=/dev/null
+# allow only POST requests
+if [[ "${REQUEST_METHOD}" != "POST" ]]; then
+  echo "ERROR: no POST request"
+  exit 1
+fi
+
+if [[ -z "${HTTP_CONTENT_LENGTH}" ]]; then
+# continue only if SIZE > 0
+if [[ "${SIZE}" -le 0 ]]; then
+  echo "ERROR: POST size <= 0"
+  exit 1
+fi
+
+# extract known params from QUERY_STRING only
+while IFS= read -r -d '&' KEYVAL && [[ -n "$KEYVAL" ]]; do
+  case ${KEYVAL%%=*} in
+    url) url=${KEYVAL#*=} ;;
+    sid) sid=${KEYVAL#*=} ;;
+    action) action=${KEYVAL#*=} ;;
+    downloadOnly) downloadOnly=${KEYVAL#*=} ;;
+  esac
+done <<END
+$(echo "${QUERY_STRING}&")
+END
+
+# check for url and action parameter
+if [[ -z "${url}" ]] || [[ -z "${action}" ]]; then
+  echo "ERROR: missing required URL parameters"
+  exit 1
+fi
+
+# check for a valid ADMIN session id
+if [[ "${#sid}" -eq 12 ]]; then
+  # parse the current version
+  [[ -r /VERSION ]] && . /VERSION
+
+  # use CCU.getVersion which is allowed only for Admins
+  RES=$(/usr/bin/curl http://127.0.0.1/api/homematic.cgi \
+                      -H 'Content-Type: application/json' \
+                      -d "{\"method\":\"CCU.getVersion\",\"params\":{\"_session_id_\": \"${sid//@}\"}}")
+
+  # check the curl result contains the current
+  # version number or not
+  if ! echo "${RES}" | grep -q "${VERSION}"; then
+    echo "ERROR: no valid admin session id"
+    exit 1
+  fi
+else
+  echo "ERROR: invalid session id"
+  exit 1
+fi
+
+  echo "ERROR: head failure"
+  exit 1
# shellcheck shell=dash disable=SC3036,SC3010,SC2034,SC3060,SC2116,SC3045 source=/dev/null
# allow only POST requests
if [[ "${REQUEST_METHOD}" != "POST" ]]; then
  echo "ERROR: no POST request"
  exit 1
fi

if [[ -z "${HTTP_CONTENT_LENGTH}" ]]; then
# continue only if SIZE > 0
if [[ "${SIZE}" -le 0 ]]; then
  echo "ERROR: POST size <= 0"
  exit 1
fi

# extract known params from QUERY_STRING only
while IFS= read -r -d '&' KEYVAL && [[ -n "$KEYVAL" ]]; do
  case ${KEYVAL%%=*} in
    url) url=${KEYVAL#*=} ;;
    sid) sid=${KEYVAL#*=} ;;
    action) action=${KEYVAL#*=} ;;
    downloadOnly) downloadOnly=${KEYVAL#*=} ;;
  esac
done <<END
$(echo "${QUERY_STRING}&")
END

# check for url and action parameter
if [[ -z "${url}" ]] || [[ -z "${action}" ]]; then
  echo "ERROR: missing required URL parameters"
  exit 1
fi

# check for a valid ADMIN session id
if [[ "${#sid}" -eq 12 ]]; then
  # parse the current version
  [[ -r /VERSION ]] && . /VERSION

  # use CCU.getVersion which is allowed only for Admins
  RES=$(/usr/bin/curl http://127.0.0.1/api/homematic.cgi \
                      -H 'Content-Type: application/json' \
                      -d "{\"method\":\"CCU.getVersion\",\"params\":{\"_session_id_\": \"${sid//@}\"}}")

  # check the curl result contains the current
  # version number or not
  if ! echo "${RES}" | grep -q "${VERSION}"; then
    echo "ERROR: no valid admin session id"
    exit 1
  fi
else
  echo "ERROR: invalid session id"
  exit 1
fi

  echo "ERROR: head failure"
  exit 1