Increases zip overlapping file checking to equal the max scan files setting.
Micah Snyder authored on 2019/08/31 03:48:57... | ... |
@@ -61,7 +61,7 @@ |
61 | 61 |
#define ZIP_MAGIC_FILE_BEGIN_SPLIT_OR_SPANNED (0x08074b50) |
62 | 62 |
// clang-format on |
63 | 63 |
|
64 |
-#define ZIP_MAX_NUM_OVERLAPPING_FILES 5 |
|
64 |
+#define ZIP_MAX_NUM_OVERLAPPING_FILES 100 |
|
65 | 65 |
|
66 | 66 |
#define ZIP_CRC32(r, c, b, l) \ |
67 | 67 |
do { \ |
... | ... |
@@ -69,6 +69,13 @@ |
69 | 69 |
r = ~r; \ |
70 | 70 |
} while (0) |
71 | 71 |
|
72 |
+#define ZIP_RECORDS_CHECK_BLOCKSIZE 100 |
|
73 |
+struct zip_record { |
|
74 |
+ uint32_t local_offset; |
|
75 |
+ uint32_t local_header_size; |
|
76 |
+ uint32_t local_data_size; |
|
77 |
+}; |
|
78 |
+ |
|
72 | 79 |
static int wrap_inflateinit2(void *a, int b) |
73 | 80 |
{ |
74 | 81 |
return inflateInit2(a, b); |
... | ... |
@@ -559,6 +566,8 @@ static inline cl_error_t zdecrypt( |
559 | 559 |
/** |
560 | 560 |
* @brief Parse, extract, and scan a file using the local file header. |
561 | 561 |
* |
562 |
+ * Usage of the `record` parameter will alter behavior so it only collect file record metadata and does not extract or scan any files. |
|
563 |
+ * |
|
562 | 564 |
* @param map fmap for the file |
563 | 565 |
* @param loff offset of the local file header |
564 | 566 |
* @param zsize size of the zip file |
... | ... |
@@ -570,8 +579,7 @@ static inline cl_error_t zdecrypt( |
570 | 570 |
* @param tmpd temp directory path name |
571 | 571 |
* @param detect_encrypted bool: if encrypted files should raise heuristic alert |
572 | 572 |
* @param zcb callback function to invoke after extraction (default: scan) |
573 |
- * @param[out] file_local_header_size (optional) size of the local file header |
|
574 |
- * @param[out] file_local_data_size (optional) size of the compressed local file data |
|
573 |
+ * @param record (optional) a pointer to a struct to store file record information. |
|
575 | 574 |
* @return unsigned int returns the size of the file header + file data, so zip file can be indexed without the central directory |
576 | 575 |
*/ |
577 | 576 |
static unsigned int parse_local_file_header( |
... | ... |
@@ -586,8 +594,7 @@ static unsigned int parse_local_file_header( |
586 | 586 |
char *tmpd, |
587 | 587 |
int detect_encrypted, |
588 | 588 |
zip_cb zcb, |
589 |
- uint32_t *file_local_header_size, |
|
590 |
- uint32_t *file_local_data_size) |
|
589 |
+ struct zip_record *record) |
|
591 | 590 |
{ |
592 | 591 |
const uint8_t *local_header, *zip; |
593 | 592 |
char name[256]; |
... | ... |
@@ -678,10 +685,10 @@ static unsigned int parse_local_file_header( |
678 | 678 |
zip += LOCAL_HEADER_elen; |
679 | 679 |
zsize -= LOCAL_HEADER_elen; |
680 | 680 |
|
681 |
- if (NULL != file_local_header_size) |
|
682 |
- *file_local_header_size = zip - local_header; |
|
683 |
- if (NULL != file_local_data_size) |
|
684 |
- *file_local_data_size = csize; |
|
681 |
+ if (NULL != record) { |
|
682 |
+ record->local_header_size = zip - local_header; |
|
683 |
+ record->local_data_size = csize; |
|
684 |
+ } |
|
685 | 685 |
|
686 | 686 |
if (!csize) { /* FIXME: what's used for method0 files? csize or usize? Nothing in the specs, needs testing */ |
687 | 687 |
cli_dbgmsg("cli_unzip: local header - skipping empty file\n"); |
... | ... |
@@ -692,12 +699,15 @@ static unsigned int parse_local_file_header( |
692 | 692 |
return 0; |
693 | 693 |
} |
694 | 694 |
|
695 |
- if (LOCAL_HEADER_flags & F_ENCR) { |
|
696 |
- if (fmap_need_ptr_once(map, zip, csize)) |
|
697 |
- *ret = zdecrypt(zip, csize, usize, local_header, num_files_unzipped, ctx, tmpd, zcb); |
|
698 |
- } else { |
|
699 |
- if (fmap_need_ptr_once(map, zip, csize)) |
|
700 |
- *ret = unz(zip, csize, usize, LOCAL_HEADER_method, LOCAL_HEADER_flags, num_files_unzipped, ctx, tmpd, zcb); |
|
695 |
+ /* Don't actually unzip if we're just collecting the file record information (offset, sizes) */ |
|
696 |
+ if (NULL == record) { |
|
697 |
+ if (LOCAL_HEADER_flags & F_ENCR) { |
|
698 |
+ if (fmap_need_ptr_once(map, zip, csize)) |
|
699 |
+ *ret = zdecrypt(zip, csize, usize, local_header, num_files_unzipped, ctx, tmpd, zcb); |
|
700 |
+ } else { |
|
701 |
+ if (fmap_need_ptr_once(map, zip, csize)) |
|
702 |
+ *ret = unz(zip, csize, usize, LOCAL_HEADER_method, LOCAL_HEADER_flags, num_files_unzipped, ctx, tmpd, zcb); |
|
703 |
+ } |
|
701 | 704 |
} |
702 | 705 |
zip += csize; |
703 | 706 |
zsize -= csize; |
... | ... |
@@ -730,6 +740,8 @@ static unsigned int parse_local_file_header( |
730 | 730 |
/** |
731 | 731 |
* @brief Parse, extract, and scan a file by iterating the central directory. |
732 | 732 |
* |
733 |
+ * Usage of the `record` parameter will alter behavior so it only collect file record metadata and does not extract or scan any files. |
|
734 |
+ * |
|
733 | 735 |
* @param map fmap for the file |
734 | 736 |
* @param coff offset of the file header in the central directory |
735 | 737 |
* @param zsize size of the zip file |
... | ... |
@@ -740,12 +752,11 @@ static unsigned int parse_local_file_header( |
740 | 740 |
* @param tmpd temp directory path name |
741 | 741 |
* @param requests (optional) structure use to search the zip for files by name |
742 | 742 |
* @return unsigned int returns the size of the file header in the central directory, or 0 if no more files |
743 |
- * @param[out] file_local_offset (optional) offset of the local file header |
|
744 |
- * @param[out] file_local_header_size (optional) size of the local file header |
|
745 |
- * @param[out] file_local_data_size (optional) size of the compressed local file data |
|
743 |
+ * @param record (optional) a pointer to a struct to store file record information. |
|
746 | 744 |
* @return unsigned int |
747 | 745 |
*/ |
748 |
-static unsigned int parse_central_directory_file_header( |
|
746 |
+static unsigned int |
|
747 |
+parse_central_directory_file_header( |
|
749 | 748 |
fmap_t *map, |
750 | 749 |
uint32_t coff, |
751 | 750 |
uint32_t zsize, |
... | ... |
@@ -755,22 +766,13 @@ static unsigned int parse_central_directory_file_header( |
755 | 755 |
cli_ctx *ctx, |
756 | 756 |
char *tmpd, |
757 | 757 |
struct zip_requests *requests, |
758 |
- uint32_t *file_local_offset, |
|
759 |
- uint32_t *file_local_header_size, |
|
760 |
- uint32_t *file_local_data_size) |
|
758 |
+ struct zip_record *record) |
|
761 | 759 |
{ |
762 | 760 |
char name[256]; |
763 | 761 |
int last = 0; |
764 | 762 |
const uint8_t *central_header; |
765 | 763 |
int virus_found = 0; |
766 | 764 |
|
767 |
- if (NULL != file_local_offset) |
|
768 |
- *file_local_offset = 0; |
|
769 |
- if (NULL != file_local_header_size) |
|
770 |
- *file_local_header_size = 0; |
|
771 |
- if (NULL != file_local_data_size) |
|
772 |
- *file_local_data_size = 0; |
|
773 |
- |
|
774 | 765 |
if (!(central_header = fmap_need_off(map, coff, SIZEOF_CENTRAL_HEADER)) || CENTRAL_HEADER_magic != ZIP_MAGIC_CENTRAL_DIRECTORY_RECORD_BEGIN) { |
775 | 766 |
if (central_header) fmap_unneed_ptr(map, central_header, SIZEOF_CENTRAL_HEADER); |
776 | 767 |
cli_dbgmsg("cli_unzip: central header - wrkcomplete\n"); |
... | ... |
@@ -816,8 +818,9 @@ static unsigned int parse_central_directory_file_header( |
816 | 816 |
|
817 | 817 |
if (!requests) { |
818 | 818 |
if (CENTRAL_HEADER_off < zsize - SIZEOF_LOCAL_HEADER) { |
819 |
- if (NULL != file_local_offset) |
|
820 |
- *file_local_offset = CENTRAL_HEADER_off; |
|
819 |
+ if (NULL != record) { |
|
820 |
+ record->local_offset = CENTRAL_HEADER_off; |
|
821 |
+ } |
|
821 | 822 |
parse_local_file_header(map, |
822 | 823 |
CENTRAL_HEADER_off, |
823 | 824 |
zsize - CENTRAL_HEADER_off, |
... | ... |
@@ -829,8 +832,7 @@ static unsigned int parse_central_directory_file_header( |
829 | 829 |
tmpd, |
830 | 830 |
1, |
831 | 831 |
zip_scan_cb, |
832 |
- file_local_header_size, |
|
833 |
- file_local_data_size); |
|
832 |
+ record); |
|
834 | 833 |
} else { |
835 | 834 |
cli_dbgmsg("cli_unzip: central header - local hdr out of file\n"); |
836 | 835 |
} |
... | ... |
@@ -858,43 +860,212 @@ static unsigned int parse_central_directory_file_header( |
858 | 858 |
return (last ? 0 : coff); |
859 | 859 |
} |
860 | 860 |
|
861 |
+/** |
|
862 |
+ * @brief Sort zip_record structures based on local file offset. |
|
863 |
+ * |
|
864 |
+ * @param first |
|
865 |
+ * @param second |
|
866 |
+ * @return int 1 if first record's offset is higher than second's. |
|
867 |
+ * @return int 0 if first and second reocrd offsets are equal. |
|
868 |
+ * @return int -1 if first record's offset is less than second's. |
|
869 |
+ */ |
|
870 |
+static int sort_by_file_offset(const void *first, const void *second) |
|
871 |
+{ |
|
872 |
+ const struct zip_record *a = (const struct zip_record *)first; |
|
873 |
+ const struct zip_record *b = (const struct zip_record *)second; |
|
874 |
+ |
|
875 |
+ /* Avoid return x - y, which can cause undefined behaviour |
|
876 |
+ because of signed integer overflow. */ |
|
877 |
+ if (a->local_offset < b->local_offset) |
|
878 |
+ return -1; |
|
879 |
+ else if (a->local_offset > b->local_offset) |
|
880 |
+ return 1; |
|
881 |
+ |
|
882 |
+ return 0; |
|
883 |
+} |
|
884 |
+ |
|
885 |
+/** |
|
886 |
+ * @brief Search the central directory for overlapping files. |
|
887 |
+ * |
|
888 |
+ * This function indexes every file in the central directory and sorts them by file entry offset. |
|
889 |
+ * Then it iterates the sorted file records looking for overlapping files. |
|
890 |
+ * |
|
891 |
+ * @param ctx |
|
892 |
+ * @param map |
|
893 |
+ * @param fsize |
|
894 |
+ * @param coff |
|
895 |
+ * @return cl_error_t CL_CLEAN if no overlapping files |
|
896 |
+ * @return cl_error_t CL_VIRUS if overlapping files and heuristic alerts are enabled |
|
897 |
+ * @return cl_error_t CL_EFORMAT if overlapping files and heuristic alerts are disabled |
|
898 |
+ * @return cl_error_t CL_ETIMEOUT if the scan time limit is exceeded. |
|
899 |
+ * @return cl_error_t CL_EMEM for memory allocation errors. |
|
900 |
+ */ |
|
901 |
+cl_error_t check_for_overlapping_files(cli_ctx *ctx, |
|
902 |
+ fmap_t *map, |
|
903 |
+ uint32_t fsize, |
|
904 |
+ uint32_t coff) |
|
905 |
+{ |
|
906 |
+ cl_error_t status = CL_CLEAN; |
|
907 |
+ cl_error_t ret = CL_CLEAN; |
|
908 |
+ |
|
909 |
+ struct zip_record *zip_catalogue = NULL; |
|
910 |
+ size_t num_record_blocks = 0; |
|
911 |
+ size_t index = 0; |
|
912 |
+ size_t records_count = 0; |
|
913 |
+ |
|
914 |
+ struct zip_record *curr_record = NULL; |
|
915 |
+ struct zip_record *prev_record = NULL; |
|
916 |
+ uint32_t nOverlappingFiles = 0; |
|
917 |
+ |
|
918 |
+ zip_catalogue = (struct zip_record *)cli_malloc(sizeof(struct zip_record) * ZIP_RECORDS_CHECK_BLOCKSIZE); |
|
919 |
+ if (NULL == zip_catalogue) { |
|
920 |
+ status = CL_EMEM; |
|
921 |
+ goto done; |
|
922 |
+ } |
|
923 |
+ num_record_blocks = 1; |
|
924 |
+ memset(zip_catalogue, 0, sizeof(struct zip_record) * ZIP_RECORDS_CHECK_BLOCKSIZE); |
|
925 |
+ |
|
926 |
+ cli_dbgmsg("cli_unzip: checking for non-recursive zip bombs...\n"); |
|
927 |
+ |
|
928 |
+ while (0 != (coff = parse_central_directory_file_header(map, |
|
929 |
+ coff, |
|
930 |
+ fsize, |
|
931 |
+ NULL, // num_files_unziped not required |
|
932 |
+ index + 1, |
|
933 |
+ &ret, |
|
934 |
+ ctx, |
|
935 |
+ NULL, // tmpd not required |
|
936 |
+ NULL, |
|
937 |
+ &zip_catalogue[records_count]))) { |
|
938 |
+ index++; |
|
939 |
+ |
|
940 |
+ if (cli_checktimelimit(ctx) != CL_SUCCESS) { |
|
941 |
+ cli_dbgmsg("cli_unzip: Time limit reached (max: %u)\n", ctx->engine->maxscantime); |
|
942 |
+ status = CL_ETIMEOUT; |
|
943 |
+ goto done; |
|
944 |
+ } |
|
945 |
+ |
|
946 |
+ /* stop checking file entries if we'll exceed maxfiles */ |
|
947 |
+ if (ctx->engine->maxfiles && records_count >= ctx->engine->maxfiles) { |
|
948 |
+ cli_dbgmsg("cli_unzip: Files limit reached (max: %u)\n", ctx->engine->maxfiles); |
|
949 |
+ break; |
|
950 |
+ } |
|
951 |
+ records_count++; |
|
952 |
+ |
|
953 |
+ if (records_count % ZIP_RECORDS_CHECK_BLOCKSIZE == 0) { |
|
954 |
+ cli_dbgmsg(" cli_unzip: Exceeded zip record block size, allocating more space...\n"); |
|
955 |
+ |
|
956 |
+ /* allocate more space for zip records */ |
|
957 |
+ if (sizeof(struct zip_record) * ZIP_RECORDS_CHECK_BLOCKSIZE * (num_record_blocks + 1) < |
|
958 |
+ sizeof(struct zip_record) * ZIP_RECORDS_CHECK_BLOCKSIZE * (num_record_blocks)) { |
|
959 |
+ cli_errmsg("cli_unzip: Number of file records in zip will exceed the max for current architecture (integer overflow)\n"); |
|
960 |
+ status = CL_EFORMAT; |
|
961 |
+ goto done; |
|
962 |
+ } |
|
963 |
+ |
|
964 |
+ zip_catalogue = cli_realloc2(zip_catalogue, sizeof(struct zip_record) * ZIP_RECORDS_CHECK_BLOCKSIZE * (num_record_blocks + 1)); |
|
965 |
+ if (NULL == zip_catalogue) { |
|
966 |
+ status = CL_EMEM; |
|
967 |
+ goto done; |
|
968 |
+ } |
|
969 |
+ num_record_blocks++; |
|
970 |
+ /* zero out the memory for the new records */ |
|
971 |
+ memset(&zip_catalogue[records_count], 0, sizeof(struct zip_record) * (ZIP_RECORDS_CHECK_BLOCKSIZE * num_record_blocks - records_count)); |
|
972 |
+ } |
|
973 |
+ } |
|
974 |
+ |
|
975 |
+ if (records_count < 2) { |
|
976 |
+ goto done; |
|
977 |
+ } |
|
978 |
+ |
|
979 |
+ /* |
|
980 |
+ * Sort the records by local file offset |
|
981 |
+ */ |
|
982 |
+ cli_qsort(zip_catalogue, records_count, sizeof(struct zip_record), sort_by_file_offset); |
|
983 |
+ |
|
984 |
+ /* |
|
985 |
+ * Detect overlapping files. |
|
986 |
+ */ |
|
987 |
+ for (index = 1; index < records_count; index++) { |
|
988 |
+ prev_record = &zip_catalogue[index - 1]; |
|
989 |
+ curr_record = &zip_catalogue[index]; |
|
990 |
+ |
|
991 |
+ /* Check for integer overflow in 32bit size & offset values */ |
|
992 |
+ if ((UINT32_MAX - (prev_record->local_header_size + prev_record->local_data_size) < prev_record->local_offset) || |
|
993 |
+ (UINT32_MAX - (curr_record->local_header_size + curr_record->local_data_size) < curr_record->local_offset)) { |
|
994 |
+ cli_dbgmsg("cli_unzip: Integer overflow detected; invalid data sizes in zip file headers.\n"); |
|
995 |
+ status = CL_EFORMAT; |
|
996 |
+ goto done; |
|
997 |
+ } |
|
998 |
+ |
|
999 |
+ if (((curr_record->local_offset >= prev_record->local_offset) && (curr_record->local_offset < prev_record->local_offset + prev_record->local_header_size + prev_record->local_data_size)) || |
|
1000 |
+ ((prev_record->local_offset >= curr_record->local_offset) && (prev_record->local_offset < curr_record->local_offset + curr_record->local_header_size + curr_record->local_data_size))) { |
|
1001 |
+ /* Overlapping file detected */ |
|
1002 |
+ nOverlappingFiles++; |
|
1003 |
+ |
|
1004 |
+ cli_dbgmsg("cli_unzip: Overlapping files detected.\n"); |
|
1005 |
+ cli_dbgmsg(" previous file end: %u\n", prev_record->local_offset + prev_record->local_header_size + prev_record->local_data_size); |
|
1006 |
+ cli_dbgmsg(" current file start: %u\n", curr_record->local_offset); |
|
1007 |
+ |
|
1008 |
+ if (ZIP_MAX_NUM_OVERLAPPING_FILES < nOverlappingFiles) { |
|
1009 |
+ if (SCAN_HEURISTICS) { |
|
1010 |
+ status = cli_append_virus(ctx, "Heuristics.Zip.OverlappingFiles"); |
|
1011 |
+ } else { |
|
1012 |
+ status = CL_EFORMAT; |
|
1013 |
+ } |
|
1014 |
+ break; |
|
1015 |
+ } |
|
1016 |
+ } |
|
1017 |
+ |
|
1018 |
+ if (cli_checktimelimit(ctx) != CL_SUCCESS) { |
|
1019 |
+ cli_dbgmsg("cli_unzip: Time limit reached (max: %u)\n", ctx->engine->maxscantime); |
|
1020 |
+ status = CL_ETIMEOUT; |
|
1021 |
+ goto done; |
|
1022 |
+ } |
|
1023 |
+ } |
|
1024 |
+ |
|
1025 |
+done: |
|
1026 |
+ |
|
1027 |
+ if (NULL != zip_catalogue) { |
|
1028 |
+ free(zip_catalogue); |
|
1029 |
+ } |
|
1030 |
+ |
|
1031 |
+ return status; |
|
1032 |
+} |
|
1033 |
+ |
|
861 | 1034 |
cl_error_t cli_unzip(cli_ctx *ctx) |
862 | 1035 |
{ |
863 | 1036 |
unsigned int file_count = 0, num_files_unzipped = 0; |
864 | 1037 |
cl_error_t ret = CL_CLEAN; |
865 | 1038 |
uint32_t fsize, lhoff = 0, coff = 0; |
866 | 1039 |
fmap_t *map = *ctx->fmap; |
867 |
- char *tmpd; |
|
1040 |
+ char *tmpd = NULL; |
|
868 | 1041 |
const char *ptr; |
869 | 1042 |
int virus_found = 0; |
870 | 1043 |
#if HAVE_JSON |
871 | 1044 |
int toval = 0; |
872 | 1045 |
#endif |
873 |
- int bZipBombDetected = 0; |
|
874 |
- uint32_t cur_file_local_offset = 0; |
|
875 |
- uint32_t cur_file_local_header_size = 0; |
|
876 |
- uint32_t cur_file_local_data_size = 0; |
|
877 |
- uint32_t prev_file_local_offset = 0; |
|
878 |
- uint32_t prev_file_local_header_size = 0; |
|
879 |
- uint32_t prev_file_local_data_size = 0; |
|
880 | 1046 |
|
881 | 1047 |
cli_dbgmsg("in cli_unzip\n"); |
882 | 1048 |
fsize = (uint32_t)map->len; |
883 | 1049 |
if (sizeof(off_t) != sizeof(uint32_t) && (size_t)fsize != map->len) { |
884 | 1050 |
cli_dbgmsg("cli_unzip: file too big\n"); |
885 |
- return CL_CLEAN; |
|
1051 |
+ ret = CL_CLEAN; |
|
1052 |
+ goto done; |
|
886 | 1053 |
} |
887 | 1054 |
if (fsize < SIZEOF_CENTRAL_HEADER) { |
888 | 1055 |
cli_dbgmsg("cli_unzip: file too short\n"); |
889 |
- return CL_CLEAN; |
|
1056 |
+ ret = CL_CLEAN; |
|
1057 |
+ goto done; |
|
890 | 1058 |
} |
891 | 1059 |
if (!(tmpd = cli_gentemp(ctx->engine->tmpdir))) { |
892 |
- return CL_ETMPDIR; |
|
1060 |
+ ret = CL_ETMPDIR; |
|
1061 |
+ goto done; |
|
893 | 1062 |
} |
894 | 1063 |
if (mkdir(tmpd, 0700)) { |
895 | 1064 |
cli_dbgmsg("cli_unzip: Can't create temporary directory %s\n", tmpd); |
896 |
- free(tmpd); |
|
897 |
- return CL_ETMPDIR; |
|
1065 |
+ ret = CL_ETMPDIR; |
|
1066 |
+ goto done; |
|
898 | 1067 |
} |
899 | 1068 |
|
900 | 1069 |
for (coff = fsize - 22; coff > 0; coff--) { /* sizeof(EOC)==22 */ |
... | ... |
@@ -909,9 +1080,13 @@ cl_error_t cli_unzip(cli_ctx *ctx) |
909 | 909 |
} |
910 | 910 |
|
911 | 911 |
if (coff) { |
912 |
- uint32_t nOverlappingFiles = 0; |
|
913 |
- |
|
914 | 912 |
cli_dbgmsg("cli_unzip: central directory header offset: @%x\n", coff); |
913 |
+ |
|
914 |
+ ret = check_for_overlapping_files(ctx, map, fsize, coff); |
|
915 |
+ if (CL_SUCCESS != ret) { |
|
916 |
+ goto done; |
|
917 |
+ } |
|
918 |
+ |
|
915 | 919 |
while ((coff = parse_central_directory_file_header(map, |
916 | 920 |
coff, |
917 | 921 |
fsize, |
... | ... |
@@ -921,9 +1096,7 @@ cl_error_t cli_unzip(cli_ctx *ctx) |
921 | 921 |
ctx, |
922 | 922 |
tmpd, |
923 | 923 |
NULL, |
924 |
- &cur_file_local_offset, |
|
925 |
- &cur_file_local_header_size, |
|
926 |
- &cur_file_local_data_size))) { |
|
924 |
+ NULL))) { |
|
927 | 925 |
file_count++; |
928 | 926 |
if (ctx->engine->maxfiles && num_files_unzipped >= ctx->engine->maxfiles) { |
929 | 927 |
cli_dbgmsg("cli_unzip: Files limit reached (max: %u)\n", ctx->engine->maxfiles); |
... | ... |
@@ -935,50 +1108,27 @@ cl_error_t cli_unzip(cli_ctx *ctx) |
935 | 935 |
ret = CL_ETIMEOUT; |
936 | 936 |
} |
937 | 937 |
|
938 |
- /* |
|
939 |
- * Detect overlapping files and zip bombs. |
|
940 |
- */ |
|
941 |
- if ((((cur_file_local_offset > prev_file_local_offset) && (cur_file_local_offset < prev_file_local_offset + prev_file_local_header_size + prev_file_local_data_size)) || |
|
942 |
- ((prev_file_local_offset > cur_file_local_offset) && (prev_file_local_offset < cur_file_local_offset + cur_file_local_header_size + cur_file_local_data_size))) && |
|
943 |
- (cur_file_local_header_size + cur_file_local_data_size > 0)) { |
|
944 |
- /* Overlapping file detected */ |
|
945 |
- nOverlappingFiles++; |
|
946 |
- |
|
947 |
- cli_dbgmsg("cli_unzip: Overlapping files detected.\n"); |
|
948 |
- cli_dbgmsg(" previous file end: %u\n", prev_file_local_offset + prev_file_local_header_size + prev_file_local_data_size); |
|
949 |
- cli_dbgmsg(" current file start: %u\n", cur_file_local_offset); |
|
950 |
- |
|
951 |
- if (ZIP_MAX_NUM_OVERLAPPING_FILES < nOverlappingFiles) { |
|
952 |
- if (SCAN_HEURISTICS) { |
|
953 |
- ret = cli_append_virus(ctx, "Heuristics.Zip.OverlappingFiles"); |
|
954 |
- virus_found = 1; |
|
955 |
- } else { |
|
956 |
- ret = CL_EFORMAT; |
|
957 |
- } |
|
958 |
- bZipBombDetected = 1; |
|
959 |
- } |
|
960 |
- } |
|
961 |
- prev_file_local_offset = cur_file_local_offset; |
|
962 |
- prev_file_local_header_size = cur_file_local_header_size; |
|
963 |
- prev_file_local_data_size = cur_file_local_data_size; |
|
964 |
- |
|
965 | 938 |
#if HAVE_JSON |
966 | 939 |
if (cli_json_timeout_cycle_check(ctx, &toval) != CL_SUCCESS) { |
967 | 940 |
ret = CL_ETIMEOUT; |
968 | 941 |
} |
969 | 942 |
#endif |
970 | 943 |
if (ret != CL_CLEAN) { |
971 |
- if (ret == CL_VIRUS && SCAN_ALLMATCHES && !bZipBombDetected) { |
|
944 |
+ if (ret == CL_VIRUS && SCAN_ALLMATCHES) { |
|
972 | 945 |
ret = CL_CLEAN; |
973 | 946 |
virus_found = 1; |
974 |
- } else |
|
947 |
+ } else { |
|
975 | 948 |
break; |
949 |
+ } |
|
976 | 950 |
} |
977 | 951 |
} |
978 |
- } else |
|
952 |
+ } else { |
|
979 | 953 |
cli_dbgmsg("cli_unzip: central not found, using localhdrs\n"); |
980 |
- if (virus_found == 1) |
|
954 |
+ } |
|
955 |
+ |
|
956 |
+ if (virus_found == 1) { |
|
981 | 957 |
ret = CL_VIRUS; |
958 |
+ } |
|
982 | 959 |
if (num_files_unzipped <= (file_count / 4)) { /* FIXME: make up a sane ratio or remove the whole logic */ |
983 | 960 |
file_count = 0; |
984 | 961 |
while ((ret == CL_CLEAN) && |
... | ... |
@@ -994,7 +1144,6 @@ cl_error_t cli_unzip(cli_ctx *ctx) |
994 | 994 |
tmpd, |
995 | 995 |
1, |
996 | 996 |
zip_scan_cb, |
997 |
- NULL, |
|
998 | 997 |
NULL)))) { |
999 | 998 |
file_count++; |
1000 | 999 |
lhoff += coff; |
... | ... |
@@ -1014,8 +1163,13 @@ cl_error_t cli_unzip(cli_ctx *ctx) |
1014 | 1014 |
} |
1015 | 1015 |
} |
1016 | 1016 |
|
1017 |
- if (!ctx->engine->keeptmp) cli_rmdirs(tmpd); |
|
1018 |
- free(tmpd); |
|
1017 |
+done: |
|
1018 |
+ if (NULL != tmpd) { |
|
1019 |
+ if (!ctx->engine->keeptmp) { |
|
1020 |
+ cli_rmdirs(tmpd); |
|
1021 |
+ } |
|
1022 |
+ free(tmpd); |
|
1023 |
+ } |
|
1019 | 1024 |
|
1020 | 1025 |
if (ret == CL_CLEAN && virus_found) |
1021 | 1026 |
ret = CL_VIRUS; |
... | ... |
@@ -1056,7 +1210,6 @@ cl_error_t unzip_single_internal(cli_ctx *ctx, off_t local_header_offset, zip_cb |
1056 | 1056 |
NULL, |
1057 | 1057 |
0, |
1058 | 1058 |
zcb, |
1059 |
- NULL, |
|
1060 | 1059 |
NULL); |
1061 | 1060 |
|
1062 | 1061 |
return ret; |
... | ... |
@@ -1137,8 +1290,6 @@ cl_error_t unzip_search(cli_ctx *ctx, fmap_t *map, struct zip_requests *requests |
1137 | 1137 |
ctx, |
1138 | 1138 |
NULL, |
1139 | 1139 |
requests, |
1140 |
- NULL, |
|
1141 |
- NULL, |
|
1142 | 1140 |
NULL))) { |
1143 | 1141 |
if (requests->match) { |
1144 | 1142 |
ret = CL_VIRUS; |