Browse code

fuzz-21329: Fix out-of-bounds read in PDF parser

Fix for an out-of-bounds read in the PDF parser when initializing
aes crypto routines that may result in a crash.

Bug found by OSS-Fuzz.

Also added checks for the arc4 init routine to mitigate the risk of a
similar issue.

Micah Snyder authored on 2020/05/05 06:00:22
Showing 4 changed files
... ...
@@ -15,6 +15,12 @@ ClamAV 0.102.3 is a bug patch release to address the following issues.
15 15
   Special thanks to Daehui Chang and Fady Othman for helping identify the ARJ
16 16
   parsing vulnerability.
17 17
 
18
+- [CVE-2020-3341](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-3341):
19
+  Fix a vulnerability in the PDF parsing module in ClamAV 0.101 - 0.102.2 that
20
+  could cause a Denial-of-Service (DoS) condition. Improper size checking of
21
+  a buffer used to initialize AES decryption routines results in an out-of-
22
+  bounds read which may cause a crash. Bug found by OSS-Fuzz.
23
+
18 24
 - Fix "Attempt to allocate 0 bytes" error when parsing some PDF documents.
19 25
 
20 26
 - Fix a couple of minor memory leaks.
... ...
@@ -27,11 +27,16 @@
27 27
 #include "arc4.h"
28 28
 #include <string.h>
29 29
 
30
-void arc4_init(struct arc4_state *a, const uint8_t *key, unsigned keylength)
30
+bool arc4_init(struct arc4_state *a, const uint8_t *key, unsigned keylength)
31 31
 {
32 32
     unsigned i;
33 33
     uint8_t j;
34
-    uint32_t *S = &a->S[0];
34
+    uint32_t *S;
35
+
36
+    if (NULL == a || NULL == key || 0 == keylength)
37
+        return false;
38
+
39
+    S = &a->S[0];
35 40
 
36 41
     for (i = 0; i < 256; i++)
37 42
         S[i] = i;
... ...
@@ -42,6 +47,7 @@ void arc4_init(struct arc4_state *a, const uint8_t *key, unsigned keylength)
42 42
         S[j]        = tmp;
43 43
     }
44 44
     a->i = a->j = 0;
45
+    return true;
45 46
 }
46 47
 
47 48
 void arc4_apply(struct arc4_state *s, uint8_t *data, unsigned len)
... ...
@@ -19,6 +19,8 @@
19 19
  *  MA 02110-1301, USA.
20 20
  */
21 21
 
22
+#include <stdbool.h>
23
+
22 24
 #include "clamav-types.h"
