CVE-2021-30045 (published 2021-04-06T13:15Z)

SerenityOS 2021-03-27 contains a buffer overflow vulnerability in the EndOfCentralDirectory::read() function.

Severity: MEDIUM
CVSS v2 base score: 6.4 (AV:N/AC:L/Au:N/C:P/I:N/A:P)
CVSS v3 base score: 9.1, CRITICAL (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:H)

Weakness information:
cwe_id: CWE-120
cwe_name: Buffer Copy without Checking Size of Input ('Classic Buffer Overflow')
description: The product copies an input buffer to an output buffer without verifying that the size of the input buffer is less than the size of the output buffer, leading to a buffer overflow.
extended_description: A buffer overflow condition exists when a product attempts to put more data in a buffer than it can hold, or when it attempts to put data in a memory area outside of the boundaries of a buffer. The simplest type of error, and the most common cause of buffer overflows, is the "classic" case in which the product copies the buffer without restricting how much is copied. Other variants exist, but the existence of a classic overflow strongly suggests that the programmer is not considering even the most basic of security protections.
         

Fix information:
repo_url: https://github.com/SerenityOS/serenity
commit_details: repo_url: https://github.com/SerenityOS/serenity
author: Idan Horowitz
author_date: 2021-03-27 17:28:22+03:00
msg: LibArchive: Make bounds checks stricter in the Zip parser

We now also check we have enough space in the incoming buffer for the
various signatures and optional (length specified) fields. This helps
prevents a possible heap overflow read.
repository: repo_url: https://github.com/SerenityOS/serenity
repo_name: SerenityOS/serenity
description: The Serenity Operating System 🐞
repo_language: C++
owner: SerenityOS
file_changes: filename: Zip.h
change_type: ModificationType.MODIFY
diff: @@ -52,11 +52,13 @@ struct [[gnu::packed]] EndOfCentralDirectory {
     bool read(ReadonlyBytes buffer)
     {
         auto fields_size = sizeof(EndOfCentralDirectory) - sizeof(u8*);
-        if (buffer.size() < fields_size)
+        if (buffer.size() < sizeof(end_of_central_directory_signature) + fields_size)
             return false;
         if (memcmp(buffer.data(), end_of_central_directory_signature, sizeof(end_of_central_directory_signature)) != 0)
             return false;
         memcpy(reinterpret_cast<void*>(&disk_number), buffer.data() + sizeof(end_of_central_directory_signature), fields_size);
+        if (buffer.size() < sizeof(end_of_central_directory_signature) + fields_size + comment_length)
+            return false;
         comment = buffer.data() + sizeof(end_of_central_directory_signature) + fields_size;
         return true;
     }
@@ -101,11 +103,13 @@ struct [[gnu::packed]] CentralDirectoryRecord {
     bool read(ReadonlyBytes buffer)
     {
         auto fields_size = sizeof(CentralDirectoryRecord) - (sizeof(u8*) * 3);
-        if (buffer.size() < fields_size)
+        if (buffer.size() < sizeof(central_directory_record_signature) + fields_size)
             return false;
         if (memcmp(buffer.data(), central_directory_record_signature, sizeof(central_directory_record_signature)) != 0)
             return false;
         memcpy(reinterpret_cast<void*>(&made_by_version), buffer.data() + sizeof(central_directory_record_signature), fields_size);
+        if (buffer.size() < sizeof(end_of_central_directory_signature) + fields_size + comment_length + name_length + extra_data_length)
+            return false;
         name = buffer.data() + sizeof(central_directory_record_signature) + fields_size;
         extra_data = name + name_length;
         comment = extra_data + extra_data_length;
@@ -165,11 +169,13 @@ struct [[gnu::packed]] LocalFileHeader {
     bool read(ReadonlyBytes buffer)
     {
         auto fields_size = sizeof(LocalFileHeader) - (sizeof(u8*) * 3);
-        if (buffer.size() < fields_size)
+        if (buffer.size() < sizeof(local_file_header_signature) + fields_size)
             return false;
         if (memcmp(buffer.data(), local_file_header_signature, sizeof(local_file_header_signature)) != 0)
             return false;
         memcpy(reinterpret_cast<void*>(&minimum_version), buffer.data() + sizeof(local_file_header_signature), fields_size);
+        if (buffer.size() < sizeof(end_of_central_directory_signature) + fields_size + name_length + extra_data_length + compressed_size)
+            return false;
         name = buffer.data() + sizeof(local_file_header_signature) + fields_size;
         extra_data = name + name_length;
         compressed_data = extra_data + extra_data_length;

programming_language: C++