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.
... | ... |
@@ -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 |
|