CVE-2022-3976 (published 2022-11-13T14:15Z)

A vulnerability has been found in MZ Automation libiec61850 up to 1.4 and classified as critical. This vulnerability affects unknown code of the file src/mms/iso_mms/client/mms_client_files.c of the component MMS File Services. The manipulation of the argument filename leads to path traversal. Upgrading to version 1.5 is able to address this issue. The name of the patch is 10622ba36bb3910c151348f1569f039ecdd8786f. It is recommended to upgrade the affected component. The identifier of this vulnerability is VDB-213556.

CVSS v3 base score: 8.8, HIGH (CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H)

Weakness information:
cwe_id: CWE-22
cwe_name: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')
description: The product uses external input to construct a pathname that is intended to identify a file or directory that is located underneath a restricted parent directory, but the product does not properly neutralize special elements within the pathname that can cause the pathname to resolve to a location that is outside of the restricted directory.
extended_description: 
            Many file operations are intended to take place within a restricted directory. By using special elements such as ".." and "/" separators, attackers can escape outside of the restricted location to access files or directories that are elsewhere on the system. One of the most common special elements is the "../" sequence, which in most modern operating systems is interpreted as the parent directory of the current location. This is referred to as relative path traversal. Path traversal also covers the use of absolute pathnames such as "/usr/local/bin" to access unexpected files. This is referred to as absolute path traversal.
         
         

Fix information:
repo_url: https://github.com/mz-automation/libiec61850
commit_details: repo_url: https://github.com/mz-automation/libiec61850
author: Michael Zillgith
author_date: 2022-10-07 15:24:44+01:00
msg: - fixed path traversal vulnerability in MMS file services (LIB61850-357)
repository: repo_url: https://github.com/mz-automation/libiec61850
repo_name: mz-automation/libiec61850
description: Official repository for libIEC61850, the open-source library for the IEC 61850 protocols
repo_language: C
owner: mz-automation
file_changes: filename: file_provider_linux.c
change_type: ModificationType.MODIFY
diff: @@ -37,7 +37,6 @@ struct sDirectoryHandle {
     DIR* handle;
 };
 
-
 FileHandle
 FileSystem_openFile(char* fileName, bool readWrite)
 {

programming_language: C++
filename: mms_common_internal.h
change_type: ModificationType.MODIFY
diff: @@ -82,6 +82,9 @@ mmsMsg_createExtendedFilename(const char* basepath, int bufSize, char* extendedF
 LIB61850_INTERNAL FileHandle
 mmsMsg_openFile(const char* basepath, char* fileName, bool readWrite);
 
+LIB61850_INTERNAL bool
+mmsMsg_isFilenameSave(const char* filename);
+
 #endif /* (MMS_FILE_SERVICE == 1) */
 
 typedef struct sMmsServiceError

programming_language: C
filename: mms_client_files.c
change_type: ModificationType.MODIFY
diff: @@ -71,7 +71,6 @@ getFrsm(MmsConnection connection, int32_t frsmId)
     return frsm;
 }
 
-
 static int32_t
 getNextFrsmId(MmsConnection connection)
 {
@@ -125,38 +124,48 @@ mmsClient_handleFileOpenRequest(
 
     if (hasFileName) {
 
-        MmsFileReadStateMachine* frsm = getFreeFrsm(connection);
+        if (mmsMsg_isFilenameSave(filename) == false) {
+            /* potential attack */
 
-        if (frsm != NULL) {
+            if (DEBUG_MMS_CLIENT)
+                printf("MMS_CLIENT: client provided unsave filename -> rejected\n");
 
-            MmsOutstandingCall obtainFileCall = mmsClient_getMatchingObtainFileRequest(connection, filename);
+             mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_FILE_FILE_NON_EXISTENT);
+        }
+        else {
+            MmsFileReadStateMachine* frsm = getFreeFrsm(connection);
 
-            if (obtainFileCall) {
+            if (frsm != NULL) {
 
-                if (DEBUG_MMS_CLIENT)
-                    printf("MMS_CLIENT: file open is matching obtain file request for file %s\n", filename);
+                MmsOutstandingCall obtainFileCall = mmsClient_getMatchingObtainFileRequest(connection, filename);
 
-                obtainFileCall->timeout = Hal_getTimeInMs() + connection->requestTimeout;
-            }
+                if (obtainFileCall) {
 
-            FileHandle fileHandle = mmsMsg_openFile(MmsConnection_getFilestoreBasepath(connection), filename, false);
+                    if (DEBUG_MMS_CLIENT)
+                        printf("MMS_CLIENT: file open is matching obtain file request for file %s\n", filename);
 
-            if (fileHandle != NULL) {
+                    obtainFileCall->timeout = Hal_getTimeInMs() + connection->requestTimeout;
+                }
 
-                frsm->fileHandle = fileHandle;
-                frsm->readPosition = filePosition;
-                frsm->frsmId = getNextFrsmId(connection);
-                frsm->obtainRequest = obtainFileCall;
+                FileHandle fileHandle = mmsMsg_openFile(MmsConnection_getFilestoreBasepath(connection), filename, false);
+
+                if (fileHandle != NULL) {
+
+                    frsm->fileHandle = fileHandle;
+                    frsm->readPosition = filePosition;
+                    frsm->frsmId = getNextFrsmId(connection);
+                    frsm->obtainRequest = obtainFileCall;
+
+                    mmsMsg_createFileOpenResponse(MmsConnection_getFilestoreBasepath(connection),
+                            invokeId, response, filename, frsm);
+                }
+                else
+                    mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_FILE_FILE_NON_EXISTENT);
 
-                mmsMsg_createFileOpenResponse(MmsConnection_getFilestoreBasepath(connection),
-                        invokeId, response, filename, frsm);
             }
             else
-                mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_FILE_FILE_NON_EXISTENT);
-
+                mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_RESOURCE_OTHER);
         }
-        else
-            mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_RESOURCE_OTHER);
     }
     else
         goto exit_invalid_parameter;