23 25
 struct arc4_state {
24 26
     /* really just 8 bit, but it is faster if reads are aligned */
... ...
@@ -26,5 +28,5 @@ struct arc4_state {
26 26
     uint8_t i, j;
27 27
 };
28 28
 
29
-void arc4_init(struct arc4_state *a, const uint8_t *key, unsigned keylength);
29
+bool arc4_init(struct arc4_state *a, const uint8_t *key, unsigned keylength);
30 30
 void arc4_apply(struct arc4_state *s, uint8_t *data, unsigned len);
... ...
@@ -1089,8 +1089,9 @@ static void aes_decrypt(const unsigned char *in, size_t *length, unsigned char *
1089 1089
     int nrounds;
1090 1090
 
1091 1091
     cli_dbgmsg("aes_decrypt: key length: %d, data length: %zu\n", key_n, *length);
1092
-    if (key_n > 32) {
1093
-        cli_dbgmsg("aes_decrypt: key length is %d!\n", key_n * 8);
1092
+    if (!(key_n == 16 || key_n == 24 || key_n == 32)) {
1093
+        cli_dbgmsg("aes_decrypt: invalid key length: %u!\n", key_n * 8);
1094
+        noisy_warnmsg("aes_decrypt: invalid key length: %u!\n", key_n * 8);
1094 1095
         return;
1095 1096
     }
1096 1097
 
... ...
@@ -1166,7 +1167,12 @@ char *decrypt_any(struct pdf_struct *pdf, uint32_t id, const char *in, size_t *l
1166 1166
     struct arc4_state arc4;
1167 1167
 
1168 1168
     if (!length || !*length || !in) {
1169
-        noisy_warnmsg("decrypt_any: decrypt failed for obj %u %u\n", id >> 8, id & 0xff);
1169
+        noisy_warnmsg("decrypt_any: decrypt failed for obj %u %u:  Invalid arguments.\n", id >> 8, id & 0xff);
1170
+        return NULL;
1171
+    }
1172
+
1173
+    if (NULL == pdf->key || 0 == pdf->keylen) {
1174
+        noisy_warnmsg("decrypt_any: decrypt failed for obj %u %u:  PDF key never identified.\n", id >> 8, id & 0xff);
1170 1175
         return NULL;
1171 1176
     }
1172 1177
 
... ...
@@ -1207,7 +1213,10 @@ char *decrypt_any(struct pdf_struct *pdf, uint32_t id, const char *in, size_t *l
1207 1207
         case ENC_V2:
1208 1208
             cli_dbgmsg("cli_pdf: enc is v2\n");
1209 1209
             memcpy(q, in, *length);
1210
-            arc4_init(&arc4, result, n);
1210
+            if (false == arc4_init(&arc4, result, n)) {
1211
+                noisy_warnmsg("decrypt_any: failed to init arc4\n");
1212
+                return NULL;
1213
+            }
1211 1214
             arc4_apply(&arc4, q, (unsigned)*length); /* TODO: may truncate for very large lengths */
1212 1215
 
1213 1216
             noisy_msg(pdf, "decrypt_any: decrypted ARC4 data\n");
... ...
@@ -2802,7 +2811,10 @@ static void check_user_password(struct pdf_struct *pdf, int R, const char *O,
2802 2802
         if (R == 2) {
2803 2803
             /* 7.6.3.3 Algorithm 4 */
2804 2804
             memcpy(data, key_padding, 32);
2805
-            arc4_init(&arc4, (const uint8_t *)(pdf->key), pdf->keylen);
2805
+            if (false == arc4_init(&arc4, (const uint8_t *)(pdf->key), pdf->keylen)) {
2806
+                noisy_warnmsg("decrypt_any: failed to init arc4\n");
2807
+                return;
2808
+            }
2806 2809
             arc4_apply(&arc4, (uint8_t *)data, 32);
2807 2810
             dbg_printhex("computed U (R2)", data, 32);
2808 2811
             if (!memcmp(data, U, 32))
... ...
@@ -2821,7 +2833,10 @@ static void check_user_password(struct pdf_struct *pdf, int R, const char *O,
2821 2821
             cl_hash_data("md5", d, 32 + pdf->fileIDlen, result, NULL);
2822 2822
             memcpy(data, pdf->key, len);
2823 2823
 
2824
-            arc4_init(&arc4, (const uint8_t *)data, len);
2824
+            if (false == arc4_init(&arc4, (const uint8_t *)data, len)) {
2825
+                noisy_warnmsg("decrypt_any: failed to init arc4\n");
2826
+                return;
2827
+            }
2825 2828
             arc4_apply(&arc4, result, 16);
2826 2829
             for (i = 1; i <= 19; i++) {
2827 2830
                 unsigned j;
... ...
@@ -2829,7 +2844,10 @@ static void check_user_password(struct pdf_struct *pdf, int R, const char *O,
2829 2829
                 for (j = 0; j < len; j++)
2830 2830
                     data[j] = pdf->key[j] ^ i;
2831 2831
 
2832
-                arc4_init(&arc4, (const uint8_t *)data, len);
2832
+                if (false == arc4_init(&arc4, (const uint8_t *)data, len)) {
2833
+                    noisy_warnmsg("decrypt_any: failed to init arc4\n");
2834
+                    return;
2835
+                }
2833 2836
                 arc4_apply(&arc4, result, 16);
2834 2837
             }
2835 2838