CVE-2021-44109: Out-of-bounds Write (CWE-787)
value: A buffer overflow in lib/sbi/message.c in Open5GS 2.3.6 and earlier allows remote attackers to Denial of Service via a crafted sbi request.
CWE description: The product writes data past the end, or before the beginning, of the intended buffer.
CVSS v3 base score: 7.5
Published: 2022-04-05T02:15Z
Repository: https://github.com/open5gs/open5gs
Language: C
Files changed: lib/sbi/message.c, src/amf/namf-handler.c
Commit date: 2021-11-17 08:09:16 +0900
Commit message: [AMF] fix the memory problem (#1247)

1. memory corruption
- Overflow num_of_part in SBI message
2. null pointer dereference
- n2InfoContent->ngap_ie_type

Diff: diff --git a/lib/sbi/message.c b/lib/sbi/message.c
index ba714a45d..9432f22b9 100644
--- a/lib/sbi/message.c
+++ b/lib/sbi/message.c
@@ -922,11 +922,11 @@ static int parse_json(ogs_sbi_message_t *message,
     }
 
     ogs_log_print(OGS_LOG_TRACE, "%s", json);
     item = cJSON_Parse(json);
     if (!item) {
-        ogs_error("JSON parse error");
+        ogs_error("JSON parse error [%s]", json);
         return OGS_ERROR;
     }
 
     if (content_type &&
         !strncmp(content_type, OGS_SBI_CONTENT_PROBLEM_TYPE,
@@ -1831,22 +1831,20 @@ static int on_header_value(
 
     ogs_assert(parser);
     data = multipart_parser_get_data(parser);
     ogs_assert(data);
 
-    if (at && length) {
+    if (data->num_of_part < OGS_SBI_MAX_NUM_OF_PART && at && length) {
         SWITCH(data->header_field)
         CASE(OGS_SBI_CONTENT_TYPE)
-            if (data->part[data->num_of_part].content_type)
-                ogs_free(data->part[data->num_of_part].content_type);
+            ogs_assert(data->part[data->num_of_part].content_type == NULL);
             data->part[data->num_of_part].content_type =
                 ogs_strndup(at, length);
             ogs_assert(data->part[data->num_of_part].content_type);
             break;
         CASE(OGS_SBI_CONTENT_ID)
-            if (data->part[data->num_of_part].content_id)
-                ogs_free(data->part[data->num_of_part].content_id);
+            ogs_assert(data->part[data->num_of_part].content_id == NULL);
             data->part[data->num_of_part].content_id =
                 ogs_strndup(at, length);
             ogs_assert(data->part[data->num_of_part].content_id);
             break;
 
@@ -1865,11 +1863,11 @@ static int on_part_data(
 
     ogs_assert(parser);
     data = multipart_parser_get_data(parser);
     ogs_assert(data);
 
-    if (at && length) {
+    if (data->num_of_part < OGS_SBI_MAX_NUM_OF_PART && at && length) {
         SWITCH(data->part[data->num_of_part].content_type)
         CASE(OGS_SBI_CONTENT_JSON_TYPE)
         CASE(OGS_SBI_CONTENT_5GNAS_TYPE)
         CASE(OGS_SBI_CONTENT_NGAP_TYPE)
             size_t offset = 0;
@@ -1899,13 +1897,13 @@ static int on_part_data(
             data->part[data->num_of_part].content[
                 data->part[data->num_of_part].content_length] = 0;
             break;
 
         DEFAULT
-            ogs_log_hexdump(OGS_LOG_FATAL, (unsigned char *)at, length);
             ogs_error("Unknown content_type [%s]",
                     data->part[data->num_of_part].content_type);
+            ogs_log_hexdump(OGS_LOG_ERROR, (unsigned char *)at, length);
         END
     }
     return 0;
 }
 
@@ -1915,11 +1913,13 @@ static int on_part_data_end(multipart_parser *parser)
 
     ogs_assert(parser);
     data = multipart_parser_get_data(parser);
     ogs_assert(data);
 
-    data->num_of_part++;
+    if (data->num_of_part < OGS_SBI_MAX_NUM_OF_PART) {
+        data->num_of_part++;
+    }
 
     return 0;
 }
 
 static int parse_multipart(
@@ -1965,10 +1965,15 @@ static int parse_multipart(
     multipart_parser_execute(parser, http->content, http->content_length);
 
     multipart_parser_free(parser);
     ogs_free(boundary);
 
+    if (data.num_of_part > OGS_SBI_MAX_NUM_OF_PART) {
+        /* Overflow Issues #1247 */
+        ogs_fatal("Overflow num_of_part[%d]", data.num_of_part);
+        ogs_assert_if_reached();
+    }
     for (i = 0; i < data.num_of_part; i++) {
         SWITCH(data.part[i].content_type)
         CASE(OGS_SBI_CONTENT_JSON_TYPE)
             parse_json(message,
                     data.part[i].content_type, data.part[i].content);
@@ -2011,18 +2016,18 @@ static int parse_multipart(
                 ogs_free(data.part[i].content);
             break;
 
         DEFAULT
             ogs_error("Unknown content-type[%s]", data.part[i].content_type);
+
+            if (data.part[i].content_id)
+                ogs_free(data.part[i].content_id);
+            if (data.part[i].content_type)
+                ogs_free(data.part[i].content_type);
         END
     }
 
-    if (data.part[i].content_id)
-        ogs_free(data.part[i].content_id);
-    if (data.part[i].content_type)
-        ogs_free(data.part[i].content_type);
-
     if (data.header_field)
         ogs_free(data.header_field);
 
     return OGS_OK;
 }
diff --git a/src/amf/namf-handler.c b/src/amf/namf-handler.c
index c52938e12..a7bba66ee 100644
--- a/src/amf/namf-handler.c
+++ b/src/amf/namf-handler.c
@@ -51,10 +51,12 @@ int amf_namf_comm_handle_n1_n2_message_transfer(
     OpenAPI_n2_info_container_t *n2InfoContainer = NULL;
     OpenAPI_n2_sm_information_t *smInfo = NULL;
     OpenAPI_n2_info_content_t *n2InfoContent = NULL;
     OpenAPI_ref_to_binary_data_t *ngapData = NULL;
 
+    OpenAPI_ngap_ie_type_e ngapIeType = OpenAPI_ngap_ie_type_NULL;
+
     ogs_assert(stream);
     ogs_assert(recvmsg);
 
     N1N2MessageTransferReqData = recvmsg->N1N2MessageTransferReqData;
     if (!N1N2MessageTransferReqData) {
@@ -115,16 +117,19 @@ int amf_namf_comm_handle_n1_n2_message_transfer(
         smInfo = n2InfoContainer->sm_info;
         if (!smInfo) {
             ogs_error("No smInfo");
             return OGS_ERROR;
         }
+
         n2InfoContent = smInfo->n2_info_content;
         if (!n2InfoContent) {
             ogs_error("No n2InfoContent");
             return OGS_ERROR;
         }
 
+        ngapIeType = n2InfoContent->ngap_ie_type;
+
         ngapData = n2InfoContent->ngap_data;
         if (!ngapData || !ngapData->content_id) {
             ogs_error("No ngapData");
             return OGS_ERROR;
         }
@@ -151,11 +156,11 @@ int amf_namf_comm_handle_n1_n2_message_transfer(
     N1N2MessageTransferRspData.cause =
         OpenAPI_n1_n2_message_transfer_cause_N1_N2_TRANSFER_INITIATED;
 
     sendmsg.N1N2MessageTransferRspData = &N1N2MessageTransferRspData;
 
-    switch (n2InfoContent->ngap_ie_type) {
+    switch (ngapIeType) {
     case OpenAPI_ngap_ie_type_PDU_RES_SETUP_REQ:
         if (!n2buf) {
             ogs_error("[%s] No N2 SM Content", amf_ue->supi);
             return OGS_ERROR;
         }
@@ -388,12 +393,11 @@ int amf_namf_comm_handle_n1_n2_message_transfer(
             ogs_assert_if_reached();
         }
         break;
 
     default:
-        ogs_error("Not implemented ngap_ie_type[%d]",
-                n2InfoContent->ngap_ie_type);
+        ogs_error("Not implemented ngapIeType[%d]", ngapIeType);
         ogs_assert_if_reached();
     }
 
     response = ogs_sbi_build_response(&sendmsg, status);
     ogs_assert(response);

Vulnerable code:         ogs_error("JSON parse error");
    if (at && length) {
            if (data->part[data->num_of_part].content_type)
                ogs_free(data->part[data->num_of_part].content_type);
            if (data->part[data->num_of_part].content_id)
                ogs_free(data->part[data->num_of_part].content_id);
    if (at && length) {
            ogs_log_hexdump(OGS_LOG_FATAL, (unsigned char *)at, length);
    data->num_of_part++;
    if (data.part[i].content_id)
        ogs_free(data.part[i].content_id);
    if (data.part[i].content_type)
        ogs_free(data.part[i].content_type);

    switch (n2InfoContent->ngap_ie_type) {
        ogs_error("Not implemented ngap_ie_type[%d]",
                n2InfoContent->ngap_ie_type);
Fixed code:         ogs_error("JSON parse error [%s]", json);
    if (data->num_of_part < OGS_SBI_MAX_NUM_OF_PART && at && length) {
            ogs_assert(data->part[data->num_of_part].content_type == NULL);
            ogs_assert(data->part[data->num_of_part].content_id == NULL);
    if (data->num_of_part < OGS_SBI_MAX_NUM_OF_PART && at && length) {
            ogs_log_hexdump(OGS_LOG_ERROR, (unsigned char *)at, length);
    if (data->num_of_part < OGS_SBI_MAX_NUM_OF_PART) {
        data->num_of_part++;
    }
    if (data.num_of_part > OGS_SBI_MAX_NUM_OF_PART) {
        /* Overflow Issues #1247 */
        ogs_fatal("Overflow num_of_part[%d]", data.num_of_part);
        ogs_assert_if_reached();
    }

            if (data.part[i].content_id)
                ogs_free(data.part[i].content_id);
            if (data.part[i].content_type)
                ogs_free(data.part[i].content_type);
    OpenAPI_ngap_ie_type_e ngapIeType = OpenAPI_ngap_ie_type_NULL;


        ngapIeType = n2InfoContent->ngap_ie_type;

    switch (ngapIeType) {
        ogs_error("Not implemented ngapIeType[%d]", ngapIeType);