programming_language: C
filename: mms_common_msg.c
change_type: ModificationType.MODIFY
diff: @@ -570,6 +570,24 @@ mmsMsg_createExtendedFilename(const char* basepath, int bufSize, char* extendedF
 #endif
 }
 
+bool
+mmsMsg_isFilenameSave(const char* filename)
+{
+    if (filename)
+    {
+        if (strstr(filename, ".."))
+            return false;
+
+        if (strstr(filename, "./"))
+            return false;
+
+        return true;
+    }
+    else {
+        return false;
+    }
+}
+
 FileHandle
 mmsMsg_openFile(const char* basepath, char* fileName, bool readWrite)
 {
@@ -620,7 +638,7 @@ mmsMsg_parseFileName(char* filename, uint8_t* buffer, int* bufPos, int maxBufPos
      * characters.
      */
     if (strstr(filename, "..") != NULL) {
-        mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_FILE_FILENAME_SYNTAX_ERROR);
+        mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_FILE_FILE_NON_EXISTENT);
         return false;
     }
 

programming_language: C++
filename: mms_file_service.c
change_type: ModificationType.MODIFY
diff: @@ -235,8 +235,6 @@ mmsMsg_createFileOpenResponse(const char* basepath, uint32_t invokeId, ByteBuffe
     response->size = bufPos;
 }
 
-
-
 void
 mmsServer_handleFileDeleteRequest(
     MmsServerConnection connection,
@@ -266,6 +264,16 @@ mmsServer_handleFileDeleteRequest(
     if (DEBUG_MMS_SERVER)
         printf("MMS_SERVER: mms_file_service.c: Delete file (%s)\n", filename);
 
+    if (mmsMsg_isFilenameSave(filename) == false)
+    {
+        if (DEBUG_MMS_SERVER)
+            printf("MMS_SERVER: remote provided unsave filename -> rejected\n");
+
+        mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_FILE_FILE_NON_EXISTENT);
+
+        return;
+    }
+
     if (connection->server->fileAccessHandler != NULL) {
         MmsError access = connection->server->fileAccessHandler(connection->server->fileAccessHandlerParameter,
                             connection, MMS_FILE_ACCESS_TYPE_DELETE, filename, NULL);
@@ -344,6 +352,17 @@ mmsServer_handleFileOpenRequest(
 
     if (hasFileName) {
 
+        if (mmsMsg_isFilenameSave(filename) == false) {
+            /* potential attack */
+
+            if (DEBUG_MMS_CLIENT)
+                printf("MMS_SERVER: remote provided unsave filename -> rejected\n");
+
+             mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_FILE_FILE_NON_EXISTENT);
+
+             return;
+        }
+
         if (connection->server->fileAccessHandler != NULL) {
             MmsError access = connection->server->fileAccessHandler(connection->server->fileAccessHandlerParameter,
                                 connection, MMS_FILE_ACCESS_TYPE_OPEN, filename, NULL);
@@ -690,6 +709,15 @@ mmsServer_handleObtainFileRequest(
 
     if (hasSourceFileName && hasDestinationFilename) {
 
+        if (mmsMsg_isFilenameSave(destinationFilename) == false) {
+            /* potential attack */
+
+            if (DEBUG_MMS_SERVER)
+                printf("MMS_SERVER: remote provided unsave filename -> rejected\n");
+
+            goto exit_invalid_parameter;
+        }
+
         /* Call user to check if access is allowed */
         if (connection->server->fileAccessHandler != NULL) {
             MmsError access = connection->server->fileAccessHandler(connection->server->fileAccessHandlerParameter,
@@ -1023,6 +1051,17 @@ createFileDirectoryResponse(const char* basepath, uint32_t invokeId, ByteBuffer*
             continueAfterFileName = NULL;
     }
 
+    if ((directoryName && mmsMsg_isFilenameSave(directoryName) == false) || 
+        (continueAfterFileName && mmsMsg_isFilenameSave(continueAfterFileName) == false))
+    {
+        if (DEBUG_MMS_SERVER)
+            printf("MMS_SERVER: remote provided unsave filename -> rejected\n");
+
+       mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_FILE_FILE_NON_EXISTENT);
+
+       return;
+    }
+
     tempCurPos = addFileEntriesToResponse(basepath, buffer, tempCurPos, maxSize, directoryName, &continueAfterFileName, &moreFollows);
 
 	if (tempCurPos < 0) {
@@ -1128,6 +1167,16 @@ mmsServer_handleFileRenameRequest(
 
     if ((strlen(currentFileName) != 0) && (strlen(newFileName) != 0)) {
 
+        if ((mmsMsg_isFilenameSave(currentFileName) == false) || (mmsMsg_isFilenameSave(newFileName) == false))
+        {
+            if (DEBUG_MMS_SERVER)
+                printf("MMS_SERVER: remote provided unsave filename -> rejected\n");
+
+            mmsMsg_createServiceErrorPdu(invokeId, response, MMS_ERROR_FILE_FILE_NON_EXISTENT);
+
+            return;
+        }
+
         /* Call user to check if access is allowed */
         if (connection->server->fileAccessHandler != NULL) {
             MmsError access = connection->server->fileAccessHandler(connection->server->fileAccessHandlerParameter,

programming_language: C