CVE-2017-18184 (published 2018-02-13T19:29Z)

An issue was discovered in QPDF before 7.0.0. There is a stack-based out-of-bounds read in the function iterate_rc4 in QPDF_encryption.cc.

Severity: MEDIUM
CVSS v2 base score: 4.3 (AV:N/AC:M/Au:N/C:N/I:N/A:P)
CVSS v3 base score: 5.5, MEDIUM (CVSS:3.0/AV:L/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:H)

Weakness information:
cwe_id: CWE-125
cwe_name: Out-of-bounds Read
description: The product reads data past the end, or before the beginning, of the intended buffer.
extended_description: 
            
            
             
			 
         
         

Fix information:
repo_url: https://github.com/qpdf/qpdf
commit_details: repo_url: https://github.com/qpdf/qpdf
author: Jay Berkenbilt
author_date: 2017-08-26 21:10:07-04:00
msg: Pad keys to avoid memory errors (fixes #147)
repository: repo_url: https://github.com/qpdf/qpdf
repo_name: qpdf/qpdf
description: qpdf: A content-preserving PDF document transformer
repo_language: C++
owner: qpdf
file_changes: filename: QPDF_encryption.cc
change_type: ModificationType.MODIFY
diff: @@ -479,6 +479,8 @@ compute_O_value(std::string const& user_password,
 
     char upass[key_bytes];
     pad_or_truncate_password_V4(user_password, upass);
+    std::string k1(reinterpret_cast<char*>(O_key), OU_key_bytes_V4);
+    pad_short_parameter(k1, data.getLengthBytes());
     iterate_rc4(QUtil::unsigned_char_pointer(upass), key_bytes,
 		O_key, data.getLengthBytes(),
                 (data.getR() >= 3) ? 20 : 1, false);
@@ -495,6 +497,7 @@ compute_U_value_R2(std::string const& user_password,
     std::string k1 = QPDF::compute_encryption_key(user_password, data);
     char udata[key_bytes];
     pad_or_truncate_password_V4("", udata);
+    pad_short_parameter(k1, data.getLengthBytes());
     iterate_rc4(QUtil::unsigned_char_pointer(udata), key_bytes,
 		QUtil::unsigned_char_pointer(k1),
                 data.getLengthBytes(), 1, false);
@@ -516,6 +519,7 @@ compute_U_value_R3(std::string const& user_password,
                                 data.getId1().length());
     MD5::Digest digest;
     md5.digest(digest);
+    pad_short_parameter(k1, data.getLengthBytes());
     iterate_rc4(digest, sizeof(MD5::Digest),
 		QUtil::unsigned_char_pointer(k1),
                 data.getLengthBytes(), 20, false);
@@ -591,7 +595,10 @@ check_owner_password_V4(std::string& user_password,
     compute_O_rc4_key(user_password, owner_password, data, key);
     unsigned char O_data[key_bytes];
     memcpy(O_data, QUtil::unsigned_char_pointer(data.getO()), key_bytes);
-    iterate_rc4(O_data, key_bytes, key, data.getLengthBytes(),
+    std::string k1(reinterpret_cast<char*>(key), OU_key_bytes_V4);
+    pad_short_parameter(k1, data.getLengthBytes());
+    iterate_rc4(O_data, key_bytes, QUtil::unsigned_char_pointer(k1),
+                data.getLengthBytes(),
                 (data.getR() >= 3) ? 20 : 1, true);
     std::string new_user_password =
         std::string(reinterpret_cast<char*>(O_data), key_bytes);
@@ -886,6 +893,7 @@ QPDF::initializeEncryption()
 
     if (V < 5)
     {
+        // These must be exactly the right number of bytes.
         pad_short_parameter(O, key_bytes);
         pad_short_parameter(U, key_bytes);
         if (! ((O.length() == key_bytes) && (U.length() == key_bytes)))
@@ -913,24 +921,12 @@ QPDF::initializeEncryption()
         UE = encryption_dict.getKey("/UE").getStringValue();
         Perms = encryption_dict.getKey("/Perms").getStringValue();
 
+        // These may be longer than the minimum number of bytes.
         pad_short_parameter(O, OU_key_bytes_V5);
         pad_short_parameter(U, OU_key_bytes_V5);
         pad_short_parameter(OE, OUE_key_bytes_V5);
         pad_short_parameter(UE, OUE_key_bytes_V5);
         pad_short_parameter(Perms, Perms_key_bytes_V5);
-        if ((O.length() < OU_key_bytes_V5) ||
-            (U.length() < OU_key_bytes_V5) ||
-            (OE.length() < OUE_key_bytes_V5) ||
-            (UE.length() < OUE_key_bytes_V5) ||
-            (Perms.length() < Perms_key_bytes_V5))
-        {
-            throw QPDFExc(qpdf_e_damaged_pdf, this->m->file->getName(),
-                          "encryption dictionary",
-                          this->m->file->getLastOffset(),
-                          "incorrect length for some of"
-                          " /O, /U, /OE, /UE, or /Perms in"
-                          " encryption dictionary");
-        }
     }
 
     int Length = 40;

programming_language: C++